Reputation: 13
I am working through Daniel Kehoe's tutorial on rails and have run into a problem that I do not understand and cannot find an answer to online. I get the NoMethodError described in the title of this post when using a flash message with params.
Here is the controller
class ContactsController < ApplicationController
def process_form
Rails.logger.debug "DEBUG: params are #{params.inspect}"
flash[:notice] = "Received request from #{params[:contact][:name]}"
redirect_to root_path
end
end
It works if I remove [:contact] and leave [:name]. If I remove [:name] and leave [:contact] it redirects but doesn't display the name. If I have both [:contact] and [:name] as the controller listing shows I get the NoMethodError and it does not redirect.
Here is my view page
<% content_for :title do %>Contact<% end %>
<h3>Contact</h3>
<div class="form">
<%= form_with( url: contact_path) do |form| %>
<%= form.label :name %>
<%= form.text_field :name, autofocus: true %>
<br/>
<br/>
<%= form.label :email %>
<%= form.email_field :email %>
<br/>
<br/>
<%= form.label 'message' %>
<%= form.text_area :content, size: '40x5' %>
<br/>
<br/>
<%= form.submit 'Submit', class: 'submit' %>
<% end %>
</div>
Here is my routes.rb
Rails.application.routes.draw do
post 'contact', to: 'contacts#process_form'
root to: 'visitors#new'
end
Here is my log when I get the NoMethodError
:
Started POST "/contact" 2017-07-23 23:25:11 +0000 Processing by ContactsController#process_form as JS Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "name"=>"Jane Doe", "email"=>"[email protected]", "content"=>"oiuwroiueroieroiuerw", "commit"=>"Submit"} DEBUG: params are "✓", "authenticity_token"=>"...", "name"=>"Jane Doe", "email"=>"[email protected]", "content"=>"oiuwroiueroieroiuerw", "commit"=>"Submit", "controller"=>"contacts", "action"=>"process_form"} permitted: false> Completed 500 Internal Server Error in 3ms
NoMethodError (undefined method `[]' for nil:NilClass):
Upvotes: 1
Views: 623
Reputation: 1058
Since form_with
is the "new standard" in Rails 5.1, I'd continue to use it.
To use form_with
successfully, you have to use syntax like this:
<%= form_with scope: :post, url: posts_path do |form| %>
<%= form.text_field :title %>
<% end %>
<%# Will generate %>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="post[title]">
</form>
Or this:
<%= form_with model: Post.new do |form| %>
<%= form.text_field :title %>
<% end %>
<%# Will generate %>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="post[title]">
</form>
For your particular situation, you should probably be doing something like this:
<% content_for :title do %>Contact<% end %>
<h3>Contact</h3>
<div class="form">
<%= form_with( scope: :contact, url: contact_path) do |form| %>
<%= form.label :name %>
<%= form.text_field :name, autofocus: true %>
<br/>
<br/>
<%= form.label :email %>
<%= form.email_field :email %>
<br/>
<br/>
<%= form.label 'message' %>
<%= form.text_area :content, size: '40x5' %>
<br/>
<br/>
<%= form.submit 'Submit', class: 'submit' %>
<% end %>
</div>
By doing this, it would modify the name of each input element from being content
, email
, name
, etc to become contact[content]
, contact[email]
, contact[name]
, etc. This would then allow your controller code to work.
Upvotes: 1
Reputation: 348
If you want to get values as object like params[:contact][:name]
, you should specify the object to the form helper. Example using form_for
helper:
<%= form_for(@contact) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here is a quick reference to see how to deal with objects in form:
http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object
If you don't have any idea about how it works, i suggest you to generate the CRUD using the scaffold generator. Example:
rails g scaffold Contact name:string email:string
This command will generate all files (controller, views) related to the CRUD. So you can use this as reference. It will be already following the Rails convention.
Upvotes: 1
Reputation: 33460
form_with(url: contact_path)
won't add the resource root to your inputs within the form rendered, take a look:
<%= form_with(url: contact_path) do |form| %>
<%= form.text_field :name %>
...
<% end %>
# Will give you
<form action="/contact" method="post" data-remote="true">
<input type="text" name="name">
</form>
And form_with(model: Model.new)
:
<%= form_with(model: Model.new) do |form| %>
<%= form.text_field :name %>
<% end %>
# Will give you
<form action="/contact" method="post" data-remote="true">
<input type="text" name="model[name]">
</form>
What you need is to pass the resource to be created, you should have a new
method in your controller, and there create an instance variable which you can then use in the model
option of your form_with
.
See also Unification of form_for and form_tag into form_with .
Upvotes: 1
Reputation: 986
Your params looks something like this
params{
:content => 'Words',
:email => '[email protected]',
:name => 'Joey Smith'
}
Side note, do you intend 'content' to be 'contact'?
Params is an object and this
params[:contact][:name]
Would work if your params was structured like:
params {
:contact => {
:name => 'Joey Smith'
}
}
Which it is not.
Not sure what you are trying to do because I don't see a 'contact'
I would guess that what you want is
flash[:notice] = "Received request from #{params[:name]}"
Upvotes: 1