Validations with Simple Form - ruby-on-rails

I'm attempting to use the simple_form gem in order to create forms. I'm also using twitter bootstrap for styling, so there are certain cases where simple_form is too rigid to do what I want to do (not always).
I'm submitting a form that should fail on validation, which it does. However, when it renders the page again, it doesn't display error messages near the input, and the page only half renders.
Controller Code: (note, I'm essentially using show/edit action as the same thing)
def update
#contact = current_resource
authorize! :update, #contact
respond_to do |format|
if #contact.update_attributes(params[:customer_relationship_contact])
format.html { redirect_to customer_relationship_contact_url(#contact), notice: "#{#contact.to_s} was successfully updated." }
format.json { head :no_content }
else
format.html { render :edit }
format.json { render json: #contact.errors, status: :unprocessable_entity }
end
end
end
def show
#contact = current_resource
authorize! :show, #contact
respond_to do |format|
format.html # show.html.erb
format.json { render json: #contact }
end
end
View/Form Code:
<%= simple_form_for #contact, :html => {:novalidate => true} do |f| %>
<%= f.error_notification %>
<fieldset>
<legend>Contract</legend>
<div class="controls controls-row">
<div class="span3">
<div class="controls controls-row">
<%= f.label :tax_id_number, 'Social Security No.' %>
<%= f.text_field :tax_id_number, class: 'span12', placeholder: 'Social Security No.' %>
</div>
</div>
</div>
</div>
</fieldset>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to "Cancel", customer_relationship_contacts_path, :class => 'btn' %>
</div>
Model Validation:
validates :tax_id_number, format: { with: /\A[0-9]+\z/, message: "Please enter only numbers without dashes." }, allow_blank: true

The problem here is that you are using f.label and f.text_field in your view - simple form is built to be simple, so what you need is f.input - so in this case:
<%= f.input :tax_id_number, label: 'Social Security No.', placeholder: 'Social Security No.' %>
Here is what happens - f.input will create a surround div with an input class. Within that div is the label and the input. If there are any errors, simple form will add the class field_with_errors to the surrounding div and add a span containing the message after the input with the class error. This will not happen if you don't use f.input
All that is left is to style them with css.

Related

Ruby: Prefill New Model Form From Previous Page's Partial Form

Ruby on Rails, Simple Form.
I have a contact/Message partial on several pages (product#index#show, static ‘services’ page) — when someone fills out the partial form and submits, I want the Message#new page to load with partial data prefilling the larger contact form without creating a new message and sending the action mailer.
Other SO solutions are for multi-step forms, wizards, or passing data through URL.
Here's my code:
_Shortcontact (the partial)
<div class="short_contact_wrapper">
<h4 id="short_container_title">Contact Us</h4>
<div class="contact-container">
<%= simple_form_for(message) do |f| %>
<div class="form-row-2">
<%= f.input :contactfirstname, label: false, placeholder: "* First Name", presence: true, :input_html => { :class => 'input_string' } %>
<%= f.input :contactlastname, label: false, placeholder: "* Last Name" %>
</div>
<div class="form-row-2">
<%= f.input :email, label: false, placeholder:"* Email" %>
<%= f.input :phone, label: false, placeholder: "* Phone" %>
</div>
</div>
<div class="contact-container">
<%= f.input :product_app, label: "Product Application Category", collection: ["Drones", "5G Telecom", "Thin Battery", "Auto Warehouse", "Robotics", "Wearables", "Electric Vehicles", "Industrial Personal Computers (Military)", "Industrial Personal Computers(Medical)", "Consumer Electronics", "Uninterruptible Power System", "Internet of Things", "Power Bank", "Other" ] %>
<%= f.input :batteryapp, label: false, placeholder: "Please describe the product and how you\'ll use the lithium ion battery or cell." %>
<div class="bottom_container" style="display: flex; justify-content: center;">
<%= f.button :submit, "Submit", class: 'contactbutton' %>
</div>
<% end %>
</div>
</div>
In my Product controller #Edit and #Show:
before_action :build_message, only: [:index, :new, :show]
def index
#products = Product.all
end
def new
#product = Product.new
# build nested models
#product.product_specs.build
#product.specifications.build
end
def create
#product = Product.new(product_parameters)
#product.category_id = product_parameters[:category_id]
# setting product stuff for TESTING
#product.picture = "products/IPC_tablet.png"
#product.iconblack = "icons/products/icon_IPC_black.png"
if #product.save!
flash[:success] = "You have saved the #{#product.name} product"
redirect_to product_path(#product)
else
flash.now[:error] = "Product was not saved"
render "new"
end
end
def show
#product_specs = #product.product_specs
end
def edit
# build nested models
#product.product_specs.build
#product.specifications.build
end
def build_message
#message = Message.new
end
Message Controller
def new
#message = Message.new
#message.build_company
puts "#{params[:message].inspect}"
puts "are there parameters?"
puts "#{#message.inspect}"
end
def create
#message = Message.new(message_parameters)
respond_to do |format|
if #message.save!
#send through mailer
MessageMailer.send_message(#message).deliver_now
format.html {redirect_to message_path(#message)}
else
format.html {render action: 'new'}
format.json {render json: #message.errors, status: :unprocessable_entity }
end
end
end
**Edit: Example of where I call _shortform: Layout/products **
<%= render '/partials/head' %>
<%= render '/partials/header' %>
<%= render 'layouts/messages' %>
<div class="product_layout_container">
<div class="layout_sidebar">
<%= link_to products_path do %>
<h4>Products</h4>
<% end %>
<%= render 'products/productsidebar', locals: {products: #products, categories: #categories } %>
</div>
<div class="product_yield">
<%= yield %>
</div>
</div>
<div class="page_container bottom_container">
<% unless user_signed_in? %>
<%= render partial: 'messages/shortcontact', locals: { message: #message }%>
<% end %>
</div>
<%= render '/partials/footer' %>
<%= render '/partials/foot' %>
I need help figuring out how to structure my partial form, or how I can prefill the Message#new form.... Thanks!
Start by adding an additional route (POST /messages/new):
resources :messages do
post :new, on: :collection
end
This is somewhat optional. Its possible to just setup this up with just GET as well but the parameters will be sent in the URL.
You can then simply bind the parameters to the model instance via strong the strong parameters.
class MessagesController
# ...
def new
#message = Message.new
# We need a condition here to avoid a parameter missing error
#message.assign_attributes(message_parameters) if params[:message]
end
end
You also have to manually override the path of the form:
simple_form_for(message, url: new_message_path)

Rails remote form collection_select onchange error

I'm trying to have a remote form automatically submit when a select option is changed. Everything works when I remove remote: true from the form tag, except I don't want the page to reload each time.
I am using Rails 5.1.4.
Code
<%= form_for #foo, remote: true do |f| %>
... fields
<%= f.collection_select :bar_id, #list, :id, :name,
{include_blank: '-'}, { onchange: 'this.form.submit();'} %>
<% end %>
Controller
def update
respond_to do |format|
if #foo.update(foo_params)
format.html { redirect_to #foo }
format.json { render :show, status: :ok, location: #foo }
else
format.html { render :edit }
format.json { render json: #foo.errors, status: :unprocessable_entity }
end
end
end
I'm getting the following error when the selection is updated in the view:
ActionController::InvalidAuthenticityToken
I'm assuming the this.form.submit(); is the problem. How can I get this form to submit successfully?
Try to the following simple way adding authenticity_token: true to your form tag like below
<%= form_for #foo, remote: true, authenticity_token: true do |f| %>
It should work, at least working for me.
Updated After Comment
You need to work with partial if you use remote: true like if your form in new.html.erb then cut out form part and create a partial for the form like _form.html.erb then render this into new.html.erb like
new.html.erb
<div id="my-form">
<%= render partial: "form" %>
</div>
and the create a js.erb file based your main file like new.js.erb and put this line like
$("#my-form").html("<%= escape_javascript(render("form")) %>");
On your controller add format.js like below
format.html { redirect_to #foo }
format.js
format.json { render :show, status: :ok, location: #foo }
For complete jQuery/AJAX reference with RoR-5 you can read this tutorial it will help to brush up your jQuery/AJAX skill
Hope that will work
add this line to your form.
<%= tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) %>
and your form should be like this:
<%= form_for #foo, remote: true do |f| %>
<%= tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) %>
... fields
<%= f.collection_select :bar_id, #list, :id, :name,
{include_blank: '-'}, { onchange: 'this.form.submit();'} %>
<% end %>
The authenticity token is a random value generated in your view to prove a request is submitted from a form on your site, not somewhere else. This protects against CSRF attacks
You can avoid above error by two ways
1) Add following code in application controller
skip_before_filter :verify_authenticity_token
Reference Link: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
2) Add authenticity_token in the form
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
OR <%= csrf_meta_tags %>

Creating new registration and new order at the same time

I'm trying to add an event registration to a current or new order. Question at the end of the post.
Event model: Contains the basic event information like title, date, description. This event model has many event options.
Event Option: Contains a description and a price. This event option has many registrations.
Registration: Allows the user to register and it takes the price from the event option price. This registration belong to an event option and to the order model.
Order: Calculates the total of the order based on the sum of all the registrations associated with it.
Creating a new registration:
In the event option show page, I have a form that creates a new registration using remote: true.
Here's the form:
<%= form_for(#registration, remote: true) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="field">
<%= f.label :event_option_id %><br>
<%= f.text_field :event_option_id, value: #event_option.id %>
</div>
<div class="field">
<%= f.label :order_item_id %><br>
<%= f.text_field :order_item_id%>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price, value: #event_option.price%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When the form is submitted, it creates the event and also creates a new order if the order does not exist. To create the new order I have this helper method in the application controller:
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
end
Here's the create method in the registrations controller:
def create
#order = current_order
#registration = Registration.new(registration_params)
#order_id = current_order.id
respond_to do |format|
if #registration.save
format.html { redirect_to #registration, notice: 'Registration was successfully created.' }
format.json { render :show, status: :created, location: #registration }
format.js {}
#order.save
session[:order_id] = #order.id
else
format.html { render :new }
format.json { render json: #registration.errors, status: :unprocessable_entity }
end
end
end
The problem is that I'm not able add the registration to the order. I'm guessing that this is happening because the registration is created before the order. The last two line of the if #registration.save in the respond_to block are saving the order. How can I add the registration to the order? Can both, the new registration and new order be created at the same time?
A simple way to get around this is to assign the registration to the order before saving the order...
#order.registrations << #registration
#order.save
Alternatively you could create the association at the time you're creating the #registration record.
Instead of...
#registration = Registration.new(registration_params)
do....
#registration = #order.registrations.build(registration_params)

Why is my Rails hidden_field not picking up any value when all other fields work fine?

I have a model for a "timeline_event" which belongs_to a sales_opportunity. For example it could be "call the prospect" or similar, with a due_date, activity_details (optional), and a checkbox to say whether it's complete or not. This works fine. I can add new timeline_events to the database via a modal on the sales_opportunity page, which also works fine. However when I try to edit the timeline_event (via the timeline_events edit controller action) the hidden_field for sales_opportunity_id refuses to pick up the value (it has no value whatsoever). Even when I try to force a value:
<%= f.hidden_field :sales_opportunity_id, :value => #sales_opportunity.id %>
It won't add any value to the hidden_field. I've tried moving the field around within the form group, but nothing seems to work. I have other forms that are similar in my model that work fine, but for some reason this isn't working - can anyone help please?
My Form:
<%= form_for(#timeline_event, :html => {:class => "form-horizontal"}) do |f| %>
<div class="form-group">
<%= f.hidden_field :sales_opportunity_id %>
<%= f.label :activity, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.text_field :activity, :placeholder => "Enter activity details" %>
</div>
</div>
<div class="form-group">
<%= f.label :due_date, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<div class='input-group date' id='datetimepicker' data-date-format="YY.MM.DD">
<%= f.text_field :due_date, class: "form-control", data: { date_format: 'YYYY/MM/DD' }, :placeholder => "YYYY/MM/DD" %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :event_notes, 'Activity Details', :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.text_area(:event_notes, :placeholder => "Put any notes about the activity here") %>
</div>
</div>
<div class="col-md-6 col-md-offset-4">
<div class="checkbox input">
<label>
<%= f.check_box :completed %> Task Completed?
</label>
</div>
<br>
</div>
<%= #sales_opportunity.id %>
<%= f.submit "Save", class: "btn btn-large btn-success" %>
<% end %>
*Note - I include the line <%= #sales_opportunity.id %> here and this does output the correct sales_opportunity_id for the timeline_event. All other fields (activity, due_date, completed) are populated with the information of the timeline_event I'm trying to edit.
Timeline_Events_Controller:
class TimelineEventsController < ApplicationController
before_action :set_timeline_event, only: [:show, :edit, :update, :destroy]
before_action :signed_in_user, only: [:edit, :update, :show, :index]
before_action :correct_org, only: [:edit, :update, :show, :index]
# GET /timeline_events
# GET /timeline_events.json
def index
#timeline_events = TimelineEvent.all
end
# GET /timeline_events/1
# GET /timeline_events/1.json
def show
end
# GET /timeline_events/new
def new
#timeline_event = TimelineEvent.new(sales_opportunity_id: params[:sales_opportunity_id])
end
# GET /timeline_events/1/edit
def edit
#timeline_event = TimelineEvent.find(params[:id])
#sales_opportunity = #timeline_event.sales_opportunity
end
# POST /timeline_events
# POST /timeline_events.json
def create
#timeline_event = TimelineEvent.new(timeline_event_params)
#sales_opportunity = #timeline_event.sales_opportunity
respond_to do |format|
if #timeline_event.save
format.html { redirect_to #timeline_event.sales_opportunity, :flash => {:success => 'Timeline event was successfully created.'} }
format.json { render :show, status: :created, location: #timeline_event }
format.js
else
format.html { render :new }
format.json { render json: #timeline_event.errors, status: :unprocessable_entity }
format.js { render json: #sales_opportunity.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /timeline_events/1
# PATCH/PUT /timeline_events/1.json
def update
respond_to do |format|
if #timeline_event.update(timeline_event_params)
format.html { redirect_to #timeline_event.sales_opportunity, :flash => {:success => 'Timeline event was successfully updated.'} }
format.json { render :show, status: :ok, location: #timeline_event }
else
format.html { render :edit }
format.json { render json: #timeline_event.errors, status: :unprocessable_entity }
end
end
end
# DELETE /timeline_events/1
# DELETE /timeline_events/1.json
def destroy
#timeline_event.destroy
respond_to do |format|
format.html { redirect_to timeline_events_url, :flash => {:success => 'Timeline event was successfully destroyed.'} }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_timeline_event
#timeline_event = TimelineEvent.find(params[:id])
end
def timeline_event_params
params.require(:timeline_event).permit(:sales_opportunity_id, :due_date, :activity, :completed, :event_notes)
end
#before filters
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
end
def correct_org
timeline_org = #timeline_event.sales_opportunity.user.organization
#user = current_user
if #user.organization_id == timeline_org.id
else
redirect_to root_url, notice: "You are not permitted to visit that page. Please create an account or sign in"
end
end
end
My timeline_event model:
class TimelineEvent < ActiveRecord::Base
belongs_to :sales_opportunity
validates :activity, presence: true
validates :due_date, presence: true
validates :sales_opportunity_id, presence: true
end
I can only imagine this is some syntax error or other stupid error on my part, because I just can't see anything wrong with the code (it's identical to all other forms on my site). Can anyone spot where I'm going wrong here please?
Thanks!
I disagree with #smathy as you indeed can explicitly pass a value to a hidden_field tag like you have done.
However, that's not even necessary because you are doing a circular assignment. By that I mean you are declaring #sales_opportunity and assigning it the value of #timeline_event.sales_opportunity in the edit action of your controller.
You then go into the view and try to assign #timeline_event.sales_opportunity back to #sales_opportunity. That's circular logic.
If the sales_opportunity for the timeline_event you are editing is already present, and you aren't wanting to allow the user to edit it, why include it in the form at all? Delete the #sales_opportunity instance variable from the edit action in the controller, and just remove the hidden field entirely from the view.
See if doing that makes it work as desired.
The form_for field helpers don't work like that, they don't take a :value key in the options. Basically the name of the field must correspond to a method on the object you've passed into form_for (in your case #timeline_event), and that's where the form will get the value from.
If you want to provide your own field and value, then use the _tag family of helpers, eg.:
<%= hidden_field_tag :name_of_field, #sales_opportunity.id %>
Or use f.fields_for to create a sub-field (usually in combination with accepts_nested_attributes_for)

Issues creating a url form field using action view and form_for

Trying to create a form field where a user can submit a url per: http://apidock.com/rails/v3.2.13/ActionView/Helpers/FormHelper/url_field
I'm getting an error: ActionView::Template::Error (undefined method `homepage' for #
here is the model:
class Idea < ActiveRecord::Base
has_many :comments
mount_uploader :picture, PictureUploader
attr_accessible :description, :name, :picture, :homepage
end
the view in form.html.erb
<%= form_for(#idea) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :link %><br />
<%= url_field("homepage") %><br />
</div>
<div class="actions">
<%= f.submit %>
the view in show.html.erb
<p><b>Name: </b><%= #idea.name %></p>
<p><b>Link:</b><%= #idea.homepage %></p>
ideas_controller
def create
#idea = Idea.new(params[:idea])
respond_to do |format|
if #idea.save
format.html { redirect_to #idea, notice: 'Idea was successfully created.' }
format.json { render json: #idea, status: :created, location: #idea }
else
format.html { render action: "new" }
format.json { render json: #idea.errors, status: :unprocessable_entity }
end
end
end
def show
#idea = Idea.find(params[:id])
#comment = #idea.comments.build
respond_to do |format|
format.html # show.html.erb
format.json { render json: #idea }
end
end
Basically, when you're yielding, and using, a variable to the block in form_for, it already sets the association of the form fields.
ie:
url_field('user', 'homepage')
is equivalent to
f.url_field('homepage')
Check out the url_field, and the form_for documentation
IMHO using url_field in the form builder is antiquated and prone to errors. Eventually I was able to find: rails auto link from tenderlove: https://github.com/tenderlove/rails_autolink coupled with tinymce-rails from spohlenz: https://github.com/spohlenz/tinymce-rails. With these 2 gems you can build a full-featured form field and display the output much more effectively. Hopefully this helps someone else.

Resources