How to conditionally add class to <body> in rails layout - ruby-on-rails

My application.html.erb file looks like this
<!DOCTYPE html>
<html lang="en">
<head>
.
.
.
</head>
<body <%= "class=landing-bg" if !#landing.nil? %>>
<%= render 'layouts/header' %>
<div class="container">
.
.
.
</div>
<%= render 'layouts/footer' %>
<% if !#landing.nil? %>
<div class="landing-gradient"></div>
<% end %>
</body>
</html>
I've left out a bunch of meta tags in the <head> and a bunch of other code in the body that's unrelated to the question.
static_pages_controller
def home
#landing = "landing"
end
I need to add a class to <body> and a div below the footer only on my home page. I feel like what I am doing is a bit hacky. And, if I created a separate layout just for the landing page it wouldn't be DRY since I would be repeating alot of code that's on this layout.
What is the best way to do this? What is the rails way?

My typical pattern is to add the controller and action into the class of the body, and then use those as selectors in my css like this:
<body class="<%= controller_name %> <%= action_name %>">
<!-- etc -->
</body>
As for your second issue of a conditionally body on only the home page, you can simply add that into the body in the main layout and display it only on the landing page:
<body class="<%= controller_name %> <%= action_name %>">
<!-- etc -->
<div class="landing-gradient"></div>
</body>
# application.css
.landing-gradient {
display: none; // Hide the gradient
}
.landing .landing-gradient {
display: inline; // Show gradient b/c of more specific selector
}
This is my personal preference since you're leaving display of individual elements to the css / presentation layer instead of within your controllers.
Finally, you could also use a content_for around the footer so that you can customize the footer across individual pages. That's a bit heavy handed for your example though, so I'd only use that if you were customizing the footer a lot.

In my case I want to have a different class per layout (at least potentially), so in app/views/layouts/application.html.erb I do this:
<body class="<%= yield(:body_class) %>">
and then in my layout file I do this:
<% content_for(:body_class) { 'my-class' } %>

You can also manage it without creating an instance variable and verifying it in your view but using the params[:action] and params[:controller]:
<body class="<% 'class_value' if params[:controller] == 'static_pages' && params[:action] == 'home' %>"
You can just validate for the action (method) if you don't have more than one home method.

Related

Rails 5 + Boostrap 3 - Use full page width for sections on landing page

Technology: Rails 5.2.2, Bootstrap 3.3.7
In my application.html.erb layout, I have the following code:
<body>
<div class="site-body">
<div class="container">
<%= yield %>
</div>
</div>
</body>
The content in my views has a fixed width due to being wrapped in Bootstrap's container class. I would like some colored sections on one of my pages to take up the full page width, but this is proving to be difficult because all of my view content is wrapped in the container class.
How can I customize just one of my views to have colored sections that will take up the full page width?
You can create a new layout:
layouts/full-width.html.erb
<body>
<div class="site-body">
<div class="container-fluid">
<%= yield %>
</div>
</div>
</body>
And you can use it only on some pages:
class SomeController < ApplicationController
layout 'full-width', only: [:full_with_page]
def full_with_page
....
end
end

What does “yield” do?

What does yield do in ruby on rails?
<body data-spy="scroll" data-target=".sidebar">
<!-- Your timezone is <%= Time.zone %> -->
<!-- <%= "Ruby Version is #{RUBY_VERSION}" if Rails.env =~ /test|development/ %> -->
<%= render partial:'shared/account_status' %>
<%= render partial:"shared/session_timeout" %>
<div class="container">
<%= render partial:"shared/branding" %>
<%= render partial:"shared/nav", locals:{icons:icons, actionable_urls:actionable_urls, top_level_items:MenuItem.top_level_items_with_access_rights_for_user(current_user).sort{|a, b| a.sequence <=> b.sequence}, current_item:current_navigation_item} %>
<div style="clear:both"></div>
<div id="content">
<%= render partial:"shared/flash", object:flash %>
<%= yield %>
</div>
</div>
<%= render partial:"shared/ldap_user_menu" if signed_in_as_ldap_user? %>
</body>
It tells Rails to put your view content to this block (which is called by yield) at that place in the layout file.
Checkout the rails guide to learn more about the ActionView.
https://guides.rubyonrails.org/action_view_overview.html
As pointed out by #Aleksei Matiushkin, yield is pure ruby, so you should also learn more about that in your own time.
Here's a (my) visual presentation to explain what happened on that line:
view.html.erb:
<p>Hello there!</p>
<p>I'm a content from view</p>
layout.html.erb:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>
Now the results will be like this:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hello there!</p>
<p>I'm a content from view</p>
</body>
</html>
Your question is not specific enough. In Rails, as with ordinary Ruby, yield used within a method definition represents the block that is passed to a method.
However, judging from the code block that you gave, you seem to particularly want to ask about yield used in layouts of a view in Rails. In such case, it represents the main content described in the view file that is to be rendered in the context. For example, when the controller is Foo, and the action is bar, then, yield used within a layout to be used in such context will be replaced by the content of /app/views/foo/bar.html (or whatever corresponding view file in other format).
It must be simple in your concept: yield is where put content block (html) from action view to layout template
example: action index render index.html, the result will be put/filled in the yield
I haven't seen render mentioned in the other answers, so I'd like to add to #vipibano's answer, that if you want to understand how this yield business works in Rails (and more specifically in the application.html.erb), you'll benefit from:
understanding how blocks work in Ruby (as mentioned by others)
understanding what Rails' render method does
The render method is called at the end of a controller action and orchestrates what block is passed to the method that is actually rendering the application.html.erb.
I've tried to visualize this a bit and there a few examples if you want to dig deeper in the according post:
https://richstone.io/debunk/

Apitome sidebar disappears when included in a layout

I am using the rspec_api_documentation and apitome gems in a version 5.2 ruby on rails app.
This produces excellent documentation, and has a sidebar (div#sidebar) to allow quick access to the correct part of the documentation. When I choose the
config.layout = "layouts/application.html.erb"
option in the apitome.rb initializer, the documentation is rendered, but the sidebar has disappeared. Looking at the page source, the code for the sidebar is not being rendered, i.e. it is not a css problem, the html is not being put into the layout. To make sure it was not something unusual in my application.html.erb file, I simplified it to this
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>
This sidebar is very useful, so how do I render it in a layout?
Based on the response to this issue, I was able to solve this.
I created a new layout at app/views/layouts.apidocs.html.erb which rendered apitome/docs/navigation. A simple example would be as follows
# app/views/layouts/apidocs.html.erb
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4">
<div id="sidebar" class="sidebar hidden-print" role="complementary">
<%= render 'apitome/docs/navigation' %>
</div>
</div>
<div class="col-md-8" role="main">
<div class="docs-section">
<%= yield %>
</div>
</div>
</div>
</div>
</body>
</html>
I then configured this layout in the
apitome initialiser.
# config/initializers/apitome
Apitome.setup do |config|
...
config.layout = "layouts/apidocs.html.erb"
end
After some css tinkering, it all looked good.

Passing objects to be used by partials inside a layout

I have a page layout with a sidebar and a main content. Both are dynamic according to the user profile.
<html>
<body>
<%- partial('partials/sidebar')%>
<html with <% ejs code %> for body>
</body>
</html>
Sails view should be:
res.view( {mydata} );
How can I pass data to the partials so it can be dynamically rendered instead of static?
Example:
controller:
res.view( {mainContent:{mainContentJson}, sidebar:{sidebarJson} );
layout.ejs:
<html>
<body>
<%- partial('partials/sidebar', sidebar)%>
<html with ejs code for body>
</body>
</html>
Yes. Partials accept arguments to be passed, and they are rendered before being inserted into the calling layout.
controller:
res.view( {
layout:<yourCustomLayoutIfNeeded>,
mainContent: <mainContentObject>,
partialContent: <partialContentObject>
});
yourCustomLayoutIfNeeded:
<%- partial(<pathToPartial>, partialContent) %>
<mainContent>

How to prevent partials in main layout from being re-rendered/updated on every page request?

Having looked around for hours, reading blogs and many other SO questions with no success, I finally got to ask for help.
I believe I still lack some understanding about the Rails layout/render/yield mechanisms.
Therefore this maybe a stupid question. I beg your pardon.
Is it possible to prevent partials from being rendered on every page request? I mean, every time I click a link on my app the layouts and partials are reloaded.
My goal is to have a somehow static side bar, populate it once by rendering the partial "the first time application.html.erb is loaded", and then update it using ajax only.
Here is my app layout:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= content_for?(:title) ? yield(:title) : "GEN" %></title>
<meta name="description" content="<%= content_for?(:description) ? yield(:description) : "GEN" %>">
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body class="darkbody">
<header class="navbar navbar-fixed-top navbar-inverse">
<nav class="navbar-inner">
<div class="container-fluid">
<%= render 'layouts/navigation' %>
</div>
</nav>
</header>
<main role="main">
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<%= render partial: 'charts/dashboard', layout: false %>
</div>
<div class="span9">
<div class="well well-small">
<%= render 'layouts/messages' %>
<%= yield %>
</div>
</div>
</div>
</div>
</main>
</body>
</html>
The "dashboard" partial should load once and never be rendered again.
In other words, navigating the app would change/refresh content in the yield section but not in the main layout "partials".
The motivation? The dashboard uses a helper method to show a value that is computed and never updated throughout the user session. If I get many reloads this helper runs on every user click and stresses the app server even if returning the same result, what happens indeed.
Is this too crazy or stupid?
Best regards,
AD
Actually, it will render HTML from this template on each request, but if data which is displayed in template doesn't need to be actual all the time, you can try action or fragment caching, like this:
<div class="span3">
<% cache 'dashboard' do %>
<%= render partial: 'charts/dashboard', layout: false %>
<% end %>
</div>
You can read more details in rails guides (http://guides.rubyonrails.org/caching_with_rails.html#fragment-caching)

Resources