I am trying to build a default checklist for my application by serializing a hash. I don't know how to pull the information in my setup_checklist hash to my views. Any help is appreciated.
Here is my user model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
serialize :checklist, Hash
before_create :setup_checklist
private
def setup_checklist
self.checklist = {
"Organize Your Finances" => false,
"Approval Letter" => false,
"Get a Real Estate Agent and look for houses" => false,
"Find lawyer" => false,
"Get the mortgage" => false,
"Apprisal and inspection" => false,
"Close the deal" => false
}
end
end
my view.html.erb
<%= form_for :checklist do |f| %>
<%= f.check_box :checklist %>
<% end %>
I know I am not even close but a steer in the right direction would be great
To display checkboxes from a hash, you need to iterate the hash and create a checkbox for each element; for example:
<%= form_for #user do |f| %>
<% #user.checklist.each do |key, value| %>
<%= f.check_box key %>
<% end %>
<% end %>
May not be the prettiest, but this seems to be working for me
view.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :checklist do |c| %>
<% #user.checklist.each do |todo, completed| %>
<%= c.check_box todo %> <%= c.label todo %><br />
<% end %>
<% end %>
<%= f.submit %>
<% end %>
and then in the controller
def update
#user = User.find(params[:id])
#user.checklist.each do |todo, completed|
#user.checklist[todo] = params[:user][:checklist][todo] == "1"
end
#user.save
redirect_to action: :show, id: params[:id]
end
you could, of course, move that controller code into something like an update_checklist method on your model or refactor however works for you, but this should get you started.
If you use strong params, you'll need to permit each possible checklist value and it'll set them to '0' or '1' instead of true or false, but the controller could be
#user.update(params.required(:user).permit(checklist: ["Organize Your Finances", ...]))
Related
I want to link each news with some particular user.
I linked comments to particular news, but this doesn't work
_form.html.erb
<%= form_for([#user , #user.newss.build]) do |form| %>
<%# if news.errors.any? %>
<div id="error_explanation">
<h2><%#= pluralize(news.errors.count, "error") %> prohibited this
news from being saved:</h2>
<ul>
<%# news.errors.full_messages.each do |message| %>
<li><%#= message %></li>
<%# end %>
</ul>
</div>
<%# end %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div class="field">
<%= form.label :description %>
<%= form.text_area :description %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
my user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :news
end
#my news.rb
class News < ApplicationRecord
has_many :comments
belongs_to :user
end
It shows different kind of errors I can't understand that...
I will recommend doing this at controller level, if you have a user logged in and you have a news controller initiate news object and create object like below,
class NewsController < ApplicationController
def new
#news = News.new #place this inside your form
end
def create
#news = current_user.news.create permit_params
if #news.errors.blank?
# Redirect or concerned logic here
else
# Rerender errors to the form
end
end
private
def permit_params
params.require(:news).permit(:title, :description)
end
end
This will make sure only the news is binded to the user who is creating it otherwise the for will be passing user_id that is an exposed vulnerability. Hope this helps :)
Ruby on rails follows the convention over configuration that means you just need to follow proper conventions to get proper result.
For Example:
Here News belongs to user and user has many news
so to fetch news of particular user you will do user.news and for news you will do news.user
Here we use the plural of news while the singular form of user
So in your code you just made a little convention mistake in your form for.
Just remove extra 's' from newss in your following code.
<%= form_for([#user , #user.newss.build]) do |form| %>
this has to be:
<%= form_for([#user , #user.news.build]) do |form| %>
What I'm trying to accomplish:
When a user registers with my app they are taken to a new account creation page. This is where they enter their desired subdomain. from this form I also want to create the owner (a user class).
The problem:
As it sits right now, when i fill out the generated form (below)
<%= form_for #account do |f| %>
<%= fields_for :owner do |o| %>
<p>
<%= o.label :f_name %>
<%= o.text_field :f_name %>
</p>
<p>
<%= o.label :m_name %>
<%= o.text_field :m_name %>
</p>
<p>
<%= o.label :l_name %>
<%= o.text_field :l_name %>
</p>
<p>
<%= o.label :email %>
<%= o.email_field :email %>
</p>
<p>
<%= o.label :password %>
<%= o.password_field :password %>
</p>
<p>
<%= o.label :password_confirmation %>
<%= o.password_field :password_confirmation %>
</p>
<% end %>
<p>
<%= f.label :subdomain %>
<%= f.text_field :subdomain %>
</p>
<%= f.submit %>
<% end %>
and try to submit the form, I get the following rails server output:
Started POST "/accounts" for 127.0.0.1 at 2018-04-08 21:52:57 -0600
Processing by AccountsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4yUhk6N40udNBMoBJz/sFzbjC/RUtU7FVyHe9NlhtBkmpGEMZE0+xMcD7E6GLOjgp02hbkrbuMNLQ5gBjh+kvA==", "owner"=>{"f_name"=>"xxxxx", "m_name"=>"xxxxx", "l_name"=>"xxxxx", "email"=>"xxxxx#xxxxxnltd.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "account"=>{"subdomain"=>"testinga"}, "commit"=>"Create Account"}
(0.2ms) BEGIN
Account Exists (0.6ms) SELECT 1 AS one FROM "accounts" WHERE LOWER("accounts"."subdomain") = LOWER($1) LIMIT $2 [["subdomain", "testinga"], ["LIMIT", 1]]
(0.1ms) ROLLBACK
Rendering accounts/new.html.erb within layouts/application
Rendered accounts/new.html.erb within layouts/application (2.4ms)
Completed 200 OK in 49ms (Views: 21.5ms | ActiveRecord: 8.3ms)
Now when I read the output I cant seem to find why this is rolling back and not saving. I do see it telling me an account already exists whit that subdomain, however this is a CLEAN database and there are no accounts saved in it! When I run byebug just before the #account.save in the accounts controller (below) there are no error messages or details I can find.
My AccountController: (I've left the byebug in the controller, perhaps im putting it in the wrong place?)
class AccountsController < ApplicationController
def index
end
def show
end
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
byebug
if #account.save
redirect_to root_path, notice: 'Account creates successfully.'
else
render action: 'new'
end
end
def edit
end
def update
end
def destroy
end
private
def account_params
params.require(:account).permit(:subdomain, :owner_id, :plan_id, :account_verified, :account_status, owner_attributes: [:id, :email, :password, :password_confirmation, :f_name, :m_name, :l_name, :office_country_code, :office_phone_number, :mobile_country_code, :mobile_phone_number])
end
end
My Account model
class Account < ApplicationRecord
RESTRICTED_SUBDOMAINS = %w(www admin loadlead)
belongs_to :owner, class_name: 'User'
has_many :users
validates :owner, presence: true
validates :subdomain, presence: true,
#uniqueness: { case_sensitive: false },
format: { with: /\A[\w\-]+\Z/i, message: 'contains invalid characters'},
exclusion: { in: RESTRICTED_SUBDOMAINS, message: 'restricted name'}
before_validation :downcase_subdomain
accepts_nested_attributes_for :owner
protected
def downcase_subdomain
self.subdomain = subdomain.try(:downcase)
end
end
My User model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
belongs_to :account
end
Any assistance here would be greatly appreciated! I have no idea where I'm going wrong with this? Thanks in advance.
Try to call fields_for on f builder instead:
<%= form_for #account do |f| %>
<%= f.fields_for :owner do |o| %>
<p>
<%= o.label :f_name %>
<%= o.text_field :f_name %>
</p>
# ....
<% end %>
# ....
<%= f.submit %>
<% end %>
And you can remove :owner_id, this attribute value will be set automatically by Rails when we're using :accepts_nested_attributes_for.
You are calling #account.save which does not raise an exception. It returns true if everything is fine, or returns false when the validation fails (if #account.valid? returns false).
If there are any validation errors, you can check them by calling:
pry(main)> #account.valid?
pry(main)> false
pry(main)> #account.errors
That should help you debug the issue.
I'm trying to create an update form on Rails, for an object that has a foreignkey to another. However, it throws this error. I'm still very greenhorn with Ruby on Rails and have just been following a video tutorial, so I'm not quite sure how to interpret this. I am current using rails 5.0.0
In travelers_controllers.rb, below line
#prf = update_prof_params["profiles_attributes"]["0"]
throws the error
undefined method `[]' for nil:NilClass
edit.html.erb
<div class="col-md-7 col-md-offset-3 main">
<% provide(:title, "Edit user")%>
<center><h1>Update your profile</h1></center>
<%= form_for(#person) do |f| %>
<%= render 'shared/error_messages' %>
<div class="col-md-12">
<%= render 'layouts/profilefields', f: f %>
<%= f.submit "Save Changes", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
_profilefields.html.erb
<%= f.fields_for :profiles do |prf|%>
<!--
<% if !#profileInfo["avatar"].blank? %>
<%= image_tag #contactInfo.avatar_url(:medium).to_s, :class=>"profilePhoto" %>
<% end %>
<div class="photoPreview">
<i class="fa fa-upload photoUpload"></i>
<p id="uploadClick">Click to Upload</p>
</div>
<%= prf.file_field :avatar, accept: 'image/png,image/gif,image/jpeg, image/jpg', id: 'uploadAvatar' %>
<p class="deletePhoto">Delete</p>
-->
<%= prf.label :about %>
<%= prf.text_field :about, :class => "form-control" %>
<%= prf.label :why %>
<%= prf.text_field :why, :class => "form-control" %>
<%= prf.label :goals %>
<%= prf.text_field :goals, :class => "form-control" %>
<%= prf.hidden_field :traveler_id, value: current_traveler.id %>
<% end %>
travelers_controller.rb
class TravelersController < ApplicationController
def edit
#person = Traveler.find(params[:id])
#profileInfo = Profile.find_or_initialize_by(traveler_id: params[:id])
##profileInfo[:email] = current_traveler.email
#This builds the form
#person.build_profile(#profileInfo.attributes)
end
def show
end
def update
#prf = update_prof_params["profiles_attributes"]["0"]
#prof = Profile.find_or_create_by(traveler_id: current_traveler.id)
if #prof.update_attributes(prf)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
private
def update_prof_params
params.require(:traveler).permit(profiles_attributes: [:about, :why, :goals,
:traveler_id])
end
end
and the models
class Profile < ApplicationRecord
belongs_to :traveler, foreign_key: "traveler_id"
end
class Traveler < ApplicationRecord
# Include default devise modules. Others available are:
# , :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
end
In TravelersController, the method update should be used for update traveler, not profile, so you can use mass-update via nested attribute like this:
def update
#traveler = Traveler.find(params[:id])
if #traveler.update(update_prof_params)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
So the above allow you to create/update profile which belongs to traveler. Besides, ensure the nested attribute was defined in your model:
traveler.rb
class Traveler < ActiveRecord::Base
# Your code here
#....
# Make sure define this
accepts_nested_attributes_for :profile
end
Update: The permitted params should be:
def update_prof_params
params.require(:traveler).permit(profile_attributes: [:about, :why, :goals, :traveler_id])
end
As you see profile_attributes should be used instead of profiles_attributes because traveler has one profile only
i have same problem just like this link:
Multiple user models with Ruby On Rails and devise to have separate registration routes but one common login route
in my app there are two model
Company
Employee
my User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :usr, :polymorphic => true
end
my Company model
class Company < ActiveRecord::Base
has_many :users, :as => :usr
end
my Employee model
class Employee < ActiveRecord::Base
has_many :users, :as => :usr
end
app/views/devise/registrations/new.html.erb
<h2>Sign up</h2>
<%
# customized code begin
params[:user][:user_type] ||= 'company'
if ["company", "employee"].include? params[:user][:user_type].downcase
child_class_name = params[:user][:user_type].downcase.camelize
user_type = params[:user][:user_type].downcase
else
child_class_name = "Company"
user_type = "company"
end
resource.usr = child_class_name.constantize.new if resource.usr.nil?
# customized code end
%>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #validatable %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<% # customized code begin %>
<%= fields_for resource.usr do |rf| %>
<% render :partial => "#{child_class_name.underscore}_fields", :locals => { :f => rf } %>
<% end %>
<%= hidden_field :user, :user_type, :value => user_type %>
<% # customized code end %>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
my registration create method :
controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def create
build_resource
# customized code begin
# crate a new child instance depending on the given user type
child_class = params[:user][:user_type].camelize.constantize
resource.usr = child_class.new(params[child_class.to_s.underscore.to_sym])
# first check if child instance is valid
# cause if so and the parent instance is valid as well
# it's all being saved at once
valid = resource.valid?
valid = resource.usr.valid? && valid
# customized code end
if valid && resource.save # customized code
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
respond_with_navigational(resource) { render :new }
end
end
end
my application helper file :
module ApplicationHelper
def my_devise_error_messages!
return "" if resource.errors.empty? && resource.usr.errors.empty?
messages = usr_messages = ""
if !resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
end
if !resource.usr.errors.empty?
usr_messages = resource.usr.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
end
messages = messages + usr_messages
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count + resource.usr.errors.count,
:resource => resource.class.model_name.human.downcase)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
end
but i got error something like this when i open any user sign_up page:
localhost:3000/emloyees/sign_up **OR**
localhost:3000/companies/sign_up
error in registration view
ERROR :-
So what am I doing wrong?
Your question is really broad but to solve the error you posted, it is erroring undefined method because of the nested hash that doesn't exist. Here's the example:
[1] pry(main)> params = {}
=> {}
[2] pry(main)> params[:user]
=> nil
[3] pry(main)> params[:user][:user_type]
NoMethodError: undefined method `[]' for nil:NilClass
If your code is to expect that params[:user] won't ever exist without a user_type and you want to default that to company, then that would simply be:
[7] pry(main)> params[:user] ||= { user_type: "company" }
=> {:user_type=>"company"}
[8] pry(main)> params
=> {:user=>{:user_type=>"company"}}
Alternativley if you want to just return a string if params[:user] doesn't exist, you can use ||= or Hash#fetch with a default value. Hash#fetch is a safer way to error handle imo.
[5] pry(main)> params.fetch(:user, "company")
=> "company"
I'm having an error with a form in a view, can't get it why is happening. I keep getting ActionView::Template::Error (undefined method 'stage' for #<User:0x007f80045ca0e0>)
I have two models, User and Stage. User has_many stages, and stages belongs_to to user. It's as follows
The Stage Model:
class Stage < ActiveRecord::Base
belongs_to :user
end
The User Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :user_setting
has_many :matches, dependent: :destroy
has_many :stages, dependent: :destroy
end
and in the user controller I have as follows:
class UsersController < ApplicationController
def show
#newMatch = current_user.matches.new
#newStage = current_user.drivepipes.new
end
end
and a form on the show.html.erb
<%= form_for [current_user], url: user_stages_path(current_user, #newStage) do |s| %>
<%= s.text_field :stage, placeholder: "Stage" %>
<%= s.submit "Save" %>
<% end %>
If each user has many stages and you're making a form for your user, <%= s.text_field :stage, placeholder: "Stage" %> is going to give you an error because the user has_many :stages which is an enumerable. So you're going to want something like
<%= form_for [current_user], url: user_stages_path(current_user, #newStage) do |s| %>
<% current_user.stages.each do |stage| %>
<%= s.text_field stage, placeholder: "Stage" %>
<% end %>
<%= s.submit "Save" %>
<% end %>
Assuming you want a text field for every stage the user has, that is. Perhaps that's not the goal here?
There is one-to-many relationship between User and Stage. So, foreign key will reside in Stage table i.e. user_id. You should make clear that You can't have an attribute 'stage' or stage_id in User object if it has many stages. However Rails have helpers:
#user.stages
will return array of stages which have been saved with this user's id, using query:
select * from stages where stages.user_id = #user.id
While:
#user.stage
will raise error.
If you want to get input for newly created stage object associated with current user, You view code should look like this:
<%= form_for current_user, url: user_stages_path(current_user, #newStage) do |user_form| %>
<%= user_form.fields_for #newStage do |stage| %>
<%= stage.text_field :stage, placeholder: "Stage" %>
<% end %>
<%= user_form.submit "Save" %>
<% end %>
Later, in controller; you can get the stage object in the similar way:
stage = Stage.new params[:user][:stage]