I am following the Agile Web Development with Rails 4 book and I'm a bit confused on a part about rendering. The simple version of the question is... within the application.html.erb file there it says
render #cart
This is confusing because I thought that there needed to be a controller associated with that view in order to know which partial and #cart variable to use. Is it simply by naming convention that this line looks for a partial like _cart.html.erb? And in that case does it not actually know what #cart is until it renders that partial?
Some clarification would be lovely. Thanks!
This is a shorthand syntax. From the docs:
Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the :object option:
<%= render partial: "customer", object: #new_customer %>
Within the customer partial, the customer variable will refer to #new_customer from the parent view.
If you have an instance of a model to render into a partial, you can use a shorthand syntax:
<%= render #customer %>
Assuming that the #customer instance variable contains an instance of the Customer model, this will use _customer.html.erb to render it and will pass the local variable customer into the partial which will refer to the #customer instance variable in the parent view.
Related
I'm adding a new partial view in ruby on rails project and want to pass some additional data to the partial. I have code below for rendering a view . I understand this is the way to pass data (id in my case) to partial view. I'm not sure if this is the best way, as I'm new to rubyonrails. How do i access in my partial view file?
render "index" , :locals => {:id => params[:id]}
You can pass data to your view(s) using options on render, one of which is locals, or by setting instance variables which automatically propagate.
Normally what's done is something like this:
#id_param = params[:id]
Where within your view you can use #id_param wherever and whenever:
<%= #id_param %>
I've chosen #id_param for a name here instead of #id to give it a bit more context as #id on its own might prompt questions of "What ID?"
If you want to do the locals method, then:
render 'index', locals: { id: params[:id] }
Using the Ruby 1.9 hash notation for simplicity here, versus the outdated 1.8 style in your original example. This produces a local variable called id that can be used in that view, like:
<%= id %>
When I call a method foo() or refer to an attribute #bar in my view file, which object is going to respond?
For example if I have a partial _ebook.html.erb that looks like
<h2>Your eBook</h2>
<p>Title: <%= #title %></p>
<p>Due Date: <%= due_date(customer) %>
etc.
which object provides #title, due_date and customer? Do those bubble up to the BooksController and its modules/superclasses?
Also, if my render includes locals, for example
render partial: "ebooks", locals: {baz: #qux}
in which object is baz being stored?
It's not other objects provide your template with local variables, it's your template being read by the controller where the object exist in the context.
The erb template annotate variables such as #bar and taken by the controller to render accordingly. If controller actually has a #foo and you want to use it as the #bar in template you plugin locals: {bar: #foo}
I've found most of the answer, at least for Rails 3.2.x. Here's my poorly written description:
As part of rendering, ActionView::Base#prepare creates an instance of an anonymous subclass of ActionView::Base and adds a couple of helper modules.
Then the view_assigns method stores the controller's instance variables in a hash in that instance.
The local variables are passed in as a hash and stored in an instance variable called locals in the same instance.
I am reading Agile Rails Web Dev book and so far for partials I had learned than we can call their name in a string form or if there is a collection of objects we can pass the object name and rails will figure out that it needs to loop through it as a collection.
Now I saw this code and suddenly all I had learned got confused:
<%= render #cart %>
My question is what is it #cart, Why it is not 'cart' ? And how should I have known that?
The Rails Guide suggests that when you do "render #cart", Rails introspects the model name of #cart and looks for a partial by that name in the current view path.
The implementation of render :partial actually calls to_partial_path on the passed object. A User object would by default return 'users/user'. So I'd check to see if the Cart class implements to_partial_path to return 'layouts/cart'.
I could be wrong but I believe that behind the scenes Rails will render a partial for anything that has a to_partial_path method. In this case I'm assuming #cart is an ActiveRecord object and all ActiveRecord objects respond to to_partial_path.
Try adding this to your template and see what it outputs. It should be the path of your partial.
<%= #cart.to_partial_path %>
Many helper methods, such as redirect_to, link_to, and url_for, can take an ActiveRecord object as a parameter instead of a hash that specifies the controller and action. I've seen the parameter passed different ways in different documentation. It sometimes gets passed as a symbol, sometimes as an instance variable, and sometimes as a local variable.
I'm confused about how the different parameter styles get expanded to return urls. I know that following REST conventions should create a url constructed of a controller and action but am unsure when Rails needs a specific parameter style to construct that url. Please help me understand the use cases for passing the ActiveRecord object as a symbol, an instance variable, or a local variable. Are there different requirements based on the method call? Or are there underlying differences in url construction?
Here are some examples:
From the API docs:
link_to "Profile", #profile
redirect_to post
<%= url_for(#workshop) %>
<%= form_for :person do |f| %> (this is described as the “generic #form_for”)
From the Ruby on Rails Guides:
<%= link_to 'New book', new_book_path %>
redirect_to(#book)
form_for(#article)
From the Rails 3 Way:
'link_to' "Help", help_widgets_path, :popup => 1
redirect_to post
url_for(timesheets_path)
form_for offer do |f|
Note: Upon further research, it seems that form_for is able to accept a local variable in the case where the calling view template passes a :locals hash as a parameter. The keys are the locals that can be used in the partial and the values are the instance variables from the template. Is that the correct understanding?
You can pass objects to link_to and url_for
You can also pass an object to a path helper
Both #post and post are objects. #post is an instance variable, and post is either a local variable or a method that will return a post
The only "weird" one is the form_for :post variety. This is old school Rails syntax, and will change this to the form_for #post syntax under the hood.
It seems that setting multiple instance variables in a controller's action (method) causes problems in the template, only the very first instance variable got passed to the template. Is there any way to pass multiple variables to the template? Thanks! And why, in Ruby's perspective, does the template get access to the instance variables in an action?
You might also want to look into the :locals option of render. Which accepts a hash such that keys are symbols that map to local variable names in your template, and the values are the values to set those local variables to.
Example:
render "show", :locals => {:user => User.first, :some_other_variable => "Value"}
and this template
User ID: <%= user.id %><br>
Some Other Variable: <%=some_other_variable%>
will produce:
User ID: 1<br>
Some Other Variable: Value
When you're reusing partials across multiple controllers. Setting local variables with the :locals option is simpler and much more DRY than using instance variables.
you shouldn't have any problem setting multiple instance variables. For example:
class CarsController < ApplicationController
def show
#car = Car.find(:first)
#category = Category.find(:first)
end
end
will allow you to access both #car and #category in cars/show.html.erb
The reason this works is nothing inherent to ruby, but some magic built into rails. Rails automatically makes any instance variable set in a controller action available to the corresponding view.