I was going through a video and where he was rendering something like below example:
<div>
<%= render posts %> # no quotes to posts
</div>
Though he has even created a partial with _posts.html.erb, he is calling with quotes to posts.
Though he has mentioned something about it like it calls, active record model, then class and then something...i could not understand it properly. Can anyone explain clearly this with simple example.
Render with quotes
<%=render 'post'%>
Rails is going to look in the current folder for a partial file which starts with _
Render without quotes
Is still going to use the same partial, but post in this case is a variable. I think this is translating to this:
<%= render partial: "post", locals: {any_string: your_variable(in this case is post)} %>
Again I haven't checked that.
The _post.html.erb is the partial, which can look like this:
<b><%=any_string%></b>
If your_variable which was assigned to any_string will contain the string 'My name is'
Your partial will print 'My name is' in bold.
Anyway partial are more complex, and they are used for DRY-ing (Don't repeat yourself) the code.
You can see more examples here.
With quotes then you are explicitly rendering a partial of that name. Without quotes something quite interesting is happening. posts (without quotes) is a variable that will be an activemodel list of records.
Now what the call to render does is it will look at the type of each of the models and then find the correct partial for the model (which will be the name of the model camel_cased) and render each one in turn.
EDIT:
If you have a model called Post and you assign some of those records to a variable (he uses posts I assume but I'll use foo to disambiguate) like so:
foo = Post.all
then by calling render foo the render function will see that you have an activerecord collection of records, it will then check the model associated with these records (Post in our example) and will loop through all of them rendering them to a partial called _post.html.erb with a local variable for each record assigning the record to post.
<%= render foo %>
is equivalent to:
<% foo.each do |my_post| %>
<%= render partial: "post", locals: {post: my_post} %>
<% end %>
Related
Learning from the Rails guide, I don't understand how local_assign works below:
To pass a local variable to a partial in only specific cases use the
local_assigns.
index.html.erb
<%= render user.articles %>
show.html.erb
<%= render article, full: true %>
_articles.html.erb
<h2><%= article.title %></h2>
<% if local_assigns[:full] %>
<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>
This way it is possible to use the partial without the need to declare
all local variables.
How is the partial even being rendered by the show action when it has the name _articles, which will only show for the index action? I also don't see why you use add the option of full: true when you could have just used locals: {full:true}. What's the difference?
Regarding the use of local_assigns:
The point of this section of the guide is to show how to access optional locals in your partials. If a local variable name full may or may not be defined inside your partial, simply accessing full will cause an error when the local is not defined.
You have two options with optional locals:
First, using local_assigns[:variable_name], which will be nil when the named local wasn't provided, or the value of the variable.
Second, you can use defined?(variable_name) which will be nil when the variable is not defined, or truthy (the string "local_variable") when the local is defined.
Using defined? is simply a guard against accessing an undefined variable, you will still have to actually access the variable to get its value:
if local_assigns[:full]
if defined?(full) && full
As for your specific questions:
How is the partial even being rendered by the show action when it has the name _articles, which will only show for the index action?
This is a typo. The correct partial name is _article.html.erb. Regardless of the action, index or show, the correct partial name is the singular of the model. In the case of rendering a collection of models (as in index.html.erb), the partial should still be singularly named.
I also don't see why you use add the option of full: true when you could have just used locals: {full:true}. What's the difference?
The point is that the full: true syntax is shorter. You have two identical options:
render partial: #article, locals: { full: true }
or
render #article, full: true.
The second is significantly shorter and less redundant.
Is it possible to render a view with get parameters ?
For example, something like :
render "/projects/sheets?id=43"
It's because I need to render a view which uses url parameters to work properly.
I tried many ways, but it only creates parameters that I can get only in the controller and that are not available after.
It's because I want to have a view that contains the html code of many other views.
This is my current code :
allProjects.html.erb :
<% Project.where(productchief: user.id).order(:title).each do |project| %>
<%= render "/projects/sheets?id=#{project.id}" #This doesn't work. %>
<% end %>
It's because I want to have a view that contains the content of all the other views in my website to allow the users to print all this content in one time.
I don't think it's possible to do it in the way you are trying to do it. You will need to change /projects/sheets to be a partial and render that instead and pass through local variables.
So to clarify /projects/sheets.html.erb becomes /projects/_sheets.html.erb and you would then invoke as:
<%= render partial: "/projects/sheets", locals: { :project_id = project.id } %>
Then within the partial _sheets.html.erb you can make reference to project_id
Generally you should be able to access the params in a view but unless there's a very specific reason you can't, I suggested altering your routes. I may be missing some info from your original question, but let's say you have routes as:
get 'projects/sheets', to: "projects#index", as: :projects
get 'projects/sheets/:id', to: "projects#show", as: :project
That would change your urls a little but would still leave the params available. Using the routes above, for example and going to: localhost:3000/projects/sheets/5?something_fun=geeks_are_us will give the following params:
{"something_fun"=>"geeks_are_us", "controller"=>"projects", "action"=>"show", "id"=>"5"}
And if you're looking to render multiple items via a partial, you can pass the assigned variable for (in your case) 'project' to render the views but it is a partial as you appear to be rendering from a view already.
So something like:
<% #projects.each do |project| %>
<%= render partial: project, locals: {project: project} %>
<% end %>
This would try to render projects/_project.html.erb and sending project as a variable. If you need other variables in your view, just pass them in the locals hash.
Hope this is some help.
I have a view calling this:
<%= render 'health_safety/access_requests/access_request_user', collection: #access_request_users %>
And in _access_request_user.html.erb I'm trying to use the collection, e.g.
<%= p access_request_user.inspect %>
And I get the following error:
undefined local variable or method `access_request_user' for #<#<Class:0x007fc8f0160790>:0x007fc8ebf70100>
I'm absolutely stumped, tried heaps of things but nothing's working. If it makes a difference, #access_request_users is a bunch of User objects pulled from the database.
Try defining the render as a partial, and if this alone doesn't work try defining the name of the variable:
<%= render partial: 'health_safety/access_requests/access_request_user', collection: #access_request_users, as: :access_request_user %>
In an ERB view, you can call
<%= render #cart.line_items %>
and, assuming you have a partial named _line_item.html.erb in the line_items directory, Rails will take care of the rest.
But I'm having some trouble converting this to something similarly succinct in Haml.
I can get something that works by explicitly calling .each on #cart.line_items or an equivalent local variable in the partial, but calling .each is exactly what I'm trying to avoid.
Here's the partial:
%tr
%td= item.product.title
%td= item.quantity
%td.item_price= number_to_currency(item.total_price)
From what I've read so far, this looks like it should produce the same behavior:
= render 'line_items/line_item', collection: #cart.line_items, as: :item
But it's still not working as expected. item isn't being passed as a local:
undefined local variable or method `item' for #<#<Class:0x007fb3c531aee0>:0x007fb3c3692160>
Does Haml have a comparably succinct way to render collections (relative to ERB)? If so, any thoughts as to what I might be missing? Or if there's a better way to do this altogether?
Thanks.
Try this(mention partial explicitly)
= render :partial => 'line_items/line_item', collection: #cart.line_items, as: :item
The render method and how it handles partials is part of Rails, and should work the same with Erb and Haml.
When using render to automatically render a collection, as in this case with render #cart.line_items, Rails will use the name of the class each entry of the collection to determine both the partial to use and the name of the local variable used in that partial.
In your Erb example Rails is using the partial _line_item.html.erb, which suggests that the objects are of type LineItem and so Rails will create a local named line_item. However in your Haml partial you are using the name item, which isn’t being defined hence the error.
Simply change all occurances of item to line_item in your partial, and you will be able to use
= render #cart.line_items
in your Haml, the same as in Erb.
Ryan Bates' nifty_scaffolding, for example, does this
edit.html.erb
<%= render :partial => 'form' %>
new.html.erb
<%= render :partial => 'form' %>
_form.html.erb
<%= form_for #some_object_defined_in_action %>
That hidden state makes me feel uncomfortable, so I usually like to do this
edit.html.erb
<%= render :partial => 'form', :locals => { :object => #my_object } %>
_form.html.erb
<%= form_for object %>
So which is better: a) having partials access instance variables or b) passing a partial all the variables it needs?
I've been opting for b) as of late, but I did run into a little pickle:
some_action.html.erb
<% #dad.sons.each do |a_son| %>
<%= render :partial => 'partial', :locals => { :son => a_son } %>
<% end %>
_partial.html.erb
The son's name is <%= son.name %>
The dad's name is <%= son.dad.name %>
son.dad makes a database call to fetch the dad! So I would either have to access #dad, which would be going back to a) having partials access instance variables or I would have to pass #dad in locals, changing render :partial to
<%= render :partial => 'partial', :locals => { :dad => #dad, :son => a_son } %>, and for some reason passing a bunch of vars to my partial makes me feel uncomfortable. Maybe others feel this way as well.
Hopefully that made some sense. Looking for some insight into this whole thing... Thanks!
In recent versions of Rails it is quite a bit easier to render partials and pass locals to them. Instead of this.
<%= render :partial => 'form', :locals => { :item => #item } %>
You can do this.
<%= render 'form', :item => #item %>
I don't do this in the Nifty Scaffold generator to keep backwards compatibility, but I'll change this in a future release.
As for whether it's acceptable to use instance variables in partials. I think it is. In all practicality, what is the downside? Certainly things can get out of hand if you aren't consistent, but I like to apply these guidelines.
Never create an instance variable just to share it between partials. Usually this means you will only be sharing the controller resource object.
If the partial is the same name as the resource, pass it as a local with <%= render #item %>.
If the partial will be shared across multiple controllers then only use locals.
This is what works well for me anyway.
Bonus tip: if you find yourself passing in a lot of locals into a partial and you want some of them to be optional, create a helper method which renders the partial. Then always go through the helper method so you can make a clean interface with optional args for rendering the partial.
Using #instance_variables in partials is bad design.
Using instance variable in partials works, but it can make it harder to maintain applications if changes are ever needed.
The downside of using instance variables in partials is that you create a dependency in the partial to something outside the partial's scope (coupling). This makes the partial harder to reuse, and can force changes in several parts of the application when you want to make a change in one part.
Partials that use instance variables:
must change when the instance variable in any controller that uses the partial changes either the instance variable name or its type or data structure
cause all controller actions that use the partial to change in the same way at the same time when there are changes to how the instance variable is used
discourage reuse, as they can only easily be reused in actions that set up instance variables with the same name and data
Instead, pass locals to the partials:
<%= render 'reusable_partial', :item => #item %>
Now, because the partial only references item and not #item, the action that renders the view that renders the reusable_partial is free to change without affecting the reusable_partial and the other actions/views that render it:
<%= render 'reusable_partial', :item => #other_object.item %>
Also, this can be reused in contexts where there is no #item:
<%= render 'reusable_partial', :item => #duck %>
If my #duck changes in the future and no longer quacks like reusable_partial expects it to (the object's interface changes), I can also use an adapter to pass in the kind of item that reusable_partial expects:
<%= render 'reusable_partial', :item => itemlike_duck(#duck) %>
Always?
There are plenty of situations where you probably don't need de-coupled partials like this, and it's easier in the short run to use an instance variable. However, it's hard to predict the future needs of your application.
As such, this makes for good general practice while having relatively low cost.
You can have it both ways. At the top of your partial:
<% item ||= #item %>
That way, it works with or without passing the local variable, providing a sane default, but not inhibiting alternate usage of the partial.
I vote for a) for a very specific reason -- DRY! If you start passing a variable like that, the next thing you know it's a mess. Let's say you need to change the way your variable is named or something else about it. You'll need to go to ALL your views and change them instead of ONE partial.
Also, if you change your partial it will change on all your views, so you'll need to know which views are used. A proper IDE should be able to help you with that, but I also like having a small comment section at the top of the view where I just mention where it's used and why. This helps another programmer and it helps you to remember in case you need to come back to a partial and modify. But the whole point of the partial is to call it WITHOUT having to pass anything from the view, so that you don't have to modify all places where partial is called from if that variable changes somehow.
Ultimately this is a design choice, and to be honest unless you are running a facebook the extra lookup you do is not that big of a deal, but it's just not very DRY.
P.S.: Just thought about it. You can actually abstract the way you call partial in a helper method, so then if the way you call your partial needs to change, you just need to modify one place.