Fill form with two values - ruby-on-rails

I have user/registrations/new.html.haml
- title 'User Registration'
.vcentered .signup-box
- if #user.authorizations.any? %h1 We just need a little more information...
- else %h1 We just need a little bit of information...
= form_for #user, url: user_registration_path, html: { class: 'new-user form-default' } do |f|
- if #user.name.blank?
= f.text_field :name, class: class_with_errors(#user, :name),
title: title_with_errors(#user, :name),
placeholder: 'Full Name'
- if #user.email.blank? || #user.errors.any?
= f.text_field :email, class: class_with_errors(#user, :email),
title: title_with_errors(#user, :email),
placeholder: 'Email'
- if #user.authorizations.empty?
= f.password_field :password, class: class_with_errors(#user, :password),
title: title_with_errors(#user, :password),
placeholder: 'Password'
= f.fields_for :profile do |p|
= p.hidden_field :username, value: ()
= p.text_field :place, class: class_with_errors(#user.profile, :place),
title: title_with_errors(#user.profile, :place),
id: 'profile-place',
placeholder: 'Type in your closest city...'
= p.hidden_field :city_id, id: 'profile_city_id'
.tip
E.G. "New York, New York, United States" or "Barcelona, Cataluña, Spain"
.note
Ps - We need this information to show you data relevant to your location.
We don't share it with anyone. If you cannot find your city, please type
the largest city closest to you.
= f.submit 'Sign up', class: 'submit'
Due to some association issue, I want to fill the value of
p.hidden_field :username, value: ()
with name and id.
For example if user fill name as 'Name Surname' and user id is '55' then username should be NameSurname55.
Please suggest code. Thanks in advance
My usercontroller looks like:
class Users::RegistrationsController < Devise::RegistrationsController
layout 'land'
#before_action :set_username
def create
params[:user][:profile_attributes].delete(:place)
super do |resource|
UserMailer.welcome(resource).deliver
aroundyoga_user = User.find_by(email: 'aroundyoga#gmail.com')
resource.follow!(aroundyoga_user) && aroundyoga_user.follow!(resource)
RegistrationWorker.perform_async(resource.id)
end
end
protected
def after_sign_up_path_for(resource)
welcome_path
end
end

Your :username input is hidden, so I assume user should not be able to edit username. Then you better remove this field from form at all and do all logic on server-side:
def create
params[:user][:profile_attributes].delete(:place)
super do |resource|
# Next 2 lines
resource.profile.username = resource.name.gsub(/\s+/, "") + resource.id.to_s
resource.profile.save
UserMailer.welcome(resource).deliver
aroundyoga_user = User.find_by(email: 'aroundyoga#gmail.com')
resource.follow!(aroundyoga_user) && aroundyoga_user.follow!(resource)
RegistrationWorker.perform_async(resource.id)
end
end
Edit: updated code according to information you provided. #pkrawat1 solution is better.

Add this in your profile model
before_create :generate_username
def generate_username
self.username = self.user.name.underscore + self.user.id.to_s
end

looks like you have used accept_nested_attributes in background.
so may be you can use callback in profile model like:
after_create: update_profile_username
def update_profile_username
username = user.name + user.id
save
end

I would avoid revealing IDs to the public, but If you insist:
Creating an object from params in the controller is as easy as writing
mo = MyObject.new params[my_object]
in your controller. If you need to change some attributes you would just do the following:
mo.name = mo.name + mo.id
remember that in both cases you have to call save to persist changes.
The biggest difficulty in your case is that if the user hasn't been persisted yet (i.e. it's a new object created using User.new) it hasn't been assigned an ID yet. But there's also an answer for that here
The easiest way might be:
class Profile < ActiveRecord::Base
after_create :update_name
def update_name
name = name + id
save
end
end

Related

How to set field automatically in Rails

I have a simple feedback form to allow the Users send the feedback. Controller for it:
class FeedbacksController < ApplicationController
expose(:feedback, attributes: :feedback_params)
def create
ApplicationMailer.customer_feedback(feedback).deliver_now if feedback.save
respond_with(feedback, location: root_path)
end
private
def feedback_params
params.require(:feedback).permit(:email, :message, :name, :phone)
end
end
Here I'm using expose gem.
And the view for it:
= simple_form_for(feedback, url: feedback_path) do |f|
legend
= t(".any_questions")
.form-inputs
= f.input :name
= f.input :email
= f.input :phone
= f.input :message, as: :text
.form-actions
= f.button :submit, t(".submit")
I want the next: to check somewhere if current user present(logged in to application) If yes = then set the email field using user.email (when he visit the feedback form -> email should be already filled in. If user not logged in to site then this field should be empty. Where should I put this logic? Thanks!
The simple form documentation explains how simple form is very easy to customize according to your needs. Assuming you are using devise for user authentication (if you're not, simple_form is designed to work with devise so you should check it out), then you can user current_user to access the currently logged in user.
I think simple_form's input_html would help you here:
= f.input :email, input_html: { value: "#{current_user.email if current_user}"}

Make array out of User ids

I'm using devise and a custom controller in Rails.
In a controller, I have:
def new
#users = Users.where(store_id: 5)
#array_of_users = []
end
In a view I have:
<%= f.hidden_field :employees, value: #users %>
Basically, I just want to get the employee ids and store them into an array, so if we have a user with id=50 and another with id=56, then store [50,56] in the hidden field.
How can I do this?
Use this :
def new
#users = Users.where(store_id: 5)
#user_ids = #users.pluck(:id)
#array_of_users = [] # no ides what is this for, so kept as it is.
end
Then inside the views :
<%= f.hidden_field :employees, :multiple => true, value: #user_ids %>
Just select the user ids directly from the database, it will return an array. If you convert the array into a string you will get something like this: [1,5,6,7]
def new
#users = User.where(:store_id, 5).select("id")
end
<%= f.hidden_field :employees, value: #users.to_s %>

Rails 4 NOT updating nested attributes

Issue: Instead of updating nested attributes, they are being created on top of the existing nested attributes when I hit the #update action of the associated features_controller.rb
Likely Cause: I think the problem lies in my lack of understanding in Rails' form_for. I think the breakdown is in my views, how I render the persisting nested attributes, and/or how I fail to specify the nested attribute's id, causing it to simply create a new one
feature.rb
class Feature < ActiveRecord::Base
...
has_many :scenarios
accepts_nested_attributes_for :scenarios,
allow_destroy: true,
reject_if: :all_blank
...
end
features_controller.rb
def update
...
project = Project.find(params[:project_id])
#feature = Feature.find(params[:id])
if #feature.update_attributes(feature_params)
# checking feature_params looks good...
# feature_params['scenarios'] => { <correct object hash> }
redirect_to project
else
render :edit
end
end
...
private
def feature_params
params.require(:feature).permit(:title, :narrative, :price, :eta, scenarios_attributes[:description, :_destroy])
end
_form.html.haml (simplified)
= form_for [#project, #feature] do |f|
...
- if #feature.new_record? -# if we are creating new feature
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
- else -# if we are editing an existing feature
= f.fields_for :scenarios do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
I'm sure there's a nicer way to achieve the if #feature.new_record? check. I'm also using a few Javascript hooks to create dynamic nested attribute forms (which I've left out), heavily influenced by Railscast #196 Nested Model Form (revised)
I would love a really nice Rails-y implementation of dealing with these sorts of nested forms.
Try adding :id to the :scenario_attributes portion of your feature_params method. You only have the description field and the ability to allow a destroy.
def feature_params
# added => before nested attributes
params.require(:feature).permit(:id, :title, :narrative, :price, :eta, scenarios_attributes => [:id, :description, :_destroy])
end
As #vinodadhikary suggested, you no longer need to check if feature is a new record, since Rails, specifically using the form_for method, will do that for you.
Update:
You don't need to define if #feature.new_record? ... else in your form. It will be taken care by Rails when you use form_for. Rails checks if the action is going to be create or update based on object.persisted?, so, you can update your form to:
= form_for [#project, #feature] do |f|
...
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
As #Philip7899 mentioned as a comment in the accepted answer, allowing the user to set the id means that they could "steal" children records belonging to another user.
However, Rails accepts_nested_attributes_for actually checks the id and raises:
ActiveRecord::RecordNotFound:
Couldn't find Answer with ID=5 for Questionnaire with ID=5
Basically the ids are looked for in the children association (again, as said by #glampr). Therefor, the child record belonging to another user is not found.
Ultimately, 401 is the response status (unlike the usual 404 from ActiveRecord::RecordNotFound)
Follows some code I used to test the behaviour.
let :params do
{
id: questionnaire.id,
questionnaire: {
participation_id: participation.id,
answers_attributes: answers_attributes
}
}
end
let :evil_params do
params.tap do |params|
params[:questionnaire][:answers_attributes]['0']['id'] = another_participant_s_answer.id.to_s
end
end
it "doesn't mess with other people's answers" do
old_value = another_participant_s_answer.value
put :update, evil_params
expect(another_participant_s_answer.reload.value).to eq(old_value) # pass
expect(response.status).to eq(401) # pass
end
In conclusion, adding the id to the permitted params as stated above is correct and safe.
Fascinating Rails.

How to send user.id with :user in params even though user.name is what is in the form drop down?

Currently, I have an action in my customers controller generating an array of names, #username_array, of all objects of class User with which to populate a drop down menu in a form that creates a new object of class Customer. The form element looks like this right now:
<%= f.select :user_id, #username_array %>
What I'd really like is for the id of the user to be sent into params[:customer][:user_id] instead of the name of that user that is chosen in the drop down. So in my create action in my customers controller I have the following code:
#customer = Customer.new(params[:customer])
#user = User.find_by_name(params[:customer][:user_id]) # where :user_id is actually currently the name selected from the drop down
#customer.user_id = #user.id
Is there an easier way of doing this?
Change your #username_array to include both the name and the id of the user:
Instead of:
["Bob","Sally","Dave"]
Make it:
[["Bob", 1],["Sally",2],["Dave",3]]
This could be accomplished by something like this:
#username_array = User.all.map {|user| [user.name, user.id]}
Then, f.select will display the name in the dropdown, but the actual value passed in through params[:customer][:user_id] will be the id of the user, which is what you want. With this in place, the following is all you need in the controller action code:
#customer = Customer.new(params[:customer])
You won't have to look up the user by name, the params hash will already have the correct id value.
Note that instead of making #username_array an instance variable you could just create a utility method in the helper for this controller, or the application helper:
def user_select
User.all.map {|user| [user.name, user.id]}
end
Then, in your view:
<%= f.select :user_id, user_select %>
If you put this in your application helper, you can use it everywhere and only have the code in one place (DRY).
you can do
#user = User.find_by_name(params[:customer][:user_id])
#user.customers.new(params[:customer])
or
#user = User.find_by_name(params[:customer][:user_id])
#customer = #user.customers.create(params[:customer])
but to do that you must have the relation (has_many, belongs_to,...)
or
Customer.new(params[:customer], :user_id => params[:customer][:user_id])
or
f.collection_select :user_id, #username_array, :id, :name

multiple text fields for a single database entry rails 3

In the app I am building I have a need to combine multiple text fields into a single database column.
For example my "Business" entry has a column "Discount"
The text field I want to read something like this:
<%= f.text_field :discount %> % Off <%= f.text_field :discount %>.
I want both of these to be entered into the database as a string: "10% Off Shoes" (or whatever).
Is there a way to do this in Rails 3?
Thanks!
**Edit!
I tried Pan Thomakos's solution (using virtual attributes) and now I am getting the following error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.split
Extracted source (around line #3):
1:
2: <%= f.label :cost %><br />
3: <%= f.text_field :percentage %> % Off <%= f.text_field :product %>.
app/models/business.rb:11:in `percentage'
I'm not really sure how to handle this! Admittedly I am weak when it comes to working within the model, I probably would have handled this in the controller.
Thanks!
Yes, the best way to do it is to use virtual attributes. Each virtual attribute will keep track of the different parts of the discount and the discount will be the combined field. Here is how I would implement it:
class Business
attr_writer :percentage, :product
before_save :create_discount
def percentage
#percentage.nil? ? discount.to_s.split('% Off ').first : #percentage
end
def product
#product.nil? ? discount.to_s.split('% Off ').last : #product
end
protected
def create_discount
discount = "#{#percentage}% Off #{#product}" unless #product.nil? || #percentage.nil?
end
end
You can then modify your view to:
<%= f.text_field :percentage %> % Off <%= f.text_field :product %>.
Switch the logic around.
class Business
attr_writer :percentage, :product
before_save :create_discount
def percentage
#percentage.nil? ? #percentage : discount.to_s.split('% Off ').first
end
def product
#product.nil? ? #product : discount.to_s.split('% Off ').last
end
protected
def create_discount
discount = "#{#percentage}% Off #{#product}" unless #product.nil? || #percentage.nil?
end
end

Resources