I'm rendering a partial in a collection like this :
<%= render :partial => 'issues/issue', :collection => #issues %>
Inside the partial, I want to render a element unless it's the last in the collection. I could of course, render the partial like this
<%= render :partial => 'issues/issue', :collection => #issues, :locals => {:issue_count => #issues.length } %>
then put this inside my partial
<% unless issue_counter + 1 == issue_count %>
<hr />
<% end %>
but I don't want to have to explicitly set the local in the render call, and I the collection isn't always going to be called #issues, so I can't just access the instance varibale. Is there some way to access the length of the collection automatically inside the partial to tell where in the collection the object falls? If there's not already, is it possible to add this in such a way that I'll automatically get the issue_count local? Any help on this will be much appreciated.
Thx,
-C
You can supply the :spacer_template option to your render :partial => X, :collection => Y call. See the ActionController::Base documenation for usage.
It probably feels heavy-handed to specify an entire partial file for a simple <hr /> element, but going this route keeps your intention clear and keeps the item partial free of unrelated divider markup.
I think you can make this happen if you modify the render_partial_collection method.
As a side note: Seems to me like you should instead use CSS on a unordered list: I get the feeling you are inserting markup HRs to style instead of using it to semantically separate items in the collection (in mark-up semantics).
Related
I want to render some child items with something other than their default partial (i.e., app/views/child_items/_child_item.html.erb). The default one was scaffolded and it isn't great for public viewing of something, but I still want to keep it for back-end management purposes.
This is what I'm going for inside the view of the parent item, assuming a partial defined in app/views/child_items/_alternate_partial.html.erb:
<%= render containing_object.child_items, :partial => 'child_items/alternate_partial' %>
But the child items still render with their default partial.
Try this one:
<%= render 'child_items/alternate_partial', :collection => containing_object.child_items %>
If we create the default scaffold in Rails, both the edit.html.erb and new.html.erb render the same _form.html.erb within. Both create forms with certain similarities and differences.
Such as:
Both create <form method="post" ...>
The submit buttons have different texts <input value='Create model'
.. and <input value='New model' ..
My questions:
How does the conditional rendering work?
How to display form elements conditionally? E.g., show this
<input> only if it is called via edit.html.erb, but do not show
it if called via new.html.erb.
If the method in q.2 possible, is it the right way? We are reusing
code instead of replicating the form all over again, isn't it?
Assuming you're following RESTful conventions, the differences you see between edit and new are based on the state of the object that you pass to the form. Rails can tell the difference between a new object and one that has been persisted by using the #new_record? method.
Model.new.new_record? # => true
Model.first.new_record? # => false
In your #new controller action, you probably have something like:
#model = Model.new
In your #edit action, you probably have something like:
#model = Mode.find(params[:id])
This #model object is then passed to the form, which handles the conditional logic internally. Another difference in the form you should notice is that the #edit version has a hidden input field that tells the server to use the PUT HTTP method.
Update
It looks like Rails actually uses the persisted? method internally as opposed to new_record?. The difference is that persisted? checks whether the record has been deleted. Otherwise, they are identical (but opposite)
You can do as it:
In new.html.erb:
<%= render :partial =>'form', :locals => {:action => 'new', :f => f } %>
In edit.html.erb:
<%= render :partial =>'form', :locals => {:action => 'edit', :f => f } %>
In _form.html.erb:
if action == 'new'
or
if action == 'edit'
Also you can send other parameters by :locals such as :show_mobile => false.
I have a couple of variables that need to be called in all controllers. Displaying latest news in the layout footer.
I create them in application_controller.rb
#hq_news_item = NewsItem.where(:branch_code => "CORP").first
#branch_news_item = NewsItem.where(:branch_code => "MN").first
In my layouts/application.html.haml
= render :partial => "layouts/footer_news" , :hq_news_item => #hq_news_item, :branch_news_item => #branch_news_item
And then in my layouts/_footer_news I style them
= hq_news_item.title
= hq_news_item.author.name
... etc
Here is the thing, no matter what I do - it keeps saying that hq_news_item is undefined in partial.
All my other partials work fine. I think it has to do with the fact that it's a layout not a view. Can't find anything meaningful in the docs.
Any ideas?
Thank you.
I think you need to pass the variables as local variables to the partial:
= render :partial => "layouts/footer_news", :locals => { :hq_news_item => #hq_news_item, :branch_news_item => #branch_news_item }
Otherwise Rails won't really understand what you are passing as a variable to the partial and what you are passing as an argument to the render function.
why don't you just use the member variable #hq_news_item?
I find I use the locals method when I have a local variable inside a view that's calling another.
This code works in rails 2.3.8
<% f.fields_for :member_collection do |builder| %>
<%= render "membrer_collection_fields", :form => builder %>
<% end %>
In this case, I have a partial _member_collection_fields that has local variable form
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.
I am fairly new to rails so I apologize if I am using the wrong terminology.
I have a model Menuitem that I would like to display the contents of in a layout. How does one go about passing an instance variable into a layout?
I was looking for a layout helper of some sort but I was unable to find anything. I was also looking at defining the instance variable in the application controller to access it in the layout, would this work? If so what is the best way to go about doing it?
Thanks!
The usual way of passing variables up from the view into the parent layout is to use the content_for method. (This answer is a copy + paste from a similar answer I posted at this question)
The normal view content gets rendered automatically into the yield call without an argument in the layout. But you can also put other placeholder content in by using yield with a symbol argument, and specifying that content from the view with content_for.
app/views/layouts/posts_layout.html.erb
<html>
<head>
<title>My awesome site</title>
</head>
<body>
<div id="someMenuStructureHere">
<%= yield(:menu_items) %> <!-- display content passed from view for menu_items -->
</div>
<%= yield %> <!-- display main view content -->
</body>
</html>
app/views/posts/index.html.erb
<%= content_for :menu_items, some_helper_to_generate_menu %>
<h1>Here is you page content</h1>
Two things I would note. First, you probably don't want to be doing this query every time you render any page in your application. You definitely want to cache your MenuItems. Second, it might be helpful to put a convenience method on MenuItems class to cache this value. So, if I define a method
def MenuItem.all_for_menu
##all_for_menu ||= MenuItem.find(:all) #returns value if exists, or initializes it
end
I can call MenuItem.all_for_menu in my layout and get all the menu items. When ever you add a new one or edit one, you'd have to invalidate that.
Another caching approach would be to put the data in a partial and cache that fragment using the standard caching call:
<% cache(:controller => "menu_items",
:action => "list",
:action_suffix => "all_menu_items") do %>
<%= render :partial => "menu", :collection => MenuItem.all_for_menu %>
<% end %>
You can then expire that fragment by calling:
expire_fragment(:controller => "menu_items", :action => "list", :action_suffix => "all_menu_items")
Any instance variables defined in the controllers are auto-magically available in your views. If you are expecting an instance variable in your layout for all actions, you may want to consider defining the instance variable in a before_filter or encapsulating it in a controller method and using helper_method to make it accessible in your views.
It really depends on what you want to do with the model. I'll just guess, and you tell me what you need different to understand better how to do this. This code would work only if your MenuItem model has a field named name.
In the controller:
# Use whatever action you are currently displaying
def index
#menu_items = MenuItem.all
end
In the index.html.erb view file:
<ul id="menu">
<% #menu_items.each do |menu_item| %>
<%= h menu_item.name %>
<% end %>
</ul>
Obviously if this was a real menu, there would be hyperlinks there too :)
items_controller.rb (or something)
def show
#menu_item = MenuItem.find(params[:id])
end
In the view show.html.erb:
<%= #menu_item.name %>