Dynamic Sidebar with Rails layout - ruby-on-rails

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.

Related

Adding extra layout to specific controllers and actions in rails 3?

In my application.html.erb I have a header partial. which I rendered with the render tag
<%= render 'layouts/header' %>
So this header applies to all the controller and all the actions.
I have a dropdown partial which i want to show, in addition to the header partial, in all the controllers except one one controller. I want something like
<%= render 'layouts/dropdown' except_controller_anycontroller %>
When I put
render :partial => 'layouts/dropdown'
It just renders the dropdown partial and all other layouts are lost (like the footer,header,body). I want to add the extra dropdown partial only to certain actions and controllers.
How can I achieve that in Rails 3.2.13?
Replace your render with this:
<%= render 'layouts/dropdown' unless #disable_dropdown %>
Then you can simply set disable_dropdown to true in any controller you like:
def test_method
#disable_dropdown = true
end
call this method in your controller filter, in which you dont want to show this:
write this on top of your controller above your first method:
before_filter :test_method
it will automatically be called when your request comes to this controller.
Hope it will help. Thanks
I would suggest something like:
<%= render 'layouts/dropdown' unless params[:controller] == "controller_to_avoid" %>

Design pattern for side bar with dynamic content in Rails

I would like to have a right side bar with content changes for each page.
For example, when I am in Friends page, the side bar should display New Friends.
When I am in Account page, the side bar should display Recent Activities.
How should I go about this to respect Rails design patterns? I heard about Cells gem, but I am not sure if I use it.
here is one way, in your layout add a named yield section
<div id="main-content">
<%= yield %>
</div>
<div id="side-content">
<%= yield(:side_bar) %>
</div>
Then in your views put content into the named yield using content_for
# friends view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/new_friends" %>
<% end %>
# account view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/recent_activity" %>
<% end %>
this requires you to be explicit about what content appears in the side bar for every view,
maybe having it do it dynamically is better? probably depends on the specific situation and your preference
see also - http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
I came by this question in a moment of a big design change in our views. After thinking about the sidebar problem a bit, I realized that there's no best solution (as always). There are better solutions for each case.
I'll compare 3 solutions here:
using content_for(:sidebar) and yield(:sidebar)
using the partials approach
using the Cells gem
1. Using content_for(:sidebar) and yield(:sidebar)
This is good for cases when each link (each controller action) you access renders a different sidebar. In this case, each view you access will have the content_for(:sidebar) part.
If your sidebar view depends only on the state of some variable in the session, for example, the sidebar should not be rendered for every link you access.
Then you should use a good caching system like turbolinks, to avoid rendering many times the same thing, or use something like the Cells gem with a javascript to render only the main part of the layout.
2. Using partials
Using partials is always good to eliminate duplication. If your sidebar is very simple and is changed for every controller, you can render it as a partial. But if you're rendering different partials in the same controller, according to some state, it may be an indication that you have business logic in your views, which should be avoided.
3. Using the Cells gem
Very good design pattern when you have to render your sidebar from a different controller than the rest of the view each time.
It takes a lot of business logic out of the view, which sure is a good practice.
Here you have an action calling a view. Inside that view, there is a statement render_cell(:sidebar, params). This statement will do some business logic and render the view of the sidebar. It's as if the first action called other controller actions to render specific parts of your view (called cells)
If you make changes to the sidebar only, you may have to create other simple action, so that a javascript will request it. This action will call the render_cell(:sidebar) method again to respond with the view.
It's a very interesting approach.
Other ideas:
Your sidebar could be rendered only with javascript from the same
action.
Your sidebar could be rendered by an angular controller, and rails sends jsons with the sidebar objects. (look for "One page apps")
try something like this
<div class="sidebar">
<% if current_page?(controller => "friends", :action => "show") %>
<h4>New Friends</h4>
<% elseif current_page?(controller => "accounts", :action => "show") %>
<h4>Recent Activities</h4>
<% end %>
</div>
If the above code fits what you are trying to do(looks like this is what you want to achieve), then stick with it, else it may be beneficial to go with some gems. Also checkout helper page on how to use current_page? method. Hope it helps

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>

Instance variables in layout

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

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