Question

I have a short question to help me understand Rails partials. I am new to RoR and just working through Agile Web Development with Rails. I am in Iteration F1 if you have the same book.

When i am not using partials, the show.html.erb from the carts looks like this:

<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>

<!-- START_HIGHLIGHT -->
<h2>Your Cart</h2>
<table>
<!-- END_HIGHLIGHT -->
  <% @cart.line_items.each do |item| %>
<!-- START_HIGHLIGHT -->
    <tr>
      <td><%= item.quantity %>&times;</td>
      <td><%= item.product.title %></td>
      <td class="item_price"><%= number_to_currency(item.total_price) %></td>
    </tr>
<!-- END_HIGHLIGHT -->
  <% end %>
<!-- START_HIGHLIGHT -->
  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>
  </tr>
<!-- END_HIGHLIGHT -->
<!-- START_HIGHLIGHT -->
</table>
<!-- END_HIGHLIGHT -->

<%= button_to 'Empty cart', @cart, method: :delete,
    data: { confirm: 'Are you sure?' } %>

Then, when I start to use partials, I created a class called _cart.html.erb and put this in it:

<h2>Your Cart</h2>
<table>
  <%= render(cart.line_items) %>

  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
  </tr>

</table>

<%= button_to 'Empty cart', cart, method: :delete, data: { confirm: 'Are you sure?' } %>

And modified my show.html.erb to this:

<% if notice %>
    <p id="notice"><%= notice %></p>
<% end %>

  <%= render(@cart) %>

So, my confusion is now, why do I have to use @cart.something when I dont have partials. For my understanding it was the model I was using which called carts.rb. So, when I then create a partial, then I just simply use cart instead of @cart and the partial is still using the model?

But why then I use render(@cart)? Is this command then just using my partial _cart.html.erb? can anybody help me to complete my understanding of it? probably it is like it is, but i confuses me a little bit at the moment :)

Was it helpful?

Solution

When you say render(@cart), what's really happening behind the scenes is Rails is making a lot of assumptions for you based on the conventions of how Rails generally works.

Let's start off with the @cart variable. This is an instance variable, which is a variable that's accessible to anything inside your controller. Views have a special relationship with controllers, so instance variables defined within a controller method are available within the view.

You could use @cart within your partial, and everything would work fine in this case. But it's generally a bad idea - you want partials to be reusable in different spots, and assuming that an instance variable exists introduces coupling between your controller and the partial.

The solution that Rails provides is the ability to pass local variables to a partial. Remember how I said that render(@cart) is a shortcut? What's really happening behind the scenes is this:

render partial: 'carts/cart', locals: { cart: @cart }

Because you're following Rails conventions, Rails can make the following assumptions:

  • The partial for a Cart is located in app/views/carts/_cart.html.erb
  • That partial is going to use a local variable named cart, which is passed to the method.

So as a result, the following:

render @cart

has the same effect as the more verbose line above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top