Dustin James
Dustin James

Reputation: 571

Dynamically render a partial using a tabbed nav in Rails

I have a web app that contains a three models - Customers, Jobs and Rooms. They are associated as follows:

class Customer < ActiveRecord::Base
has_many :jobs

class Job < ActiveRecord::Base
belongs_to :customer
has_many :rooms

class Room < ActiveRecord::Base
belongs_to :job

Users can create as many rooms as they want.

Currently, I have a view that looks like this:

<div class="row">
  <div class="panel panel-primary">
    <div class="panel-heading">
      <h3 class="panel-title center">Room Information</h3>
    </div>
      <div class="panel-body">
        <ul class="nav nav-tabs" role="tablist">
          <% @job.rooms.each do |room| %>
            <li><%= link_to room.room_type %></li>
          <% end %>
        </ul>
            <%= render 'layouts/roomview' %>

            <div class="pull-right">
              <%= link_to new_job_room_path(@job, @customer), class: "btn btn-primary" do %>
                <span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;Add New Room
              <% end %>
            </div>
    </div>
  </div>
</div>

In this view, I would like to click each tab and then have the roomview partial render for the room associated to the corresponding tab.

My _roomview.html.erb partial is as follows:

<% @job.rooms.each do |job| %>
  <div class="table-responsive">
    <table class="table">
      <tr>
        <h4 class="info-text"><%= room.room_type %> Information</h4>
      </tr>
        <tr class="info">
          <th class="col-md-2 info-text"><u>Room Details</u></th>
          <th class="col-md-2"></th>
          <th class="col-md-2 info-text"><u>Door Style Details</u></th>
          <th class="col-md-2"></th>
          <th class="col-md-2 info-text"><u>Drawer Box Details</u></th>
          <th class="col-md-2"></th>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Room Type:</strong></td>
          <td class="col-md-2"><%= room.room_type %></td>
          <td class="col-md-2"><strong>Upper Door Style:</strong></td>
          <td class="col-md-2"><%= room.upper_door_style %></td>
          <td class="col-md-2"><strong>Drawer Box Style:</strong></td>
          <td class="col-md-2"><%= room.drawer_box_style %></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Material Type:</strong></td>
          <td class="col-md-2"><%= room.material %></td>
          <td class="col-md-2"><strong>Lower Door Style:</strong></td>
          <td class="col-md-2"><%= room.lower_door_style %></td>
          <td class="col-md-2"><strong>Drawer Track Style:</strong></td>
          <td class="col-md-2"><%= room.track_style %></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Material Details:</strong></td>
          <td class="col-md-2"><%= room.material_details %></td>
          <td class="col-md-2"><strong>Drawer Front Style:</strong></td>
          <td class="col-md-2"><%= room.drawer_front_style %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Exterior Colour:</strong></td>
          <td class="col-md-2"><%= room.exterior_colour %></td>
          <td class="col-md-2"><strong>Panel Back Style:</strong></td>
          <td class="col-md-2"><%= room.panel_back_style %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Interior Colour:</strong></td>
          <td class="col-md-2"><%= room.interior_colour %></td>
          <td class="col-md-2"><strong>Finished End Style:</strong></td>
          <td class="col-md-2"><%= room.finished_ends %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr class="info">
          <th class="col-md-2 info-text"><u>Counter Top Details</u></th>
          <th class="col-md-2"></th>
          <th class="col-md-2 info-text"><u>Molding Details</u></th>
          <th class="col-md-2"></th>
          <th class="col-md-2 info-text"><u>Custom Order Details</u></th>
          <th class="col-md-2"></th>
        </tr>       
        <tr>
          <td class="col-md-2"><strong>Counter Top Material:</strong></td>
          <td class="col-md-2"><%= room.counter_top_material %></td>
          <td class="col-md-2"><strong>Closed To Ceiling:</strong></td>
          <td class="col-md-2"><%= room.closed_to_ceiling %></td>
          <td class="col-md-2"><strong>Custom Order Name:</strong></td>
          <td class="col-md-2"><%= room.order_name %></td>
        </tr>   
        <tr>
          <td class="col-md-2"><strong>Counter Top Supplier:</strong></td>
          <td class="col-md-2"><%= room.counter_top_supplier %></td>
          <td class="col-md-2"><strong>Crown Molding:</strong></td>
          <td class="col-md-2"><%= room.crown_molding %></td>
          <td class="col-md-2"><strong>Custom Order Details:</strong></td>
          <td class="col-md-2"><%= room.order_description %></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Counter Top Colour:</strong></td>
          <td class="col-md-2"><%= room.counter_top_colour %></td>
          <td class="col-md-2"><strong>Under Cabinet Molding:</strong></td>
          <td class="col-md-2"><%= room.under_cabinet_molding %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Counter Top Edge:</strong></td>
          <td class="col-md-2"><%= room.counter_top_edge %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Counter Top Backsplash:</strong></td>
          <td class="col-md-2"><%= room.backsplash %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>
        <tr>
          <td class="col-md-2"><strong>Sink Install:</strong></td>
          <td class="col-md-2"><%= room.sink_install %></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
          <td class="col-md-2"></td>
        </tr>

    </table>
  </div>
<% end %>

Here are my routes:

resources :customers do
  resources :jobs
end

resources :jobs do
  resources :rooms
end

And finally, here is my jobs show controller:

def show
  @job = Job.find(params[:id])
  @customer = Customer.find(params[:customer_id])
end

As it stands right now, when I load the view I am getting a controller name error for the show action. The error states that I have an undefined method for 'room'.

Considering my stated functionality above - can anyone troubleshoot this? I am unsure of how to structure the show action to properly render my partial.


Here is the exact error string:

undefined local variable or method `room' for #<#<Class:0x007fa01f9827b0>:0x007fa01f9a8d48>

Upvotes: 1

Views: 1071

Answers (1)

Richard Peck
Richard Peck

Reputation: 76784

Several issues here:


Controller

If your models are nested, why don't you use your associations in the controller calls:

#app/controllers/jobs_controller.rb
class JobsController < ActiveRecord::Base
  def show
    @customer = Customer.find params[:customer_id]
    @job = @customer.jobs.find params[:id]
  end
end

Far more efficient!


Error

Your error is quite explicit:

undefined local variable or method `room'

This means that somewhere in your show view, your app is going to try and reference room (local var) without it being defined. Without a specific reference to the error in question, I'll have to work through to the cause of the issue:

#app/views/jobs/_roomview.html.erb
<% @job.rooms ...

Firstly, the above is incorrect. In partials, you need to pass "local" variables (as otherwise, they lose their modularity):

<%= render partial: "roomview", locals: { job: @job } %>

What would be better would be to use the following:

<%= render partial: "roomview", collection: @job.rooms, as: :room %>

#_roomview.html.erb
(no need for .each any more)
<div class="table-responsive"> ....

--

Finally, I would look at how you're calling the various dependencies of your show view. Specifically the partial, but also any links etc. You have the following link:

<%= link_to room.room_type %>

Surely you'd be better calling <%= link_to "Room", room.room_type %>

Upvotes: 3

Related Questions