I am working on dynamic form generator. And I've noticed strange behaviour
class Model
include Mongoid::Document
field :name, :type => String
end
model = Model.new
model.name = "My Name"
model.surname = "My Surname"
#=> NoMethodError: undefined method `surname='
but
model = Model.new( :name => "My Name", :surname => "My Surname" )
#=> ok
model.surname
#=> "My Surname"
model.surname = "New Surname"
#=> "New Surname"
Can somebody explain why I can create new fields with mass assignment and can't add fields through attribute?
Per the Mongoid documentation, the getter/setter methods (e.g. .surname) will only work if the field exists in the document (which is why when you create a new Model with the field, it works).
You can still set/read the fields like so:
model[:surname]
model.read_attribute(:surname)
model[:surname] = "My Surname"
model.write_attribute(:surname, "My Surname")
See http://mongoid.org/docs/documents/dynamic.html
Related
I've got a hash that I define in my plot controller, under the edit action
#custom_params = { :custom_plot_type => "Line", :x_axis_title => "", :x_low => "", :x_high => "", :y_axis_title => "", :y_low => "", :y_high => "" }
When I visit the edit page, I have a form_with #plot as the model containing the following select box:
= fields_for :custom_params do |c|
= c.label(':custom_plot_type', "Plot Type")
= c.select(:custom_plot_type, options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
Rails gives me the following error
undefined method `custom_plot_type' for #< Hash:0x00007ffff8c4b030>
I have followed the answer on this question and I'm pretty sure my definition of the hash is correct. But no matter which way I format it (for example, using "custom_plot_type" => "Line" and #custom_params["custom_plot_type"] instead) I get the same error in the view.
I can also output the element I want in the console within the controller, using
puts #custom_params
puts #custom_params[:custom_plot_type]
This outputs the correct value, but as soon as I try to access it in the view it crashes
Edit: Adding more information about the form to clarify in response to a comment. My form is structured as follows:
= form_with(model: #plot, local: true, method: "patch") do |f|
= label_tag('plot[name]', "New plot name:")
= text_field_tag('plot[name]', #plot.name)
%br
= label_tag('plot[description]', "Description:")
= text_field_tag('plot[description]', #plot.description)
%br
%br
= fields_for :custom_params do |c|
= c.label(':custom_plot_type', "Plot Type")
= c.select(:custom_plot_type, options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
The thinking behind this was to have the a form that has fields that are associated with the Plot model, and also to have a subform for "custom params" that are not associated with the model itself but will be saved to a file. So the form does have a model instance associated with it, but the select tag in question does not.
I edited the form to use a select_tag instead of a c.select element, as follows:
= fields_for :custom_params do |c|
= label_tag('custom_params[:custom_plot_type]', "Plot Type")
= select_tag('custom_params[:custom_plot_type]', options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
I still use the value of c for other form elements later on, but using _tag elements instead of elements related to the form builder allow me to use the arbitrary hash within the select tag - thanks #tgmerritt for the suggestion
On the screenshot, you'll see that invalid feedback is specifying the attribute name from the input, here "Adulthood". I don't want it and i can't delete it. It's not in the acceptance message from the model, not in the view. Where does it come from ?
Code from the model :
validates :adulthood, acceptance: { message: "Only adults can signup on La Voyageuse" }, on: :create
Code from the view :
<%= f.input :adulthood, as: :boolean, label: t('.adulthood?'), class:"form-checkbox" %>
You have not inspected your view part which display error log correctly. Following will help you to inspect and handle your issue
u = User.new
u.valid? # => false
u.errors.messages # => {:email=>["This field is required."], :password=>["This field is required."]}
u.errors.full_messages # # => ["Email This field is required.", "Password This field is required."]
You was just subjected to show,
u.errors.messages[:email] # => "This field is required."
Inspect and edit your code in view.
I added a custom error message on the input to fix the problem :
<%= f.input :adulthood, as: :boolean, label: t('.adulthood?'), class:"form-checkbox", error: "You need to be an adult" %>
It will be internationalized so i'll call the same i18n tag than the model.
I have a form_for for creating a new record. I have set getter and setter methods to access form field in my view. Below are my getter ans setter methods with my form view,
Getter & Setter Methods respectively :
def manufacturer_model_name
self.manufacturer_models.pluck(:name).join(', ') unless self.manufacturer_models.blank?
end
def manufacturer_model_name=(names)
names = names.split(',').map{|n| n.strip}.delete_if(&:empty?) if names.present?
names.uniq.each do |name|
id = ManufacturerModel.where(:name => name, :manufacturer_id => manufacturer.id).first_or_create.id
if self.new_record?
self.user_skill_manufacturer_models.build(:user_skill_id => self.id, :manufacturer_model_id => id)
else
self.user_skill_manufacturer_models.where(:user_skill_id => self.id, :manufacturer_model_id => id).first_or_create.id
end
end if names.present?
end
Form View:
= f.text_field :manufacturer_model_name, :class => 'form-control
My problem is that, my input field is autocomplete with multiple set to true, to get multiple values. when user enters multiple comma separated values and submits form and if there are any errors on the form, my new action is rendered and user losts all the entered values forcing him to reenter all again. How can I solve this problem?
It would be better to make a manufacturer_model_name_form field or some such via attr_accessor, and then parse that in validate. It would look something like this:
class ManufacturerModel < ActiveRecord::Base
attr_accessor :manufacturer_model_name_form
validate :validate_manufacturer_model_name
def validate_manufacturer_model_name
errors.add(:manufacturer_model_name, 'not a valid manufacturer model name') if !!true # perform your validation here in place of `!!true`
end
end
Then, in your form, you would use manufacturer_model_name_form instead of manufacturer_model_name:
= f.text_field :manufacturer_model_name_form, :class => 'form-control'
I need to have different error messages for the same model depending on the form's context and location.
For the User model which validates presence of first_name:
In the back-office page it is OK to have the validation message "First name can't be blank"
In the registration page the message should be "Please type your first name"
I am looking for a clean and best-practice oriented solution, because I would like not to hack with view helper and such.
Any hint appreciated, thanks
You can use validate method in User model . Something like this
validate do |user|
if user.first_name.blank? && user.id.blank?
# id blank means the user is in registration page as he is new user.
user.errors.add(:base, "Please type your first name")
elsif user.first_name.blank?
user.errors.add(:base, "First name can't be blank")
end
end
May be using hidden_field and attr_accessor, I hope you can achieve what you want,
Form 1:
<%= f.hidden_field :check_form, :value => true %>
Form 2:
<%= f.hidden_field :check_form, :value => false %>
You need to pass check_form value also to the model.
Model:
attr_accessor :check_form
validates_presence_of :first_name, :if => :check_form_is_true?, :message => "First Name can't be blank"
validates_presence_of :first_name, :unless => :check_form_is_true? //here you need to use i18n oriented translation to show the custom error message
private
def check_form_is_true?
check_form == true
end
config/locales/en.yml
en:
activerecord:
attributes:
user:
first_name: ""
errors:
models:
user:
attributes:
first_name:
blank: "Please type your first name"
Hope it helps :)
How do you test for a text form field that has no value such as in the following case:
<input class="string required" id="student_name" name="student[name]"
size="50" type="text" />
I know if a value is present you would have:
<input class="string required" id="student_name" name="student[name]"
size="50" type="text" value="John" />
and you can to test to make sure the form field is pre-populated with the name "John" with the following:
it "should display an enmpty field for the student name" do
rendered.should have_selector("input", :type => "text",
:name => "student[name]",
:value => "John")
end
I tried specifying ":value => nil" but that didn't work.
Documentation on have_selector() is pretty slim so haven't been able to find anything there and have not been able to find any examples via searches (likely searching with the wrong terms).
As per suggestion I also tried :value => '' but get the following error message:
expected following output to contain a <input type='text' name='vendor[name]' value=''/>
When I tried :value => nil I got the following error message:
NoMethodError:
undefined method `include?' for nil:NilClass
Answer
The solution was to use either :value => "" or :value => "".
The reason it did not initially work in my case was that I had included both the webrat and capybara gems in my Gemfile. After removing webrat it worked using both solutions.
try specifying the value as ""
it "should display an enmpty field for the student name" do
rendered.should have_selector("input", :type => "text",
:name => "student[name]",
:value => "")
end
What #Unixmonkey said in the comment should work. You can also do:
page.first(:css, "#student_name")[:value].should be_nil
Edit Oh I guess #Unimonkey's comment didn't work according to your update, but the above will work - I've tested it out.
Edit 2 Re: Comments Try this instead since you're in a view spec:
selector = css_select("#student_name").first
selector["value"].should be_nil