Rails, render a different header just on homepage - ruby-on-rails

I'm creating a rails app and I must differentiate the header for home page.
I already created a partial with the _home_header version and the _header version to use in every page, but I don't know how can I manage the change.
The header is included in my layout, and I render the same layout for every page. How can I tell to "layout" to use the _home_header version instead of the standard version when I request homepage?

I would use the current_page? helper and look at the root_path.
# app/views/layouts/application.html.erb
<% if current_page?(root_path) %>
<%= render 'layouts/home_header' %>
<% else %>
<%= render 'layouts/header' %>
<% end %>

Use something like this in application.html.erb
<% if request.original_url == root_url %> ## Specify the home url instead of root_url(if they are different)
<%= render 'layouts/home_header' %> ## Assuming that _home_header.html.erb is under layouts directory
<% else %>
<%= render 'layouts/header' %> ## Assuming that _header.html.erb is under layouts directory
<% end %>

Typically, you add more specific versions of pages in controller-specific sub directories.
That is, if you have a layout application.html.erb which renders a header partial...
# app/views/layouts/application.html.erb
<!doctype html>
<html>
...
<body>
<%= render 'header' %>
...
This will look for a header partial first in app/views/<controller_name>/ then in app/views/application/. So, your site-wide header would reside in app/views/application/_header.html.erb, and your homepage partial would reside in app/views/home/_header.html.erb, and it would "just work". Rails would load the more "specific" header.

An option to #meagar suggestion would be use a before_action on your application controller for that:
class ApplicationController
beore_action :set_header
private
def set_header
#header = if is_my_page
"Special header"
else
"Other header"
end
end
end
and in your layouts/application.html.erb:
<title><%=#title%></title>
The bright part of his solution is that all text are kept on the view files, which makes sense. The not-so-bright part is that is harder to follow.

Related

On rendering from controller, current_page method does not seem to work

I have a navigation bar included in application.html.erb. Because for some pages, such as the signup page, I need to place additional code inside the navigation bar, I have excluded those pages for showing the navigation bar through application.html.erb and instead included it in their respective view pages. See code below.
A problem arises when invalid data is entered in the signup form. The controller method then renders new. However, application.html.erb then doesn't seem to recognize that the current_page is still signup_path, therefore not applying the exception for not showing the navigation bar on that page. As a result when it renders new, the navigation bar is shown twice: once by order of application.html.erb and once by order of the view page itself.
Why, when rendering new on an invalid form entry, does it not see that it's still on signup_path? How should I adjust my code so that it does not show the navigation bar twice in that situation? Is there perhaps a way of including <%= yield special code if any %> in application.html.erb and <% special_code %> <% end special_code %> in the view page that passes this special code to application.html.erb?
In application.html.erb I have:
<% unless current_page?(signup_path) %>
<nav class="banner">
<%= render partial: "shared/header" %>
</nav>
<% end %>
In the view of my signup page:
<nav class="banner">
<%= render partial: "shared/header" %>
Additional code that needs to be within 'nav' for this page
</nav>
Controller method:
def create
#user = User.new(user_params)
if #stakeholder.save
flash[:success] = "A confirmation email has been sent to you."
redirect_to root_url
else
render 'new' ###This is where it goes wrong!
end
end
You can use content_for and yields to create a default in your layout which views can override.
# layouts/application.html.erb:
<% if content_for?(:banner) %>
<%= yield(:banner) %>
<% else %>
<div id="banner">
<h1>This is the default...</h1>
</div>
<% end %>
/users/signup.html.erb:
<%- content_for :banner, flush: true do -%>
<!-- move along, nothing to see here -->
<%- end -%>
The advantage here is that you don't end up turning your layouts into a birds nest of conditionals. You can easily just inject whatever you want into the layout from views.
The cons are that you have to use a stupid hack with a HTML comment to override the block to display nothing since content_for? trims the block. content_for does not play nice with fragment catching either.
addded
I didn't touch on this before unless current_page?(signup_path) does not work as you expect since render 'new' does not magically move you to the new action. In fact the current_path is /users since the form POST's to that url.
It just tells rails to find a template named 'new' and render it.
A corrected version would be:
<% unless controller_name == 'users' && ['new', 'create'].include?( action_name) %>
<nav class="banner">
<%= render partial: "shared/header" %>
</nav>
<% end %>
You are completely right. This is where it goes wrong
render 'new' ###This is where it goes wrong!
Here's what happens
user requests a new action, which renders the new template
user submits the form, thus requesting the create action in your controller
inside your create action you render your new template instead of create when validation fails
So basically user is no longer on the new page, but on the create page with a view rendered from new.
The easiest solution would be to change expectation for the header to both new and create actions, since you redirect on success, so you won't use it otherwise.

How Do I "Extend" a view in Rails

So I'm working on an open source project and due to different versions, there's the issue where I can't count on there being a controller for a view. Instead this email would be send out via a rake task for one version and a few others would done via a controller. Now you understand why I'm asking a bad practice question...
I have a layout for a view. Does anyone know a way to specify what the layout is for the view within the view. Some pseudo-code:
<%= extends 'layout/test_mailer` %>
<h1> Hey there! </h1>
And the layout would have the usual yield within it.
I hope I'm explaining the problem good enough.
<%= render partial: "hey_page", layout: "layout/test_mailer" %>
Check part 3.4.3 Partial Layouts at RailsGuides.
I think using yield and content_for should solve the problem. [Guides]
# my_layout.html.erb
<%= yield :mail_view %>
# my_mail_view.html.erb
<%= content_for :mail_view do %>
<!-- html -->
<% end %>
Of-course, if you are using params to get the layout, this would be a wrong answer.
Then, you can also use:
<%= render partial: "link_area", layout: "graybar" %>
You can use
//controller action
def index
render layout: test_mailer
end
//view, index.html.erb
<h1> Hey there! </h1>
//view, layout/test_mail.html.erb
<html>....layout for you test mail
<% yield %>
</html>

how to extend child template to parent in rails

I am new to rails. I am having difficulty in understanding template inheritance. Earlier I have worked in django and seen template inheritence there. There I saw child is told about parent using "extends" command. Can anyone explain how it works here. I have gone through guidelines of ruby but it was not clear.
Thanks
It's quite simple to do in Rails.
You simply tell the template you are currently rendering to render another template.
For example layouts/application.html.erb contains something like this:
<% content_for :navigation do %>
<nav>...</nav>
<% end %>
<% content_for :content do %>
<%= yield %>
<% end %>
<%= render :template => 'layouts/main_application' %>
The important part is the render :template part that then delegates this template to also render the layouts/main_application.html.erb that in my case looks something like this:
<header>
...
</header>
<body>
<%= yield :nav %>
<%= content_for?(:content) ? yield(:content) : yield %>
</body>
What I am doing here is having a main template that does not contain the navigation (for things like login etc) and the application.html.erb adds that navigation to the :nav content placeholder.

ruby template erb

I have a layout.html.erb file w hich should act as a common file for all the pages to decorate as shown below.
<%= render :partial => "layouts/header" %>
<%= render :partial => "layouts/leftsidemenu" %>
<body>
<%= #content_for_layout %>
</body>
<%= render :partial => "layouts/footer" %>
How can I configure this rails framework so that, so that I do not want to include layout.html.erb in all the pages as
<%= render :partial => "layouts/layout" %>
I need to configuration file to decorate, as we do in Struts framework using sitemesh decorator.xml file.
thanks in advance
Mahesh
First of all, default layout for Rails app is in <rails_app>/app/views/layouts/application.html.erb and is used because all your controllers inherit from ApplicationController (see the name, as convention Rails use layout with same base name as controller, or name of parent controller and so on).
Second, your layout should look something like that:
<%= render :partial => "header" %>
<%= render :partial => "leftsidemenu" %>
<body>
<%= yield %>
</body>
<%= render :partial => "footer" %>
of even paste content from header and footer to this layout. More informations about layouts you can find in this guide.
If you want to change some aspect of page, for example title, then you can do so with layouts too:
# header.html.erb
<head>
<title>
<%= yield(:title) of "Default title" %>
</title>
</head>
# page.html.erb
<% content_for :title do %>
Specific title
<% end %>
Page content
If you want to use layout from different file, then you can do so that way:
# ApplicationController.rb
class ApplicationController < ActionController::Base
# ...
layout 'your_layout' # file in app/views/layouts
# ...
end
Take a look at the Rails docs for structuring layouts, especially the yield and content_for tags. You can also specify a layout with layout in a controller, or a default one for all controllers in your application controller.

How to include a css or javascript in an erb that is outside the layout?

Sorry for the slightly noobish question, as I am writing my first rails app.
I get the idea of the layout view, but if you are using them, is there any way to include a view specific js or css file? For example, I have layouts/products.html.erb, and for products/edit.html.erb I want products_edit.css, but I don't want that css for all product views, what is the best practice to accomplish that?
If you have a generic edit.css file, I would suggest an if in your layout
<%= stylesheet_link_tag 'edit' if params[:action] == 'edit' %>
Otherwise you can use content_for with a yield to add additional tags into the head.
layout.html.erb
<head>
...
<%= yield(:header) if #content_for_header %>
</head>
products/edit.html.erb
<% content_for :header do -%>
<%= stylesheet_link_tag 'edit_product' %>
<% end -%>
You can add a stylesheet tag inside the head tag of the layout by doing something like this:
layouts/products.html.erb:
<head>
...
<%= yield :css %>
...
</head>
products/edit.html.erb
<% content_for :css do
stylesheet_link_tag 'products_edit'
end %>
You can't if your </head> tag is in your layout.
You may want a different layout for that controller action. Like this on the render:
render :action => "index", :layout => "some_other_layout
Also you can set a different default layout for a whole controller with this line in the controller class:
layout "some_other_layout"
Check the API docs, there's some complex things you can do with conditionals on that layout method.

Resources