Rails remote form collection_select onchange error - ruby-on-rails

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 %>

Related

Rails - How to implement :remote => true, to edit comments on the same page

I'm building an events site using Rails 5. On the Event#show page I have a comments section which I want to give edit/update functionality to without the user leaving the page.
I'm trying to use :remote => true in the form but I'm hitting errors, here's the views and controller code -
comments/_form.html.erb
<%= simple_form_for([#event, #event.comments.build, :remote => true ]) do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
Comments_controller.rb
def update
respond_to do |format|
if #comment.update
format.html { redirect_to #comment, notice: 'Comment was successfully updated.' }
format.js { }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
The current error I'm getting is 'undefined method `model_name' for {:remote=>true}:Hash'. This is pointing to the events#show method in the Events controller. I'm not sure I'm using :remote => true in the correct place hence the error. This is the first time I've done this so I'm hitting errors each step of the way. Any help/assistance would be appreciated.
You are not using proper format of simple_form_for. It should be
<%= simple_form_for([#event, #event.comments.build], remote: true ) do |f| %>

Rails: ActionController::InvalidAuthenticityToken when adding a Image

I am new to Ruby-on-rails and I am currently working on a project that let a user log in to add,create update delete a Marvel character. Each characters have a name, description, origin, alliance and image.
I used Carrierwave for file upload.
I used the scaffold command and everything was working fine, until I decided to be able to create and update my characters on the same page using .js.erb files instead of having to redirect the user to 2 different pages for the create and the update.
I have the following error everytime I try to create a character with a image. everything works fine when I don't add a image:
ActionController::InvalidAuthenticityToken
I know that there are a few different other similar questions already asked on the forum but I can't seem to find the answer to my problem.
I am using Rails 4.2.6.
I tried to add the gem remotipart but it didn't fix my issue.
create.js.erb code:
$("#characters").append("<%= escape_javascript(render #character)%>");
create action in the controller:
def create
#character = Character.new(character_params)
respond_to do |format|
if #character.save
format.html { redirect_to #character, notice: 'Character was successfully created.' }
format.json { render :show, status: :created, location: #character }
format.js
else
format.html { render :new }
format.json { render json: #character.errors, status: :unprocessable_entity }
end
end
end
I hope I provide enough information, thanks in advance!
Edit:
Here is the code I have in the form.html.erb that let the users add a image:
<div class="field">
<%= f.label :image %>
<%= f.file_field :image %>
<% if f.object.image %>
<%= image_tag f.object.image.url %>
<!--<%= f.label :remove_image %>
<%= f.check_box :remove_image %> -->
<% end %>
</div>
I think your Rails forms now will not render the CSRF field in the form:
<%= form_for #character, :remote => true, :authenticity_token => true,:multipart => true do |f| %>
.....
<% end %>
skip_before_action :verify_authenticity_token
Put this in your controller

Validations with Simple Form

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.

simple_form_for undefined method `model_name' for NilClass:Class

I receive the following error when building a basic form to create new data. When I hit submit I get
simple_form_for undefined method `model_name' for NilClass:Class
**_form.html.erb**
<%= simple_form_for(#calls) do |f| %>
<%= f.error_notification %>
<%= f.input :caller_name %>
<%= f.input :caller_phone, hint: "xxx-xxx-xxxx" %>
<%= f.input :caller_address %>
<%= f.input :primary_diagnosis %>
<%= f.error :base %>
<%= f.button :submit %>
<% end %>
**calls_controller.rb**
def create
#call = Call.new(params[:call])
respond_to do |format|
if #call.save
format.html { redirect_to #call, notice: 'Call was successfully created.' }
format.json { render json: #call, status: :created, location: #call }
else
format.html { render action: "new" }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
**new.html.erb**
<h1>New Call</h1>
<%= render 'form' %>
<%= link_to 'Back', calls_path %>
I'm at a bit of a lost here as I've followed rails naming conventions, even tried this with a scaffold with the same result. Already restarted the webrick as well. Help?
You are asking for a simple_form_for(#calls)
Yet your controller is creating #call
You should change
<%= simple_form_for(#calls) do |f| %>
to
<%= simple_form_for(#call) do |f| %>
Assuming you set #call in the controller, I believe you need to pass it to the partial. See part 3.4.4 on this page: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
The tag should look like this:
<%= render :partial => "form", :locals => { :calls=> #calls} %>
I think you must have a "new" method defined in your controller. Just create a new empty instance and give it back to the view.
def new
#call = Call.new
end
The "create" method is a response for the POST action, when you send back a form with contents. Then you create a new instance from the params received and save it to the database.

Rails: Problem with routes and special Action

Sorry for this question but I can't find my error!
In my Project I have my model called "team".
A User can create a "team" or a "contest". The difference between this both is, that contest requires more data than a normal team.
So I created the columns in my team table.
Well... I also created a new view called create_contest.html.erb :
<h1>New team content</h1>
<% form_for #team, :url => { :action => 'create_content' } do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<p>
<%= f.label :url %><br />
<%= f.text_fiels :url %>
</p>
<p>
<%= f.label :contact_name %><br />
<%= f.text_fiels :contact_name %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
In my teams_controller, I created following functions:
def new_contest
end
def create_contest
if #can_create
#team = Team.new(params[:team])
#team.user_id = current_user.id
respond_to do |format|
if #team.save
format.html { redirect_to(#team, :notice => 'Contest was successfully created.') }
format.xml { render :xml => #team, :status => :created, :location => #team }
else
format.html { render :action => "new" }
format.xml { render :xml => #team.errors, :status => :unprocessable_entity }
end
end
else
redirect_back_or_default('/')
end
end
Now, I want on my teams/new.html.erb a link to "new_contest.html.erb".
So I did:
<%= link_to 'click here for new contest!', new_contest_team_path %>
When I go to the /teams/new.html.erb page, I get following error:
undefined local variable or method `new_contest_team_path' for #<ActionView::Base:0x16fc4f7>
So I changed in my routes.rb, map.resources :teams to map.resources :teams, :member=>{:new_contest => :get}
Now I get following error: new_contest_team_url failed to generate from {:controller=>"teams", :action=>"new_contest"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: ["teams", :id, "new_contest"] - are they all satisfied?
I don't think adding :member => {...} is the right way doing this. So, can you tell me what to do? I want to have an URL like /teams/new-contest or something.
My next question: what to do (after fixing the first problem), to validate presentence of all fields for new_contest.html.erb? In my normal new.html.erb, a user does not need all the data. But in new_contest.html.erb he does. Is there a way to make a validates_presence_of only for one action (in this case new_contest)?
UPDATE:
Now, I removed my :member part from my routes.rb and wrote:
map.new_contest '/teams/contest/new', :controller => 'teams', :action => 'new_contest'
Now, clicking on my link, it redirects me to /teams/contest/new - like I wanted - but I get another error called:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I think this error is cause of #team at <% form_for #team, :url => { :action => 'create_content_team' } do |f| %>
What to do for solving this error?
I'm not sure about how your models work, but in my code I've always written;
#team.user_id = #current_user.id
instead of
#team.user_id = current_user.id
That would mean the id wasn't being passed to the controller giving you the error, wouldn't it?
Okay, I found my errors.
For the record:
First of all, I forgot to write the code inside my def new_contest. Here it is:
def new_contest
if #can_create
#team = Team.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #team }
end
else
redirect_back_or_default('/')
end
end
There were several typos, too, in my .erb file like text_fiels instead of text_field or create_content instead of create_contest.
current_user is working fine for me.

Resources