Loading a nav bar when in a specific controller rails - ruby-on-rails

I am having a bit of trouble in rails.
What I want to do is to display some extra links in the application layout when a specific controller is in use. How do I do this?
I am loading the pages dynamically using jquery and I tried using <%if controller_name == "foo"%> then do some magic, without any success.
If somebody could point me in the right direction or even a jquery-rails rendering tutorial that would be great.
Thanks.

Check out the content_for magic provided by Rails. It allows you to specify something like this in your application layout:
<%= yield :header %>
And then in your individual templates do something like this:
<% content_for :header do %>
Content I want put in the header
<% end %>
Which basically results in the content inside the content_for block being captured and rendered at the point of the yield statement. So, you can specify that in the templates for your controller.

Related

DRY layouts and views

I have a user profile page with a sidebar. I need to create more pages within the profile. For example, edit password, edit profile information, statistics, purchase history list, etc. I'm not sure how to proceed while keeping things DRY. I'm trying to get everything to be the exact same except the main content. While going through some tutorials I came across yield but it was mostly used in the application.html.erb to render navigation, footer, etc. I don't understand how to use it for "sub-views".
The way I'm doing it right now seems wrong.
Routes:
as :user do
# Routes to change password of signed in user
get 'user/password' => 'users/registrations#edit_password', as: 'edit_password'
# Routes to change user profile information of signed in users
get 'user/profile' => 'users/registrations#edit_profile', as: 'user_profile'
end
Views:
views\users\show.html.erb:
views\users\registrations\edit_profile.html.erb:
views\users\registrations\edit_password.html.erb:
All contain this 1 line
<%= render 'users/shared/profile' %>
views\users\shared\profile:
<%= render 'users/profile/sidebar' %>
<!-- Display Profile or Password based on route -->
<% if current_page?(user_path current_user) %>
<!-- User Profile -->
<%=render 'users/profile/adminPanels' %>
<% elsif current_page?(edit_password_path) %>
<!-- Password Reset -->
<%=render 'passwordForm' %>
<% else %>
<!-- Profile Edit -->
<%= render 'users/registrations/profileForm' %>
<% end %>
Basically what I wanted to do is keep all the surrounding layout but change the rendered content. Now that I need to add more, extending this if statement really seems like the wrong way to go.
Yeah this is definitely not the way to go, but it's good that you recognize that so no worries. As you guessed, the way to do this involves using layouts and yield. You can read about yield in this Rails guide.
While you can have a layouts like application.rb that your entire Rails app uses by default, you can also define layouts nested within this layout. This is described in the same Rails guide as above, towards the bottom.
This way, the stuff that is the same for your entire application is defined in the application layout, the stuff that is same for everything that is a user profile is defined in the users layout, and the stuff that is specific to each view is defined in there.
Side note: as the users layout is in the layouts folder, I acted as though you moved the _sidebar partial there as well since it is really a partial that belongs to the layout and should be near it.
views/layouts/users.html.erb
<%= render '_sidebar' %>
<%= yield :users_content %>
<%= render template: 'layouts/application' %>
views/users/show.html.erb
<% content_for :users_content do %>
put the view code specific to users/show here
<% end %>
views/users/registrations/edit.html.erb
<% content_for :users_content do %>
put the view code specific to editing a user registration here
<% end %>
etc.
The only thing that you may have issue with is that Rails is using the name of the controller to match the nested users layout and that may break for the registrations stuff if that's a different controller. You can fix that by explicitly calling render template: 'layouts/users' inside of those controller actions.
From what code snippet you've provided, the DRYest way would be to move
<%=render 'users/profile/adminPanels' %>
directly to the show.html.erb page after rendering shared/profile. Same thing for other views.

Rails what is the difference between content_for and yield?

For example: content_for(:stuff) vs yield :stuff
I know they are implemented slightly differently, but is there any real functionality difference?
Is there a generally accepted best practice?
yield is how you specify where your content areas is going to go within a layout. You might have something like this:
<div>
<h1> This is the wrapper!</h1>
<%= yield :my_content %>
</div>
content_for is how you specify which content is going to be rendered into which content area. You might have something like this:
<% content_for :my_content do %>
This is the content.
<% end %>
The result would be
<div>
<h1> This is the wrapper!</h1>
This is the content.
</div>
They are opposite ends of the rendering process, with yield specifying where content goes, and content_for specifying what the actual content is.
Is there a generally accepted best practice?
The best practice is to use yield in your layouts, and content_for in your views. There is a special second use for content_for, where you give it no block and it returns the previously rendered content. This is primarily for use in helper methods where yield cannot work. Within your views, the best practice is to stick to yield :my_content to recall the content, and content_for :my_content do...end to render the content.
yield:
yield identifies a section where content from the view should be
inserted
content_for:
The content_for method allows you to insert content into a named yield
block in your layout. For example, this view would work with the
layout that you just saw:
yield :stuff will grab the contents that are pushed by content_for(:stuff)
So, using yield you can define the sections in your view/layouts and you use content_for for adding contents to those sections. Any unnamed yield will grab all other contents.
You can learn more about it reading the tutorial.
Calling #content_for stores a block of markup in an identifier for later use. In order to access this stored content in other templates, helper modules or the layout, you would pass the identifier as an argument to content_for.
yield can still be used to retrieve the stored content, but calling yield doesn't work in helper modules, while content_for does....more: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for
From the Ruby on Rails API:
Calling content_for stores a block of markup in an identifier for later use. In order to access this stored content in other templates, helper modules or the layout, you would pass the identifier as an argument to content_for.
Note: yield can still be used to retrieve the stored content, but calling yield doesn't work in helper modules, while content_for does.
You can then use content_for :not_authorized anywhere in your templates.
<%= content_for :not_authorized if current_user.nil? %>
This is equivalent to:
<%= yield :not_authorized if current_user.nil? %>
content_for, however, can also be used in helper modules.
https://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for

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

How can I use content_for to put something in :yield

I am in ruby 1.9.2, rails3.
So My website has some structures,
and I want to put menu in a middle of my webpage.
I am doing something like (within application.html.erb file)
blahblahblah
<div id="menu">
<%= yield :menu %>
<div>
blahblhablah
I have a file menu.html.erb which has menu structure for the site.
What can I do if I want to use a file within ./layout folder to be used to be part of that yield :menu? I was wondering, if I have to use content_for for every controller, and within every functions...
Btw, menu.html.erb will be different for each controller, so thats why I am yielding it.
In conclusion, I just want to include one common shared menu.html.erb pretty much everywhere.
You could do something like this in your views:
<% content_for(:menu) do %>
<%= render :partial => "/layouts/user_menu.html.erb" %>
<% end %>
You could try to combine this with controller.controller_name (not sure this works for Rails3) and load a different menu for each controller automatically.
You might consider watching the railscast on layouts, it's concise and helpful.
Numbers 7 and 8.
http://railscasts.com/episodes?search=layout

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.

Resources