Moving error conditionals out of the view. -Rails - ruby-on-rails

I have a form that has a text field for name, it has a presence validation on it. When a user forgets to input the name the text field turns red this is all fine but I'd like to clean up my view a little bit but I'm not sure how? Maybe a helper? Maybe a method? I'm a bit new to rails and I'm looking for some advice on this problem. Here is my code for the view.
VIEW
<% if f.object.errors[:name].present? %>
<%= f.text_field :name, :class => 'alert' %>
<div class="validation">
<% f.object.errors[:name].each do |error| %>
<%= error %>
<% end %>
</div>
<% else %>
<%= f.text_field :name %>
<% end %>
It's a pretty standard if else statment but I'd think its a little too ugly for the view.

You can achieve a similar result using your model validation. If you require a presence validation on a model, then it will tell the user the field cannot be blank. This would allow you to tell the user they cannot leave a form field blank without manually coding this in your views.
Example: Post Model - app/models/post.rb
class Post < ActiveRecord::Base
validates :title, :presence => true
validates :body, :presence => true
end
If the user now attempts to submit the form with either Title or Body empty then they will be asked to resubmit the form, with a little tool tip saying "Please fill out this field".
No logic is needed in the view to achieve this, simply validate presence in your model.

Related

Rails custom validation for required fields

I have an app where a user has to fill in all survey questions (radio buttons below each question). Sample params which I'm getting from the view when the user answered only one question are:
{"answer_11"=>"", "answer_12"=>"", "answer_16"=>"", "answer_9"=>"Velit assumenda id.", "answer_10"=>""}
I know I should use the required options inside of a form but it won't worked with my simple form views:
<%= simple_form_for :test_results, url: test_results_path do |f| %>
<% #randomize_questions.map do |q| %>
<%= q[:question] %>
<%= f.input "answer_#{q[:id]}", required: true, collection: q[:answers], as: :radio_buttons, value: { question: q[:question], answer: q[:answer]} %>
<% end %>
<%= f.button :submit %>
<% end %>
create action
def create
#test_result = TestResult.new(
answer: test_result_params,
)
#test_result.save
end
def test_result_params
params.require(:appropriateness_test_results).permit!
end
How to write such validation to prevent creation of a new record if a user did not answer all questions?
It would be helpful to see the schema of DB for that model (TestResult). I am assuming it has a json or somehow serialized field called answer that stores that hash {"answer_11"=>"", "answer_12"=>"", "answer_16"=>"", "answer_9"=>"", "answer_10"=>""}. And requirement is to validate that there are no blank values. you can have following validation in TestResult model (assuming TestResult#answer returns the answer hash)
validate :no_blank_answers
def no_blank_answers
if answer.values.any?(&:blank?)
errors.add(:answer, "cannot have blank answers")
end
end
have not tested in IRB but should work.
You can write a validator for TestResult model.
validates :answer, presence: true - and if your result don't have a answer(field will be null) this return a error, you can saw his on #test_result.errors
https://guides.rubyonrails.org/active_record_validations.html

Uploading 2 images in rails using two field

I have a form for a blog, and I would like to have two field for images. One image being the cover (in Show) and another image will serve as a preview (in index).
My form looks as follow:
<%= semantic_form_for #blog, :html => { :multipart => true } do |f| %>
<%= t :Choose_File_for_cover %> <%= f.file_field :image_path, id: "avatar-upload2", required: true %>
<img id="img_prev3" width="100%" height=200 src="#" alt="your image" class="img-thumbnail hidden"/>
<%= t :Choose_File_for_homepage %> <%= f.file_field :homepagepic, id: "avatar-upload3", required: true %>
<%= f.hidden_field :image_path_cache %>
<%= f.hidden_field :homepagepic_cache %>
<%= f.actions do %>
<%= f.action :submit, :as => :input %>
<% end %>
<% end %>
My model looks like:
class Blog < ApplicationRecord
belongs_to :user
acts_as_taggable
mount_uploader :image_path, BlogUploader
mount_uploader :homepagepic, BlogcoverUploader
end
It works well when I only have the image_path (the cover), but when I add a new field for homepagepic, i get a ROLLBACK at validation.
Can someone help me on how to select files through two separate fields on the same form please.
Thank you
The code you've provided is very sparse and it would be helpful to see a little bit more (e.g. the controller and the uploader).
I can, however, hazard a guess: image_path is an existing helper method provided by Rails (see https://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html#method-i-image_path). I have absolutely no idea what happens when you use this as a name for a form field. It could also be because you declare your submit button to be an input (I've only ever seen and used as: :button for f.action :submit).
So overall, I would pick the following approach:
rename your upload fields to cover_image and the other one to preview_image (that's what you've described in your posts as their respective purpose, so you should name them accordingly)
change the submit to a button and remove all the noise from your template and start with the bare minimum: the two upload fields and nothing else (see sample code below – note that I haven't tested it but it should work or be very close to working)
after that works, start adding back the noise (i.e. the translations, the cache fields etc.)
Test that it still works after every step. If you can write a Capybara test, do that – otherwise test it manually.
If you have questions, feel free to ask.
<%= semantic_form_for #blog, html: { multipart: true } do |f| %>
<%= f.file_field :cover_image %>
<%= f.file_field :preview_image %>
<%= f.actions do %>
<%= f.action :submit, as: :button %>
<% end %>
<% end %>
class Blog < ApplicationRecord
belongs_to :user
acts_as_taggable
mount_uploader :preview_image, BlogUploader
mount_uploader :cover_image, BlogcoverUploader
end
As the previous poster said it's hard to debug your code without all the pieces to the puzzle. A ROLLBACK is happening because one or more validations failed.
Any time you have a ROLLBACK you can add a ! to the create or update method being called on the object being rolled back and ActiveRecord will throw an error telling you why the ROLLBACK happened instead of failing gracefully.
Once you know why your object isn't persisting you can check the params of the controller action that form is submitting to. Perhaps you forgot to whitelist a param via strong params?

How to deal with serialized field in rails form

I have a serialized field in my model
class Screen < ActiveRecord::Base
serialize :options
end
The user should be able to add / edit n number of options for each record. I saw this SO question and tried
<%= f.fields_for :options do |o| %>
<%= o.label :axis_y %>
<%= o.text_field :axis_y %>
<%= o.label :axis_x %>
<%= o.text_field :axis_x %>
<% end %>
but my problem is I don't know what are the fields user want to add, and user can specify variable number of attributes foroptions. What is the best/proper way to to do this ? Any help much appreciated. Thanks
I've never seen serialize before, so I looked it up. Here's a tutorial; apparently you need to specify the type of the serialized object as well:
serialize :options, Hash
To whitelist the hash attrributes, you have a couple options.
You could create a custom validator (see here for instructions)
You can also overwrite the options= method:
def options=(val)
whitelisted_keys = ["some", "keys"]
if val.is_a?(Hash) && val.keys.all? { |key| whitelisted_keys.include? key }
super
else
errors.add(:options, "is invalid")
self
end
end
You also might need to configure your screen_params method, so if things aren't working show that code in your question.

rails validation before action

Here's what happens in my app:
customer enters phone number
phone number is searched in customer model to see if it exists
if it does exist, go to the customer show page
if it does not exist, go to customer new page to create new customer
According to my understanding, model validations are for data that is entered/edited/deleted into the database.
However, how can we check (use the validation) before anything searches in the database? And if it is incorrect (example: using letters instead of digits for phone number), then would show the error.
I was implementing html form input options to prevent someone from inputting letters in the phone number input box like this:
<%= form_tag(new_customer_path, method: :get) do %>
<%= telephone_field_tag :phone, nil, placeholder: "Phone Number", required: true, pattern: "[0-9]{10}", title: "Number must be 10 digits", maxlength: "10", class: "input-lg" %>
<%= submit_tag "Enter", class: "btn btn-primary btn-lg" %>
<% end %>
However my form was getting big because I would then put the html form input box options on everything (phone, zip-code, email, ect.).
What is more proper "Rails" way? Using active record validations to show errors, or providing html form input options to validate the data beforehand? Or is it the combination of both (max secure? client side and before database)?
Model
class Customer < ActiveRecord::Base
validates_presence_of :first_name, :last_name, :phone, :email, :zip_code
validates_uniqueness_of :phone, :email
validates :phone, :zip_code, :numericality => {:only_integer => true}
validates_length_of :phone, is: 10
validates_length_of :zip_code, is: 5
end
Controller
def new
if #customer = Customer.find_by(phone: params[:phone])
flash[:success] = "Welcome back"
redirect_to #customer
else
#customer = Customer.new(phone: params[:phone])
flash.now[:warning] = "Customer not found, please sign up!"
end
end
error messages partial
<% flash.each do |key, value| %>
<div class="alert alert-<%= key %>">
×
<ul>
<li>
<p><%= value %></p>
</li>
</ul>
</div>
<% end %>
The rails validation is probably good enough but if you're worried about it you could add a javascript validation on the client side. There are several plugins you could use if you want to do form validations client side or you could write your own. This might be helpful: http://www.tutorialspoint.com/javascript/javascript_form_validations.htm
Just add front end validations with JavaScript and HTML. Your Rails validations looks fine.
Html: required attribute.
Jquery: Check out this link - http://jqueryvalidation.org/

Rails Virtual Attribute(s) Not Validating

I guess that in many ways I'm still a newbie with some of this rails stuff.
I have ActiveRecord Model for Payments.
But, it only has two things that get added to it's table and those are done once we get a positive response back from authorize.net.
In the Controller for this model I have my Cart form.
Within the Cart form I have billing information with default values pulled from #client and the credit card information. It looks a bit like this:
<%= form_for #payment, :url => { action: "checkout" } do |f| %>
...show errors ...
<%= f.fields_for #client do |ff| %>
<%= ff.label :firstname, 'First Name*' %>
<%= ff.text_field :firstname %>
...more fields ....
<%= ff.label :zip, 'Zip*' %>
<%= ff.text_field :zip %>
<% end %>
<%= f.label :cardnumber, 'Card Number*' %>
<%= f.text_field :cardnumber %>
... more cc info fields ...
<% end %>
Now in the Model I have added attr_accessor :cardnumber, and other card info fields.
I don't have any getter or setter methods for these (perhaps that is what I am missing).
However, I do have this in the Payment Model:
validates :zip, presence: true, numericality: true
validates :cardnumber, presence: true, numericality: true
Yet, so far the form will bypass this validation all together and goes direction to the checkout. Any help would be greatly appreciated! How do I get these validations to work properly?
If you want to get into the technical details, most of Rails' baked-in validators inherit from ActiveModel::EachValidator and this validator explicitly checks the attributes collection from ActiveModel via #read_attribute_for_validation. If #zip and #cardnumber have been set up with attr_accessor they are most likely not part of #attributes and thus skipped by the validator.
The simplest workaround would be to write a private method that validates zip/cardnumber and then call .validates with the name of that validation method. The pattern that was recommended by Koz would look like this...
class Payment
attr_accessor :zip
validate :assure_zip_not_blank
private
def assure_zip_not_blank
errors.add(:zip, 'cannot be blank') if zip_blank? && new_record?
end
def zip_blank?
self.zip.blank?
end
end
Separating the validation into two methods (assure_zip_not_blank and zip_blank?) may be overkill in this particular case but it's helpful when the logic becomes more complicated and/or you can reuse the logic.

Resources