I've watched this screencast to add a page title when in a view, is there a way I can do the same but add a class the body tag?
Not sure what you mean, you can do it the same way:
In a view:
<% content_for :body_class, "my_class" %>
In a layout file:
<body class="<%= yield (:body_class) %>">
I usually make a helper method for stuff like this so you can have defaults set up cleanly
application_helper.rb
def body_class(class_name="default_class")
content_for :body_class, class_name
end
view:
<% body_class "foo" %>
application.html.erb
<body class="<%= yield (:body_class) %>">
Sometimes using the current controller name as a class name we'll do:
<body class="<%= controller.controller_name %>">
I find this simpler and a bit more elegant, but of course thus you won't be able to assign individual class names.
s. Add Class To Body Using ERB In A View - Rails
In the layout page:
<% if content_for?(:body_class) %>
<body class="<%= content_for(:body_class) %>" >
<% else %>
<body>
<% end %>
In the content page:
<% content_for :body_class do 'my-body-class' end %>
I've used the accepted method in my app for a while, but never really loved how it worked, because if there is no class, you're gonna have that class=' ' on your body tag, littering your code. For my current use case, I just wanted a widescreen class (but you could easily get more advanced with different classes per your use case). I'm happy with this approach:
In your application helper:
def body_tag(&block)
content = capture(&block)
content_tag(:body, content, class: #widescreen ? "widescreen" : nil)
end
In application.html.erb
<%= body_tag do %>
<%# the rest of your content here %>
<% end %>
Then in your application controller:
private
def enable_widescreen
#widescreen = true
end
Then in any controller that you want it, just do:
before_action :enable_widescreen
Then feel free to make the class logic more advanced if you want to use it for different classes besides 'widescreen' - but the point is that this is an elegant way to allow for there NOT to be a class if you don't specify one, without
<body class>
showing up in your html.
I prefer to use the following method:
<body class="<%= content_for?(:body_class) ? yield(:body_class) : controller_name %>">
That method avoids the dreaded <body class>.
I frequently use the controller name to scope a number of styles so it's nice to not need to supply a content_for on every view if I only needed that one class.
Related
I have a situation in rails (version 4.04, ruby version 2.1) where I've been using the standard application.html.erb to define the main framework for my site, header, footer, nav bar, etc. When I got to an inner div, call it, inner-content, thats where I put a <% yield %> statement so that the sub template can take over and place its content in the correct place (for example products#show or products#index have show.html.erb and index.html.erb respectively which just the content for those actions).
The problem is I realized I was duplicated some code in those sub templates. In ever one of them (except one) I always was starting off like this:
<div class="columns large-6 medium-6 center-small">
<div class="inner_wrapper">
And I was always ending like this:
</div>
</div>
So I was thinking, I shouldn't be repeating all this code. I should move this into application.html.erb so that every template automatically gets the inner-content set up correctly.
The problem is that one action I was talking about. There is one action that has a different setup. I don't want to have to type in those extra 2 divs for every sub-layout except one. Is there a better way to do this?
One way could be to check which controller your currently using this in your application.html.erb
<% if params[:controller] == "controller name" %>
<div>
<%= yield %>
</div>
<% else %>
<div class="different div">
<%= yield %>
</div>
<% end %>
Not sure if this the best way, but its one way to do it.
Create a different layout file and call it maybe products_layout.html.erb.
Then in the controller
class ProductsController < ApplicationController
layout: 'products_layout'
....
end
Or do it on a per action
def show
render 'show', layout: 'products_layout'
end
http://guides.rubyonrails.org/layouts_and_rendering.html
I'm new on RoR and I'm trying to understand how the communication between the view and the layout works.
I found some documentation and I get I need to use provide or content_for methods but it doesn't really explain how it gets accomplished.
Also, why do I need to use yield in my layout to print the value?
Example:
home.html.erb (view)
<% provide(:title, 'Home') %>
application.html.erb (layout)
<title>Great App | <%= yield(:title) %></title>
In your sub-view, you can use either of these methods by passing a block with text content (which can be useful for long content passages such as those that include HTML tags):
<% content_for :title do %>
<h1>The Title</h1>
<% end %>
Or you can simply pass a string directly to #content_view as the second argument:
<% content_for :title, "The Title" %>
There's some better documentation for these methods in the ContentHelper module.
Layouts just wrap other views, and subviews can be thought of as "blocks" that are passed to the layout. If you think of them that way, its natural that the yield keyword is used to invoke the subview like a block.
I have the following little problem.
In layout I yield to content_for to set up some classes on my body tag:
<body class="<%= yield(:body_classes) %>
They I would like to call content_for
<%= content_for(:body_classes, "one") %>
So far so good. I use content_for for the second time:
<%= content_for(:body_classes, "two") %>
In my HTML I get the following:
<body class="onetwo">
Is there an elegant way to separate those two classes by space? I can think of couple of hacky solution, but nothing feels right...
Many Thanks!
I don't think content_for is a good fit in this case. However, you can solve the problem elegantly with a couple of helper methods (extracted from one of my Rails projects):
def klass(*classes)
#classes = [] if #classes.nil?
#classes += classes
#classes.uniq!
nil
end
def has_klass?(klass)
!#classes.nil? && #classes.include?(klass)
end
def body_klasses
#classes.map(&:to_s).join(" ") rescue nil
end
Usage in templates:
<%= klass :one, :two %>
<%= klass :three %>
In the layout, determine if a certain class is set:
<% if has_klass? :one %>
And finally...
<body class="<%= body_klasses %>">
You can further customize these to better suit your needs.
Just put a space before (or after) the class each time you set content_for.
<% content_for(:body_classes, "one ") %>
By the way, you probably don't want the = in your setting tags; you generally don't want Rails to output into the HTML the content you're storing away for later.
Maybe you can check, before adding something to :body_clases, if thereĀ“s something already in it, in order to add the space before adding the new content.
<% content_for(:body_classes,content_for?(:body_classes) ? ' one' : 'one') %>
<% content_for(:body_classes,content_for?(:body_classes) ? ' two' : 'two') %>
The final Html will be:
<body class="one two">
so basically my application looks like this
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<div id="content">
<div id="content_left">
// Some Stuff
</div>
<div id="content_right">
<%= yield %>
</div>
</div>
</body>
</html>
now i want to switch between a two column layout and a single column layout based on the controller (at best: also based on the method i'm using).
between the body and the content and also in there head there is way too much stuff for simply creating a second layout and adding this as a layoutcall in my controller without too much code duplication.
what i would love to do is something like this:
all should use this
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
// much stuff
<%= yield %>
</body>
</html>
and now i can switch between two layouts, f.e
single_col
<div id="content">
<%= yield %>
</div>
or two_column
<div id="content">
<div id="content_left">
// Some Stuff
</div>
<div id="content_right">
<%= yield %>
</div>
</div>
and then in the last yield there should be the view that is related to my controller and method.
is there an easy way to achieve this?
thanks for all hints.
PLEASE leave a comment if something is unclear.
Put the different parts of the layouts in partials! Then you render the partials based on what is in
params[:controller] and params[:action]. For example:
<% if params[:controller] == "controller_name" %>
<%= render 'partial_name1' %>
<% else %>
<%= render 'partial_name2' %>
<% end %>
The params[:controller] and params[:action] are always available! This is an example to show you how it works. Of cause there shjouldnt be any logic in the view aswell!
You could use two layouts that would both render header and footer partials containing what's common to both.
OR, you use a <%= yield :sidebar %> and then inject something
<% content_for(:sidebar) do %>
some stuff here
<% end %>
Take a look at Rails guide section about those here.
I think you looking for a way to use a shared layout with two sub layouts inside, here you go:
add this to your application_helper.rb
# Allows easy using nested layouts
def inside_layout(layout = 'application', &block)
render :inline => capture_haml(&block), :layout => "layouts/#{layout}"
end
layouts/application.html.haml
!!!
%html
%head
-# your header content
%body
.content
= yield
layouts/single_column.html.haml
= inside_layout do
.middle
= yield
layouts/two_column.html.haml
= inside_layout do
.left
-# your shared left content
.right
= yield
the column layouts can now be used like normal layouts, so you can just set them in your controller
layout "single_column"
Note: All markup is in HAML a gem which I can highly suggest.
hope it helps :)
You can use multiple layouts in a rails project. You could have a 2 column and a single column one.
To specify the layout used, provide it in the method in the controller.
def index
respond_to do |format|
if current_user
format.html {render 'pages/index', :layout => 'home'}# index.html.erb
else
format.html {render 'pages/landingpage', :layout => 'landingpage'}# index.html.erb
end
end
end
For example the controller above, will render the the views index.html.erb and landingpage.html.erb based on the value of current_user. You can also only provide the layout parameter to render like render :layout => 'home'
You could also have a look at nested layouts but I have never worked with those.
You could use sub_layouts
Class IndexController < ApplicationController
def index
def sub_layout
"left"
end
end
end
Add sub layouts to /app/view/shared/layouts/sub/_mysublayout.haml or .erb
in /app/views/layouts/application.haml/.erb * your main layout file*
= render :partial=>"layouts/sub/#{controller.sub_layout}"
With some checking for nils with rescue nil you can make this sub template load the layout for say a right or a left col, use this with great benefit in my apps. Its a big time saver and gives you the extra flexibility to set the layout on an action base.
Im assuming that in your single column layout you have no information for your content_left.
And in two column layout you have information for both content_left and content_right.
There are lots of ways but im recommending something completely controlled by CSS.
CSS classes will be like following
#content_left{
float: left;
background-color: red;
}
#content_right{
background-color: green;
}
#content {
margin: 0 auto;
width: 80%;
}
Now notice... if content_left div is empty then the content_right div will expand to full width. And you are ready with single column layout. And if you have data in content_left it will be showing accordingly.
I am using helper method to set Dynamically sidebar,you can use as per your requirement.
<div id="main_content" class="<%= main_content_css_class %>">
<%= yield %>
</div>
<div id="sidebar" class="<%= sidebar_css_class %>">
<%= yield :sidebar %>
</div>
In Helper
def sidebar_enabled?
current_page = "#{controller.controller_name}.#{controller.action_name}"
current_controller = controller.controller_name
pages = %w(home)
return pages.include?(current_page) || pages.include?(current_controller)
end
def main_content_css_class
sidebar_enabled? ? "grid_12" : "grid_16"
end
# Returns the CSS class for the 'sidebar' div depending on sidebar requirement
def sidebar_css_class
sidebar_enabled? ? "grid_4" : "dont-show"
end
Using your code is also cleaner and maintainable.
I usually put classes on the body element for the controller and action name, as it comes in handy in many ways. Here's an example based on that strategy:
...
<body class="<%= controller_name %> <%= action_name %>">
<div id="content">
<div id="sidebar">
// Some Stuff
</div>
<div id="main">
<%= yield %>
</div>
</div>
</body>
...
then it's a matter of getting the right CSS. If you had a 'posts' controller, the sidebar is usually visible, and you want to hide it:
body.posts { #sidebar { display: none; } };
If you want it to only show up sometimes, you could invert the logic so it's usually hidden, then override with a more specific scope to show it. Or, you could have one column on all 'index' actions, two columns for everything else, etc.
The downside to this, and it's not insignificant, is that your views are coupled to the name of the controller. I.e. this strategy violates the "tell, don't ask" principle. I still think it's worth it, since I haven't been bit by a nasty refactoring involving this code yet. Usually it's a simple matter of changing a couple instances of the controller name in ex. "posts.css.scss" when renaming the file; no big deal. Just something to weigh.
Also, note that I picked more descriptive class names. "Left" and "right" are ephemeral, as your example demonstrates. "Sidebar" and "main" might not be what you have, but I'd make an attempt to describe what goes in them if possible.
im trying to make a app
with users
this users can join multiple groups - every group has the same menu on the page
their group page is accessable about group/1 or group/2
so i wanted to put the the menu in the application.html.erb, with lnks depending on the group.id - but i dont know how to acces this id in the application.html.erb
This is often done using content_for in the layout. Let's say you want your menu in a certain div in application.html.erb:
# application.html.erb
<div id="menu_div>
<ul>
... etc ...
</ul>
</div>
Replace the inner content with a yield statement:
<div id="menu_div>
<%= yield :group_menu %>
</div>
Then in the view template add the content_for block:
# page
<% content_for :group_menu do %>
<ul>
... etc ...
</ul>
<% end %>
Each page template can then define its own menu code in a content_for block. This can be further generalized by using a helper method in the block and passing in instance variables.
EDIT
Assuming #group is assigned in the controller, you might do something like:
<% content_for :group_menu do %>
<%= show_me_the_menu(#group) %>
<% end %>
and in the helper (obviously contrived example):
def show_me_the_menu(group)
content_tag :ul do
group.users.collect do |user|
concat(content_tag(:li, user.some_method))
end
end
end
The correct approach would be to set this in ApplicationController via before_filter and just use it as an instance variable in views.
So in controller set something like #links = some logic where you calculate your links based on current user.
In view you do something like:
<ul>
<%- for link in #links -%>
<li><%= link.title =>
<%- end -%.
</ul>
Of course, you set your #links in ApplicationController only if you want your links to be available to all your controllers/views, which I think you do.
Rails Cells could also be used here http://cells.rubyforge.org/
i solved my problem in a different way now. i created in the group model a "menu" and this i render partial in the application.html.erb