Rails sublayout - ruby-on-rails

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.

Related

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.

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>

Set a browser and page title for Devise pages?

I have methods that set a title for the browser and for the page itself. On my Devise pages I would like to set these two methods but am not sure how to.
My Code:
helpers/application_helper
def title # for entire application
base_title = "Testing"
if #title.nil?
base_title
else
"#{base_title} - #{#title}"
end
end
def page_title # for page header partial
"#{#page_title}"
end
views/layouts/application
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<%= csrf_meta_tag %>
<%= favicon_link_tag %>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
</head>
<body>
<div id="container">
<%= render "shared/page_header" %>
<%= render "shared/flash_message" %>
<%= yield %>
</div>
</body>
</html>
views/shared/_page_header
<% unless #page_title.blank? %>
<div id="page-header">
<span><%= #page_title %></span>
</div>
<% end %>
Now I have a RegistrationsController to override the functionality whenever I need to but as it inherits to the DeviseController, I don't think it can get to the Application Helper? I also tried to put this same code in the Registration Helper but that didn't work either. What should I do?
Maybe you can use;
application_helper.rb
module ApplicationHelper
def title(page_title)
content_for(:title) { page_title }
end
end
application.html.erb
<title><%= yield :title %></title>
view
<%= title "Your page title here" %>

Rails render partial without entire html layout

Ok so I have a problem with rails and rendering partials. I have a layout called profile and inside of the profile layout I have all my js, stylesheets, etc included.
<html>
<head>
<title>Profile</title>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" %>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js" %>
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "main" %>
<%= stylesheet_link_tag "reset" %>
<%= csrf_meta_tag %>
</head>
<body>
<%= yield %>
</body>
</html>
Inside of the yield tag(profile/index.html.erb) above is the following
<%= render :partial => 'pages/page', :layout => "layouts/default", :locals => {:page => #page } %>
Now in the pages/page view there are the same default tags such as css and js files. When i remove the css styles then I lose the styling for the pages/page view. Is there a way I can render a partial without recalling the same css and js files or what is a better way to go about doing something like so?
I always create the option to overwrite the stylesheets as follows:
<%= stylesheet_link_tag content_for?(:stylesheets) ? yield(:stylesheets) : "application", :debug => Rails.env.development? %>
Then inside a view
<% content_for :stylesheets %> some stuff or nothing in here <% end %>
That will let you specify in a view rendered in a layout you want no stylesheets and the same principle applies for javascripts.
That having been said if you are rendering a partial inside a layout that has an html tag and head etc. you should probably investigate if there is a better way to do what you are doing.
You need to pick one or the other: layout the original method call, or pass a layout to the partials. Doing both would be illogical.
There is a more thorough discussion here:
http://www.mikemayo.org/2012/rendering-a-collection-of-partials-with-content_for
I rarely see the usage of( or I am wondering if Rails support this usage... )
<!-- confirmed, this usage will cause error in Rails 3.2 -->
<%= render :partial => "some_partial", :layout => "some_layout" ... %>
I prefer to choose the specific layout in the controller:
def some_action
# some code
render :layout => "some_layout"
end
A partial is basically just a "slice of page" (like slice of cake... but in code form). It's designed to populate a small part of the page; typically one which will by dynamically updated depending on page variables.
Seems like you're confusing the purpose of layouts, views & partials in my opinion. If you want to dynamically load CSS / JS, put a "content_for" block in the profile views with a default layout, like this:
Layout
#layouts/default.rb
<html>
<head>
<title>Site Title</title>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" %>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js" %>
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "main" %>
<%= stylesheet_link_tag "reset" %>
<%= yield :header_includes %>
<%= csrf_meta_tag %>
</head>
<body>
<%= yield %>
</body>
</html>
Views
#views/profiles/index.html.erb
<% content_for :header_includes do %>
<%= stylesheet "profile_custom_css" %>
<% end %>
Partial
Partials could be used to keep your code DRY & give the output of specific header files, like this:
Partial
#views/elements/_custom_header.rb
<% content_for :header_includes do %>
<% headers.each do |type, value| %>
<% if type == "java" %>
<%= javascript_include_tag value %>
<% else %>
<%= stylesheet_link_tag value %>
<% end %>
<% end %>
<% end %>
View
#views/profiles/index.html.erb
<%= render :partial => 'elements/custom_header', locals: { :headers => [["java", "profile_custom"], ["stylsheeet", "profile_custom"]] } %>
#Resume standard view code here
Layout
#layouts/default.rb
<html>
<head>
<title>Site Title</title>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" %>
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js" %>
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "main" %>
<%= stylesheet_link_tag "reset" %>
<%= yield :header_includes %>
<%= csrf_meta_tag %>
</head>
<body>
<%= yield %>
</body>
</html>
I've not tested passing the partial locals as a hash, so the syntax may be incorrect, but this is what we'd do to load up the required code. The added benefit is that content_for only yields content that has been defined (I.E you just have to include yield :custom_headers and it will only show if the content block is present)

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