Dynamic body id - ruby-on-rails

How can I set ID for body tag in Rails?
<body id="login">

I don't find any of the solutions particularly elegant or flexible. Add a body_class method to application_helper.rb:
def body_class
[controller_name, action_name].join('-')
end
..in layout:
<body class="<%= body_class %>">
#e.g output: <body class="articles-show">
You can then refine the above helper to dynamically inject other identifiers such as modules.

You should use content_for tag here.
In your application layout:
<html>
...
<%= yield :body || "<body>" %>
...
</body>
</html>
And then from any view you can call this:
<% content_for :body do %>
<body id='login'>
<% end %>
That's it :)

You can set a variable #body_id in your action inside a controller or in your view and you can use it in your layout.
So for example if you have an action index you can add this code in your controller:
def index
#body_id = "myid"
end
or in your view index.html.erb as:
<% #body_id = "myid" %>
Then in your layout, I suppose application.html.erb you can add:
<body<%= " id=#{#body_id}" if #body_id %>> # no quotes around #{} are needed ;)

Be careful! The following will not work in rails 3.1
<%= yield :body || "<body>" %>
It might look like it works but if you view source you'll notice your body tag is missing unless you override it. Most browsers will work around a missing body tag.
The correct way to do is as follows
<%= content_for?(:body) ? yield(:body) : raw("<body>") %>

Be careful, you shouldn't be setting view data in your controllers, as some of the above posts have recommended. It breaks the MVC pattern, and creates needless objects that won't be used anywhere but HTML versions of your pages.
Another thing to be aware of is increased risk when you assign your body tag inside a logic block - why risk the page not outputting your body tag (breaking the page) if you don't have to? The example above:
<%= yield :body || "<body>" %>
Someone may not entirely understand your meaning in a child partial, and you're just increasing the duplication of the body tag, meaning you have more places to look if somebody doesn't terminate the tag, etc. There's no need for this risk. A more sensible solution is to yield just the class. HAML will allow you to supply optional class elements, eg:
%body{ id: yield(:body_id), class: yield(:body_class) }
And then set those values in your view:
- content_for(:body_class) { 'dashboard business' }
I'd love a solution that allows for multiple partials to add classes to the one yield block, but haven't found a non-hacky way to do this yet.

<body id="<%= #bodyid %>">
and in your controller, you can set this
#bodyid = "login"

U didnt really explain what exactly u need to do then, my first guess is:
<body id="<%= #body_id %>">

Related

Difference between using provide() and assigning a variable for page titles in Rails?

Could someone explain why it is preferred when embedding ruby for things like page titles to use
<% provide(:title, 'Help') %>
and then using
<%= yield :title %>
rather than jus using a variable:
<% title = 'Help' %>
<%= title %>
I'm assuming its to do with the fact that you can yield before you have called provide() but if that is the case why is it not possible to call the variable before defining it?
Thanks :)
If you want to simply render a variable in the view, the second method will do.
However, provide and yield offer a various ways to build the rendered content. For, example, you pass instance variable like #posts which you may already assign a variable after a complicated algorithm, which you will not do in a view template.
http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
provide (or content_for) are used to pass some elements from the view to the layout, so if you have layout:
<html>
<head>
<title><%= yield :title %></title>
</head>
<body>
<%= yield %>
</body>
</html>
Than in all the views, you can set the content for title with provide or content for. Local variables cannot do this, as they only live in a given view.
provide stores a block of markup in an identifier for later use. In this case, 'Help' in the symbol :title. The provide is enclosed in <% %> to indicate it is executing this code and not printing out in the view.
yield in this case just spits that block back out. The yield is enclosed in <%= %> to indicate it is being printed out into the view.
Think of it as setting a variable and printing out a variable.
See: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide for more information. Note that provide is really a wrapper for content_for so that's where the good stuff is in that link.

Different background color for different pages in rails

So I'm using an application.html.erb file which is basically the layout for every page in my website. But I want the homepage to have a white background and the rest of the pages to have a different background. The problem is, if I wrap the entire homepage file in a div, it only wraps the "yield" place and so it shows as a box with a white background within a larger box with a gray background. So how can I change the entire background of the homepage and leave the rest?
Thanks!
Expanding on the answer provided by #muffinista:
You can use an instance variable set in the controller to determine when to put the 'homepage' class on the body tag. So:
def index
#home_page = true
# existing code
end
and in the layout:
<body class="<%= #home_page ? 'homepage' : ''%>">
<%= yield %>
</body>
Consider putting a special class on the body tag (or perhaps your main wrapper) of your homepage, then do it in CSS. So you can have on your homepage:
<body class="homepage">
<p>hi!</p>
</body>
Then on your other pages:
<body>
<p>i am not a homepage!</p>
</body>
And in your CSS:
body {
// some general css
}
body.homepage {
// some css for homepage elements
background-color: #000000;
}
UPDATE: you can use a helper like this to make life easier:
def body_class
#body_class || ''
end
Then in your homepage views, put something like this at the top:
<% #body_class = "homepage" %>
Obviously this depends on the specifics of your app, but it works fine for me.
Just to add to the accepted answer, if you need to set different backgrounds for multiple pages, it would make sense and add readability to move the code for finding an appropriate background into a partial:
<body class= <%=render partial: "layouts/background" %> >
_background.html.erb:
<%- if #main_page_background OR #stylish_background %>
"main_page_background"
<%- elsif #dark_background %>
"dark_page_background"
<%- else %>
""
<% end %>

Confused on Advanced Rails Layout Nesting

I currently have a few layouts that my application uses (the ... is identical between all layouts):
# application.html.erb
...
<div id="section"><div class="wrapper"><%= yield %></div></div>
...
# wide.html.erb
...
<div id="section" class="wide"><div class="container-12"><%= yield %></div></div>
...
# thin.html.erb
...
<div id="section" class="thin"><div class="container-06"><%= yield %></div></div>
...
I am looking to re-factor the code to reduce duplication, however the strange placement of the class variables (outside the yield) makes it difficult. Should I be using variables for declaring the class values within my layout (and move to a single layout) or should I be adding container.html.erb layout that contains the duplicate ... then render the three other layouts from it (or does another third option exist that I am missing)? I'm looking for the "Rails" way to do it if possible. Thanks!
module ApplicationHelper
def section_helper(outer_class=nil,inner_class)
content_tag(:div, :class=> outer_class, :id => :section) do
content_tag(:div, :class=> inner_class) do
yield
end
end
end
end
and in the layouts:
<%= section_helper("wrapper") { yield } %>
<%= section_helper("wide","container-12") { yield } %>
<%= section_helper("thin","container-06") { yield } %>
This works nicely for the first case where there is no "outer" class, since :class => nil renders nothing. But you could also pass in a hash if having an optional first argument is confusing.
We've done something like this using an instance variable like #sectionclass. Then we set it to a default in the ApplicationController and flip it to page specific values in other controllers. Then your page would be something like this:
<div id="section" class="<%= #sectionclass %>"><div class="container-12"><%= yield %></div></div>
The nice part of the instance variable is that the nil case fails silently with an empty string. (Albeit some may say this is 'bad').

Can I get the name of the current controller in the view?

Is there a way to figure out what the current controller is from within the view?
For an example of why I would want to know this: if several controllers share the same layout, I may have a part in the layout ERB file where I want to highlight the current page's menu item based on the controller.
Maybe that is a bad approach. If so, what is the more preferred way to do this?
I'm interested to know about getting the name of the current controller either way, though.
(Obviously I could put something like #controller_name = 'users' in each controller; but that seems like the sort of thing Rails would've already done behind the scenes. So I'm just wondering if there's a built-in way.)
controller_name holds the name of the controller used to serve the current view.
Use controller.controller_name
In the Rails Guides, it says:
The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values
ActionController Parameters
So let's say you have a CSS class active , that should be inserted in any link whose page is currently open (maybe so that you can style differently) . If you have a static_pages controller with an about action, you can then highlight the link like so in your view:
<li>
<a class='button <% if controller.controller_name == "static_pages" && controller.action_name == "about" %>active<%end%>' href="/about">
About Us
</a>
</li>
#to get controller name:
<%= controller.controller_name %>
#=> 'users'
#to get action name, it is the method:
<%= controller.action_name %>
#=> 'show'
#to get id information:
<%= ActionController::Routing::Routes.recognize_path(request.url)[:id] %>
#=> '23'
# or display nicely
<%= debug Rails.application.routes.recognize_path(request.url) %>
reference
controller_path holds the path of the controller used to serve the current view. (ie: admin/settings).
and
controller_name holds the name of the controller used to serve the current view. (ie: settings).
If you want to use all stylesheet in your app just adds this line in application.html.erb. Insert it inside <head> tag
<%= stylesheet_link_tag controller.controller_name , media: 'all', 'data-turbolinks-track': 'reload' %>
Also, to specify the same class CSS on a different controller
Add this line in the body of application.html.erb
<body class="<%= controller.controller_name %>-<%= controller.action_name %>">
So, now for example I would like to change the p tag in 'home' controller and 'index' action.
Inside index.scss file adds.
.nameOfController-nameOfAction <tag> { }
.home-index p {
color:red !important;
}

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

Resources