Instance variables in layout - ruby-on-rails

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 %>

Related

How do I cache dynamic header/footer in a rails app?

Say I have a simple app with the following layout
<html>
<head>...</head>
<body>
<%= render :partial => 'layouts/header' %>
<%= yield %>
<%= render :partial => 'layouts/footer' %>
</body>
</html>
My header and footer partials are pretty simple but they require dynamic data which takes a a few ms to fetch from the db. Right now, I get the header data with a before_action: method in my application controller since it's need on a all pages (except Ajax calls):
before_action :prepare_header
private
def prepare_header
#news = ...
end
How can I cache either the prepare_header action or header.erb.html.
I am using Rails 4.0.0
Thank you.
You can do it with fragment caching in the view.
Your #news variable is calculated based on some data, for example params[:a], params[:b] and params[:c]. Based on that you can wrap your header (and then partial) with a cache block
<% cache([params[:a], params[:b], params[:c], 'header']) do %>
you html code here
<% end %>
with that you can have a cached version of your partial depending on the desired control variables. Check here for more on view caching.
A good practice is to add more control to the cache_key in order to invalidate it on demand, like:
cache([cache_key_1, cache_key_2, version_number])
when something changes you bump the version_number and invalidate accordingly!
And of course an expires_at parameter.
UPDATE
For action caching you can try this gem. And pass your desired params in an
if: Proc { ... }
parameter

In Rails 3: Can a template image be associated with the view that's being displayed?

Sorry, I'm very new at Rails so I'll try to be as specific as I can be.
In my template I have a large "header" style image. I would like to swap that image out for another image that is associated with the view that is being displayed. Maybe this can be done using a helper? I don't even know where to begin with this.
I know I could make a bunch of template pages and load each of them with the desired view, but I think thats a lot of repeated lines of code to load when I simply want to swap one image. Does anyone have an idea?
There are a few options depending on your needs. The first thing that comes to my head is to create a couple of helper methods. One to call from your custom views and one to call from your global layout.
For example, create a file app/helpers/layout_helper.rb
module LayoutHelper
def header_image_tag
#header_image ||= 'whatever-my-default-image-is.png'
image_tag #header_image
end
def header_image(image_path)
#header_image = image_path
end
end
In your layout file... e.g app/views/application.html.erb. Something like:
<div id='banner'>
<%= header_image_tag %>
</div>
In your individual view files that you don't want the default image:
<% header_image 'other-image.png' %>
That should get you started. You may want to allow the header_image_tag to take some options to pass onto the image_tag, or set some defaults that can be overridden.
The other thing you can take advantage of is content_for and yield blocks.
Example... in your custom views, you could put something like this at the top of your view:
<% content_for :banner do %>
<%= image_tag 'blah.png' %>
<% end %>
And in your layout
<div id='banner'>
<%= yield :banner || image_tag 'my-default.png' %>
</div>

Dynamic Sidebar with Rails layout

For instance, i want to have my sidebar to have several dynamic content. Using other method will lead me to put query codes into View, which is not a good idea at all. I would like to keep any query in my Controller.
Currently as i know there are several ff. method:
Render a shared partial -> No where to put the query
render :partial => "shared/sidebar"
Content For -> Additional details in the comment
<%= yield :sidebar %>
<% content_for :sidebar do %>
Netscape<br>
Lycos<br>
Wal Mart<br>
<% end %>
3rd is write it directly to the layout file.
So how should I make this work?
IF you want this in every view, you can place the method that populates the necessary data in application_controller and use a before_filter to trigger it.
before_filter :load_sidebar
def load_sidebar
#data = Thingy.find(:all)
end
Then your partial or content_for element checks for #data and processes.
If you wanted to reduce the amount of code in your application_controller.rb, you may want to consider using the Cells gem.
This would allow you to define your 'query' in a separate cell controller, and you would render the content for it using something like render_cell :sidebar, :myquery inside your view.

collection counter in rails partials

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).

Rendering view from different controller

Say I have controllers Apples and Bees, and new actions in both. In Bee's new action, I set some variables for display in 'bees/new'. I happen to also want to render this same template from Apples's new method. What's the correct way of setting up the variables in this case? I take it copying over the assignments from Bees isn't the right way of going about it.
If you're going to be displaying it in more than one place, your best bet is to use a partial. You can move all relevant view code into a partial (let's call it "apples_new", which means you'd save it as /app/views/apples/_apples_new.html.erb).
Then, in your regular apples/new.html.erb view you can just call that partial:
<!-- /app/views/apples/new.html.erb -->
<h1>Apples New</h1>
<%= render :partial => "apples_new" %>
And in your Bees "new" view, you can do:
<!-- /app/views/bees/new.html.erb -->
<h1>Bees New</h1>
<% if #bees.has_apples? $>
<%= render :partial => "apples/apples_new" %>
<% end %>
Note that in my example above, I'm adding some logic. I'm assuming you only want to call the same form in certain scenarios, so I added the "has_apples?" method to demonstrate the logic.
Quick note: you can also compress that logic into one line:
"apples/apples_new" if #bees.has_apples? %>

Resources