obx
obx

Reputation: 13

Learn Ruby on Rails: NoMethodError (undefined method `[]' for nil:NilClass):

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

Answers (4)

Kyle
Kyle

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

Gabriel Gomes
Gabriel Gomes

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

Sebasti&#225;n Palma
Sebasti&#225;n Palma

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

Don M
Don M

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

Related Questions