How do I use 2 Layouts for Pages in Rails? - ruby-on-rails

All the pages on my site use the application layout. I want my static pages to also use the Static Pages layout. How do I use both layouts together?
Application Layout:
<html>
<head></head>
<body>
<%= yield %>
</body>
</html>
Static Pages Layout:
<div class="static">
</div>
Static page:
<p>Hello</p>
I want the page to result in:
<html>
<head></head>
<body>
<div class="static">
<p>Hello</p>
</div>
</body>
</html>

From http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts
Inside your second layout, static pages layout:
Static layout
<%= render :template => 'layouts/application' %>
Application layout
<%= yield %>
Controller where you want to use static layout
class AController
layout 'static_layout'
static_layout should be in views/layouts.
I suppose you could use layout convention too:
class StaticController
And a file app/views/layouts/static.html.erb
Or select layout in render call:
render 'something', layout: 'static'

You need to understand the used of the yield
application.html.erb
<html>
<head></head>
<body>
<% if #static %>
<div class="static">
<%= yield :static %>
</div>
<% end %>
<%= yield %>
</body>
</html>
static_page.html.erb
<% content_for :static do %>
<p>Hello</p>
<% end %>
Only thin you need to take care now in static page controller action set a #static to true

Hi In controller use layout
class StaticPagesController < .....
# you can apply only,execpt if you want
layout 'static'
def home
end
end

Related

How can I use yield :content before content_for in Rails templates?

I need to use yield/content_for in <head>, but assign the value in <body>. I have found that this works fine when the value is being assigned from within a template that is being yielded to, but not one that is being rendered. Templates that are being rendered are compiled after <head>, so my value is already set in stone. Is there a way to achieve what I am trying to do?
I tried making application.html.erb look like this:
<%= render layout: 'application_template' do %>
<!-- <body> content here -->
<% end %>
and _application_template.html.erb look like:
<!doctype>
<html>
<head>
<%= content_for :my_value %>
</head>
<body>
<%= yield %>
</body
</html>
but the same problem happens, the value is nil when _application_template.html.erb is rendered.
You should be able to do the following.
In your head check if there is any content to yield and yield it if it is.
<% if content_for?(:my_value) %>
<%= yield :my_value %>
<% else %>
And then define your content somewhere in your body.
<% content_for :my_value do %>
# your contents
<% end %>
I figured out a solution.
# layouts/application.html.erb
<% content_for(:body_content) { render partial: 'layouts/body_content' } %>
<!doctype html>
<html>
<head>
<%= yield :my_value if content_for?(:my_value) %>
</head>
<body>
<%= yield :body_content %>
</body>
</html>
and
# layouts/_body_content.html.erb
# everything that used to be in <body></body> goes here and is maintained here.
This makes sure that all of the #content_for calls I need to happen will happen before <head> is compiled in my layout. It is also easier to maintain than some of the other hacks I had thought of trying.

Yield layout in rails

I'm trying to add a layout to the application layout. I having trouble trying to figure out all the different layout solutions. First I tried just a layout inside a layout because I didn't fully grasp what partials are or if they are layouts also? Maybe I can start with that question. What is the difference between a layout and a partial.
Here is what I have right now. I'm trying to just separate out my header code which has a navigation and some other elements into a separate layout. I want this layout to be on all views. Meaning it should be a layout inside the applications layout along with other views that are been called when their controllers are called. Which is covered in my code with the <%= yield %>. That yeild works but the :header one does not.
Application Layout app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>home</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<div id="header"><%= yield(:header) %></div>
<%= yield %>
</body>
</html>
Header layout app/views/layouts/application.html.erb
<% content_for :header %>
<p>HEADER TEXT</p>
<% end %>
Why would the code above not work?
I also saw code like this that I tried but it gave me an error.
<%= render layouts/header %>
Can someone please explain all these different methods.
Thanks.
It's good practice to separate your header and footer into partials which you would live in the views/layouts folder as '_header.html.erb' and '_footer.html.erb' respectively.
You can then optionally wrap each partial with specific div's which is what you're trying to do with the header (you could do the same with the body too), and it would end up looking like this:
<!DOCTYPE html>
<html>
<head>
<title>home</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<div id="header">
<%= render 'layouts/header' %>
</div>
<%= yield %>
<div id="footer">
<%= render 'layouts/footer' %>
</div>
</body>
</html>
That will do what you want it to do.
Note that 'layouts/header' and 'layouts/footer' have '' around them.
To answer your question on what is the difference between a layout and a partial, well a layout is something that will used throughout your application, such as a consistent header or footer. A partial can be a layout, but it doesn't have to be, so you can partial specific to other views across your site.
<% content_for :header do %>
<p>HEADER TEXT</p>
<% end %>
You forgot the do
So - first - The yield syntax may or may not be correct, but I've never seen it, and I don't like it. I only ever call yield once in a file.
If you want to render a header in your application template file, that's certainly possible - my advice would be to place it in your template file directly - after all, that's what template files are for. If you want to completely encapsulate your header for some reason or another in seperate files, what you need is partials. You're going to do something like this:
<body>
<%= render :partial => "shared/header" %>
<%= yield %>
</body>
Which will render your header content, stored in /shared/_header.html.erb into the layout here.
Check out this guide here for more info

Rails sublayout

I have a Messages controller, with actions like: Sent messages,Received messages,Send new message. They are displayed to the user in a toolbar form. The thing is, when I render each view, I have to manually render the toolbar as well. So, here's how the code for the views looks like:
sent_messages.html.erb
<%= render "shared/toolbar" %>
# render stuff for sent messages
received_messages.html.erb
<%= render "shared/toolbar" %>
# render stuff for received messages
new.html.erb
<%= render "shared/toolbar" %>
# render stuff for new message
The views don't look very DRY. Is there a way I could specify that I want the toolbar to render before everything else, in the Messages controller?
app/views/layouts/application.html.erb
<html>
<head>
<title></title>
</head>
<body>
<div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
</body>
</html>
app/views/layouts/messages.html.erb
<% content_for :content do %>
<%= render "shared/toolbar" %>
<%= yield %>
<% end %>
<%= render file: "layouts/application" %>
From the documentation:
Suppose you have the following ApplicationController layout:
app/views/layouts/application.html.erb
<html>
<head>
<title><%= #page_title or "Page Title" %></title>
<%= stylesheet_link_tag "layout" %>
<style><%= yield :stylesheets %></style>
</head>
<body>
<div id="top_menu">Top menu items here</div>
<div id="menu">Menu items here</div>
<div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
</body>
</html>
On pages generated by NewsController, you want to hide the top menu and add a right menu:
app/views/layouts/news.html.erb
<% content_for :stylesheets do %>
#top_menu {display: none}
#right_menu {float: right; background-color: yellow; color: black}
<% end %>
<% content_for :content do %>
<div id="right_menu">Right menu items here</div>
<%= content_for?(:news_content) ? yield(:news_content) : yield %>
<% end %>
<%= render template: "layouts/application" %>
That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div.

In my admin section, use same layout, but add an additional css and js file?

In my admin section, I want to use the same layout as my other website, but I want to add an additional CSS file and JS file.
How can I do this?
If your controller sets a variable isadmin, check for it in the corresponding view.
If it is true, include the files.
Controller:
class BooksController < ApplicationController
def index
#isadmin = true # probably calculated somehow
end
end
View:
<head>
<% if #isadmin then %>
<script />
<link />
<% end %>
</head>
In your layout:
<%= yield :additional_scripts %>
In your views, where you want to add those scripts:
<% content_for :additional_scripts do %>
# Include scripts here
<% end %>

Extending an existing layout in Rails

I have my main application layout, but then I have an /account section of my site which has exactly the same layout as the application layout markupwise, except the /account pages have an added sidebar in the content area of the layout.
Rather than blatantly copy the application layout and create a nearly redundant "account" layout, I'd like to extend the application layout, adding the sidebar in the content area.
So I have something like this in my application layout:
<html>
<body>
<div id="content">
<%= yield %>
</div>
</body>
</html>
and I want
<html>
<body>
<div id="content">
<div id="sidebar"></div>
<%= yield %>
</div>
</body>
</html>
Is there a way to accomplish this without copying code?
You can have more than one yield in a layout, simply give the additional ones a name:
<html>
<body>
<div id="content">
<%= yield :sidebar %>
<%= yield %>
</div>
</body>
</html>
You can add HTML for that yield by using the content_for method
<% content_for :sidebar do -%>
<div id="sidebar"></div>
<% end -%>
But you'll have to add that to every view you want to have a sidebar. Instead, create views/layouts/application_with_sidebar.html.erb
<% content_for :sidebar do -%>
<div id="sidebar"></div>
<% end -%>
<%= render :file => 'layouts/application' %>
Further reading
If you'd prefer to keep the number of yields to a minimum, you can nest your layouts instead.
views/layouts/application.html.erb
<html>
<body>
<div id="content">
<%= yield(:with_sidebar) or yield %>
</div>
</body>
</html>
views/layouts/application_with_sidebar.html.erb
<% content_for :with_sidebar do -%>
<div id="sidebar"></div>
<% end -%>
<%= render :file => 'layouts/application' %>
controllers/accounts_controller.rb
class AccountsController < ApplicationController
layout 'application_with_sidebar'
...
end
Often you'll have the same situation for other parts of the site in which case it may make sense to use nested layouts.
http://guides.rubyonrails.org/v2.3.8/layouts_and_rendering.html#using-nested-layouts
If your /account route is bound to the account controller, you can always have a fullfeatured layout with conditional part like this
render :template => "/shared/sidebar" if controller.controller_name == "account"
(I have to admit that it does not please the eye though)

Resources