Reputation: 2965
I'm learning RoR and I'm having trouble getting a value from a related table to display. I've tried the suggestions mentioned here and here but I still haven't gotten it to work.
I have a customer record that has a one-to-many relationship with contacts.
Here are snips of my models:
class Contact < ActiveRecord::Base
belongs_to :customer
class Customer < ActiveRecord::Base
has_many :contacts, dependent: :destroy
and this is my Contacts index.html.erb that is producing the error
<% @contacts.each do |contact| %>
<tr class="<%= cycle("even pointer", "odd pointer") %>">
<td><%= contact.first_name %> <%= contact.last_name %></td>
<td><%= contact.customer.name %></td> <!-- this is the error line -->
<td><%= contact.office_phone %></td>
<td><%= contact.cell_phone %></td>
<td><a href="<%= contact.email %>"><%= contact.email %></a></td>
<td>
<% if current_user.admin? %>
<%= link_to "edit", contact, method: :edit %>
<% end %>
</td>
</tr>
<% end %>
The error I get is: undefined method `name' for nil:NilClass
What am I doing wrong? It seems to me like the way I'm referencing the field I'm not actually getting to the data in the Customers table but I'm not really sure. Did I build the relationship correctly in my models?
UPDATE to add controller code
class ContactsController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]
def index
@contacts = Contact.paginate(page: params[:page])
end
def show
@contact = Contact.find(params[:id])
end
def new
@contact = Contact.new
end
def create
@contact = Contact.new(contact_params)
if @contact.save
flash[:success] = "Contact created!"
redirect_to @contact
else
render 'new'
end
end
def edit
@contact = Contact.find(params[:id])
end
def update
@contact = Contact.find(params[:id])
if @contact.update_attributes(contact_params)
flash[:success] = "Contact updated"
redirect_to @contact
else
render 'edit'
end
end
private
def contact_params
params.require(:contact).permit(:first_name, :last_name, :email, :address1,
:address2, :office_phone, :cell_phone, :website, :city, :zip, :facebook, :twitter)
end
end
class CustomersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]
def index
@customers = Customer.paginate(page: params[:page])
end
def show
@customer = Customer.find(params[:id])
end
def new
@customer = Customer.new
end
def create
end
def edit
@customer = Customer.find(params[:id])
end
def update
@customer = Customer.find(params[:id])
if @customer.update_attributes(customer_params)
flash[:success] = "Customer updated"
redirect_to @customer
else
render 'edit'
end
end
private
def customer_params
params.require(:customer).permit(:name, :email, :address1,
:address2, :phone, :fax, :website, :city, :zip, :facebook, :duns_number)
end
end
EDIT: This is my contact.html.erb form.
<%= form_for @contact, html: { class: "form-horizontal form-label-left" } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<%= f.label :first_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :first_name, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="form-group">
<%= f.label :last_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :last_name, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="form-group">
<%= f.label :customer_id, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }), class: 'form-control' %>
</div>
</div>
<div class="form-group">
<%= f.label :office_phone, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :office_phone, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="ln_solid"></div>
<div class="form-group">
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-3">
<%= f.submit "Save changes", class: "btn btn-primary" %>
</div>
</div>
<% end %>
Upvotes: 2
Views: 2816
Reputation: 21
Generally this can help
For example, you have 2 tables City and Post, each post has city_id and you want to display in the views the name of the city associated with the post, you should call in the views the bloc then the name of the table then the name of column like that:
<% @post.each do |post| %>
<%= post.city.name %
<% end %>
Upvotes: 1
Reputation: 6187
You are referencing it correctly.
It looks like your contact
has not yet been saved to the database. This would cause the relation to not yet exist.
You would might need to be creating the relationship differently.
I usually do something like:
customer = Customer.create
customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith'})
which creates the associations and commits to the DB right away. Then, in the same request, your contact
's customer
will be set and accessible.
You could also do this, but it's a little redundant:
customer = Customer.create
customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith', customer: customer})
EDIT
It seems that perhaps you need to be assigning a customer to the contact when it is created/updated. In your Contact form, you'll likely need something like this (seeing your existing forms would help here):
<%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }) %>
or (using simple_form):
f.input :customer_id, collection: Customer.all, selected: @contact.customer_id
# or more simply
f.association :customer
Or maybe a hidden form field if you know the Customer when building the form:
f.input :customer_id, as: :hidden, value: @customer.id
Then in your controller, add customer_id
to the contact_params
like this:
def contact_params
params.require(:contact).permit(
:first_name, :last_name, :email, :address1,
:address2, :office_phone, :cell_phone,
:website, :city, :zip, :facebook, :twitter,
:customer_id
)
end
Upvotes: 2
Reputation: 15146
Try .try() activesupport method:
<td><%= contact.customer.try(:name) %></td>
If contact.customer
is nil (Contact doesn't belongs_to
Customer), :name
method wouldn't be invoked.
UPDATED Another hints:
a. That's a bad thing that you have a contact without customer. Consider to add this to your model:
class Contact < ActiveRecord::Base
...
validates :customer, presence: true
b. You can replace code like that:
<tr class="<%= cycle("even pointer", "odd pointer") %>">
with
<%= content_tag :tr, class: cycle('event pointer', 'odd pointer') %>
c. Replace this:
<a href="<%= contact.email %>"><%= contact.email %></a>
with this:
<%= mail_to contact.email, contact.customer.try(:name) %>
Upvotes: 1