Using turbolinks with dynamic layouts - ruby-on-rails

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>

Related

In Ruby on Rails how to apply active state to navigation depending on the page?

So I have a nav partial that's rendering on the application that every other page is inheriting. Then in the application it's looking for body content to render. The navigation links are something like:
<ul class="ul-class">
<li class="li-class">One</li>
<li class="li-class">Two</li>
<li class="li-class">Three</li>
</ul>
EDIT:
I cannot adjust the li or a tag as they're being pulled down from our CMS. The only thing I can affect is added a class to the ul or the li.
What I'm trying to do is if a person clicks and goes to page Two that Two is then marked active with a border on the bottom. CSS like this:
.nav-active {
border-bottom: 5px solid #d9d9d9;
padding-bottom: 21px;
}
I know I have some jQuery code that will do this (minus some border issues) but I'm trying to do it the Rails way.
For reference the application file has:
<body>
<%= render partial: 'shared/nav' %>
<%= content_for?(:body) ? yield(:body) : yield %>
<%= render 'shared/footer' %>
</body>
Then in two file I have:
<%= content_for :head do %>
<%= stylesheet_pack_tag 'styles/two' %>
<% end %>
<%= content_for :body do%>
<h2>Words going here</h2>
<% end %>
Navigation is present but I'd like for Two to be active. I've tried something like this and it did nothing:
<%= if current_page?('two') then '.nav-active%>
<%= content_for :head do %>
<%= stylesheet_pack_tag 'styles/two' %>
<% end %>
<%= content_for :body do%>
<h2>Words going here</h2>
<% end %>
Is there a better/actual way to apply CSS to the current page in Rails?
EDIT: Also tried the following:
<%= 'nav-active' if current_page?('/two') %>
This kind of worked but not really. It's not really applying a class but the word nav-active.
You can define a helper like this:
module ApplicationHelper
def current_class?(test_path)
return 'nav-active' if request.path == test_path
''
end
end
And use it in your erb template:
<li class="li-class <%= current_class?('/two') %> ">Two</li>
A more rails way would be to use the link_to helper:
<%= link_to "Two", '/2', class: current_class?('/two') %>
There is also an active_link_to gem which wraps link_to helper, with the facility to inject configurable conditions when the link would be considered active.

Using meta tags gem, how to render metas in content_for?

I'm having problems with this Gem, when using content for.
In my application.html.erb, I have the following:
<head>
<% if current_page?(root_path) %>
<meta name="description" content="My page">
<% else %>
<%= yield :description_meta %>
<% end %>
</head>
And then, in my views, I do the following:
<% content_for :description_meta do %>
<% set_meta_tags :description => "My description" %>
<% end %>
It wont paint any metas, and if I render it with <%=, it will show in the body of the page as normal text.
Any ideas? Thanks
Read MetaTags Usage.
Seems like set_meta_tags just configure meta tags and display_meta_tags render them.

Rendering A Partial / Layout With Multiple Blocks

I have a very simple requirement - I have a layout comprising of a header and body. It is a sub-layout of the page, not for the page itself.
This layout is repeated throughout multiple pages, and it is possible the structure around it will change. So I want to be able to separate the content of the header and the content of the body from the structure that contains it.
My first attempt was to use render a partial as a layout that used named yields to render a header and body:
<header class="Resource-header">
<%= yield :resource_header %>
</header>
<div class="Resource-body">
<%= yield :resource_body %>
</div>
Then render it from my templates like this:
<%= render layout: 'admin/resource' do %>
<% content_for :resource_header do %>
<% end %>
<% content_for :resource_body do %>
<% end %>
<% end %>
However, this renders nothing.
I started playing with the order of things, and discovered that if the content_for blocks are declared before the call to the partial, this approach does work:
<% content_for :resource_header do %>
<% end %>
<% content_for :resource_body do %>
<% end %>
<%= render layout: 'admin/resource' do %><% end %>
However this just feels incredibly hacky. It seems that content_for is scoped globally, and there is no association between the content_for block and the partial rendering.
So what is the correct way for me to achieve this?
I just happened to have exactly same problem.
Solution is:
in your partial layout file 'admin/resource' body:
<header class="Resource-header">
<%= yield resource, :resource_header %>
</header>
<div class="Resource-body">
<%= yield resource, :resource_body %>
</div>
in your templates do:
<%= render layout: 'admin/resource' do |resource, section| %>
<% case section %>
<% when :resource_header %>
Resource header shows here.
<% when :resource_body %>
Resource body shows here.
<% end %>
<% end %>
Take a look on rails presenters https://www.ruby-toolbox.com/categories/rails_presenters
Maybe your solution is cells gem.
Eventhough the question is quite old now, I had a similar issue today. I came up with sth. like this. No gem or custom class required, just some fancy block usage ;)
<!-- app/views/layouts/fancy-blocks.html.erb -->
<%
body, footer = nil
yield(
proc {|&blk| body = capture(&blk) },
proc {|&blk| footer = capture(&blk) }
)
%>
<section class="body"><%= body %></section>
<footer><%= footer %></footer>
<!-- app/views/some-other/view.html.erb -->
<%= render 'layout/fancy-blocks' do |body, footer| %>
<% body.call do %>
BODY
<% end %>
<% footer.call do %>
FOOTER
<% 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.

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