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!
A partial allows you to separate layout code out into a file which will be reused throughout the layout and/or multiple other layouts. For example, you might have a login form that you want to display on 10 different pages on your site.
Ruby on Rails Views Partials Partials allow you to extract pieces of code from your templates to separate files and also reuse them throughout your templates. To create a partial, create a new file that begins with an underscore: _form.html.erb.
In Rails, instance variables (like @books ), are used to share data between your controller & views. But you can still use them normally, for your own classes.
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 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:
<%= 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) %>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With