Extending an existing layout in Rails - ruby-on-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)

Related

How do I use 2 Layouts for Pages in 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

Using turbolinks with dynamic layouts

I am rendering different layouts for each controller like so:
# layouts/student.html.erb:
<% content_for :stylesheets do %>
#menu_container {background-color: #3085BD;}
<% end %>
<% content_for :side_menu do %>
<h4 style="text-align: center;"> STUDENTS</h4>
<% end %>
<%= render :template => 'layouts/application' %>
Using turbolinks, I've found that the layout is not changing when I navigate between different parts of the site. A page refresh fixes everything.
Is there a way to tell turbolinks to grab the layout along with the page content?
I finally got this to work by placing the following lines within the body tag of application.html.erb:
<%= content_for?(:side_menu) ? yield(:side_menu) : yield %>
<style type="text/css">
<%= content_for?(:stylesheets) ? yield(:stylesheets) : yield %>
</style>

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: Correct way of making exception of "Yield" by using "Content_for"?

I have a Rails app, in which I fit my entire website into a 980width container with the following code in my 'application.html.erb' file:
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= yield %>
</div>
</div>
Now, I want to make 2 file exceptions for fitting the content within the container. I want the index page and another page to expand across the entire page, so I need to get those two pages outside of the common 'yield' set above.
I tried doing so with:
<% if current_page?(root_url) %>
<%= yield :index %>
<% elsif current_page?(:controller => "tracks", :action => "show", :id => params[:id])) %>
<%= yield :show_track %>
<% else %>
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= yield %>
</div>
</div>
and
<% content_for :show_track do %>
blah blah blah
<% do %>
THE PROBLEM: The show_track page doesn't load. I did some searching, and it seems like the above method should work, but it's not, and I was wondering if I needed to do something else as the "show" page was made through scaffoldaing(RESTful).
Is there a better way to take out the 2 pages from the container than using if..else conditions?
Is there a better way to take out the 2 pages from the container than using if..else conditions?
This is subjective, but I would use nested layouts, then define the layouts for each page type in the controller.
First your basic top level layout. I'm calling it "application", the default, but you could call it whatever. Note how if there's content_for? :application it will yield it, otherwise it will just yield. This is key to the setup. All nested layouts should follow a similar pattern; in this way they can render further nested child layouts, or be used as layouts themselves.
<!-- layouts/application.html.erb -->
<html>
<body>
<%= content_for?(:application) ? yield(:application) : yield %>
</body>
</html>
Then for the container, you'd define layout which can be nested inside "application", this one setting up your container HTML and rendering content inside.
<!-- layouts/container.html.erb -->
<%= content_for :application do %>
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= content_for?(:container) ? yield(:container) : yield %>
</div>
</div>
<% end %>
<%= render :file => "layouts/application" %>
Then just move your conditional logic to the controller, like:
layout :determine_layout
protected
function determine_layout
# pseudocode here, you get it
(index or tracks) ? "application" : "container"
end
You could stop there. Continue to see how you might further nest layouts.
However you could go further, and use the nested layout setup to nest arbitrary numbers of different layouts. Say, for example, that tracks had another content block you needed to fill. You could define another nested layout, like:
<!-- layouts/tracks.html.erb -->
<%= content_for :some_other_block do %>
// stuff that should be in some other block
<% end %>
<%= content_for :container do %>
// stuff that should be in the container
<% end %>
<%= render :file => "layouts/container" %>
Then in your controller, you'd change your determine_layout to set the "tracks" layout for tracks, e.g.:
function determine_layout
# pseudocode here, you get it
if index
"application"
elsif tracks
"tracks"
else
"container"
end
end

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.

Resources