I am trying to add a "Create Account" box to the sidebar of my homepage. I added it to my application layout but I only want it to appear when the user is on my homepage. What is the best way to detect which page you are on and when to render the content?
<% if current_page? %>
<%= render layout/create_account %>
Thanks in advance
I would do it via content_for
In your sidebar insert this:
<%= yield :create_account_box %>
And then in your homepage view do this:
<% content_for :create_account_box do %>
render your box here.
<% end %>
This technique allows to inject content from subviews to superviews without any ifs or stuff like that.
Related
In my quest to keep my application views as DRY as possible I've encountered a little snag. My appliation.html.erb incorporates a static sidebar menu. Each of my main controllers incorporates a secondary sidebar menu (essentially a submenu). I can take the code that renders the menu out of application.html.erb and put it in each of my views and change the secondary sidebar there, but this produces a lot repetition in my views.
I saw this SO post and looked at this page, but I was unable to get either idea to work. I was thinking that I could put something like:
<% provide(:submenu, 'layouts/sidebars/sidebar_customers_contacts') %>
at the top of each view and use that to render the associated partial by doing
<% content_for(:submenu) do %>
<%= render :partial => :submenu %>
<% end %>
from the application.html.erb but of course that didn't work.
This is my current application.html.erb:
<div class="side">
<%= render 'layouts/sidebar' %>
<%= render 'layouts/sidebars/sidebar_dashboard' %><!-- this needs to load a sidebar based on the controller that calls it. Each view of the controller will get the same sidebar. -->
</div>
<div class="main-content">
<%= yield %>
</div>
I feel like I'm making this more difficult than it really is. Is there a simple way to do this?
Rails provides a helper called controller_name which you can read more about here.
Assuming you adhere to your own naming conventions, this should work as-is. If you decide some controllers don't get a sidebar, you may need to throw in some conditionals...
application.html.erb
<div class="side">
<%= render "layouts/sidebar" %>
<%= render "layouts/sidebars/#{ controller_name }" %>
</div>
<div class="main-content">
<%= yield %>
</div>
EDIT
Sorry, my mistake was using single quotes instead of double-quotes. You cannot use #{string interpolation} within single quotes. Source
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.
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>
I have a main layout (application) and two 'sub' layouts (dashboard and admin). In my dashboard and admin controllers respectively I have a before_filter which render templates: the template I want (either dashboard or admin).
In my dashboard and admin layouts, I am doing something along the lines of:
<% content_for :top_menu do %>
<%= render partial: "layouts/menu/top", locals: {section: 'admin'} %>
<%= render partial: "layouts/menu/sub", locals: {section: 'admin'} %>
<% end %>
So this is including the top partials with a section local which shows the correct options I want.
In my application layout, I have the following:
SOME HTML HERE
<%= yield :top_menu %>
SOME HTML HERE
<%= yield %>
SOME HTML HERE
The problem is that the content from my views isn't being displayed, I'd expect it to be displayed where the 'yield' is in my application layout.
I have read: http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts - but following that just displays the menus twice and still doesn't display my content.
I think I am failing to understand something here, help would be appreciated.
In short, I want top menus and it's in the controller that I want to specify which menu is to be used. I'm sure there is a better solution to this that I am missing also.
I have fixed this by doing:
layout 'menu/admin'
In my controller, and adding:
<%= render template: "layouts/application" %>
To my layouts.
How does one setup a condition if 'post_controller/show' is viewed.
I would like an 'Edit' button to display on the header navigation if the user is viewing the post, without setting up a different layout and have it just rely on the existing _header.html.erb for navigation.
# Header navigation partial _header.html.erb
<%= link_to 'Edit post', edit_post_path(#post) %>
I know I can take a param and set clause something like:
<% if params[:id] %>
<%= link_to 'Edit post', edit_post_path(#post) %>
<% end %>
The problem with the above is it will cause an exception undefined method 'edit_post' if you visit a different route that doesn't relate to the Post model.
If I can get away from many layouts and partials that would great but if the suggestion is that I should be using layouts and partials in this scenario then I will.
Many thanks all.
# Header navigation partial _header.html.erb
<%= yield(:edit_link) %>
#In your post_controller/show view :
<% content_for :edit_link, link_to('Edit post', edit_post_path(#post)) %>
And that's about it. You can change the content of :edit_link wherever you want, for different models etc ...