How do I remove a heading yield a specific page? - ruby-on-rails

I am providing the h1 into my header, but I don't want it on products/show. How do I remove it just for that page?
_header.html
<header>
<h1><%= yield(:heading) %></h1>
</header>
Homepage
<% provide(:heading, 'This is the homepage heading') %>
products/show
<% provide(:heading, '') %>
Obviously, I could just not provide any heading for the products/show page but there is CSS styling applied to my H1 that is screwing up my design, whether there is content in the h1 tag or not.

Only render the element if it has content:
<header>
<% if content_for?(:heading) %>
<h1><%= yield(:heading) %></h1>
<% end %>
</header>

Use a different partial? Put an if-ish statement around the header?
As-is the elements themselves will always be there, because there's no reason for them not to be.

Related

How to use yield with dashboard sidebar to show contents on same page

I have a rails app with a functionality quite similar to yelp with a dashboard for customers where they can edit their place but also change their acccount settings, etc. A customer can only have one place for now.
I've created a dashboard controller, views with a partial for the sidebar and everything is working so far but my problem is that when clicking on a link in the sidebar it yields to the application.html.erb. I want to yield everything from the sidebar to the main part of the dashboard/index.html.erb
So my question is how do I yield the things I click in my sidebar to the part on the right next to the sidebar on the page. Basically the functionality is like a navbar on top (only for logged in customers) but I get confused with two yields. I tried "content_for" and <%= yield :sidebar %> but didn't figure out how to get it working yet. Also I am using devise with a user and customer model which share the views and have the functionality for the customer to edit his user account in the dashboard sidebar which might cause a problem with "content_for"?
Please note that I am still learning ruby on rails and am very happy for any kind of input!
dashboard_controller.rb
def index
#place = Place.where(customer_id: current_customer.id).first
end
dashboard/index.html.erb
<div class="content">
<div class="sidebar">
<%= render 'dashboard/sidebar' %>
</div>
<div class="main">
<%= yield %>
</div>
</div>
_sidebar.html.erb
<li class="nav-link">
<%= link_to "<span class='fa fa-cog'></span> Edit".html_safe, edit_place_path(#place) %>
</li>
<li class="nav-link">
<%= link_to "<span class='fa fa-cog'</span> Settings".html_safe, edit_customer_registration_path %>
</li>
<li class="nav-link">
<%= link_to "<span class='fa fa-sign-out'></span> Log Out".html_safe, destroy_customer_session_path, method: :delete %>
</li>
application.html.erb
<body>
<%= render 'layouts/shared/header' unless #disable_navbar %>
<%= yield %>
<%= render 'layouts/shared/footer' unless #disable_footer %>
</body>
Within the context of a layout, yield identifies a section where content
from the view should be inserted.
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
So no - yield will not "yield" to your dashboard/index.html.erb. Rather if you want it to have a different layout you should create a layout.
Lets look at an example:
<% # app/views/layouts/application.html.erb %>
<body>
<nav id="top-menu">
<h1><%= link_to 'MyApp', root_path %></h1>
<ul>
<%= yield :nav_links %>
<%# lets provide some default content for nav_links %>
<% content_for :nav_links do %>
<li><%= link_to 'Products', products_path %></li>
<% end %>
</ul>
</nav>
<div id="main">
<%= yield %>
</div>
</body>
Here we create a named yield in the layout called :nav_links and also add some "default" content with content_for :nav_links.
So lets look at what happens when we render /products/index.html.erb:
<div class="products">
<% if #products.any? %>
<%= render #products %>
<% else %>
<p>No products are available at this time</p>
<% end %>
</div>
<%# we also want to add a contextual navigation link %>
<% content_for(:nav_links) do %>
<li><%= link_to 'Sales', sales_path %></li>
<% end %>
The rendered result is:
<body>
<nav id="top-menu">
<h1>MyApp</h1>
<ul>
<li>Products</li>
<li>Sales</li>
</ul>
</nav>
<div id="main">
<div class="products">
<p>No products are available at this time</p>
</div>
</div>
</body>
Rails inserts the content from the view into the "main" layout and the content from content_for(:nav_links) is concatenated into the buffer.
Also I am using devise with a user and customer model which share the
views and have the functionality for the customer to edit his user
account in the dashboard sidebar which might cause a problem with
"content_for"?
The only problem you may have is a "namespace collision" - if you are using yield :sidebar and a gem for example is also using the same name for a yield you may have unexpected results.

Using multiple yields to insert content

I am trying to insert content on my page with yield but every time action removes whole content from the page. I have one main yield which is working fine:
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
But inside that new content which is displayed on one page I have another yield:
<div class="container">
<%= render 'admins/menu' %>
<%= yield :admin %>
</div>
When user clicks on the menu which is rendered, new content should be displayed below that menu.
admins/_menu.html.erb
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li><%= link_to "Users", :controller => "admins", :action => "test" %></li>
<li><%= link_to "1", ... %></li>
<li><%= link_to "2", ... %></li>
<li><%= link_to "3", ... %></li>
</ul>
</div>
</div>
</div>
Controller:
class AdminsController < ApplicationController
def index
end
def test
#users = User.paginate(page: params[:page])
end
end
test.html.erb
<% content_for :admin do %>
<h1>All users</h1>
...
<% end %>
When I click on the option 'Users' from menu, page refreshes, menu disappears and nothing is displayed inside `body'. I want the content to be displayed below menu. How to use that second yield and accomplish this functionality?
I hope the question is not confusing. If question is confusing, please write me in comments and I will edit it immediately.
Thank you :)
So, when you go to the index page you will get the piece of html that will be placed in the main layout, and this piece of html look like this:
<div class="container">
<%= render 'admins/menu' %>
<%= yield :admin %>
</div>
This code will yield :admin properly.
When you go to the test page you do not have this html code anymore (since it only belongs to the index method). So, anything you put in the content_for(:admin) block will be ignored since no-one is printing it.
What you probably want to do is creating a shared layout for all your admin pages. Follow this guide and you'll have your solution.
Solution
Edit the application.html.erb layout using this:
<%= content_for?(:content) ? yield(:content) : yield %>
instead of
<%= yield %>
Then create an admins.html.erb file inside the layouts folder to handle your admin pages' layout. Something like this:
<% content_for :content do %>
  <div class="container">
<%= render 'admins/menu' %>
<%= yield %>
</div>
<% end %>
<%= render template: "layouts/application" %>
Will do fine. Then in the index.html.erb and test.html.erb just place regular HTML content, without using the content_for(:admin) block. Everything should work fine and you'll have your custom admin template, with a slightly different look from regular pages.
Calling yield doesn't work in helper modules, while content_for does, so you should replace your yield calls in the helper files.
Also noteworthy: using provide is recommended over content_for when you're only using the method in 1 place instead of multiple places. You'll get better performance since it won't leave the buffer open while looking for more content, and your intent will be clearer to other developers that may see your code. (see http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide)
I found that you have to add an yield (without actually outputing) before the namespaced tags.
<div>
<% yield %>
<div class="mt-3">
<div class="text-2xl tracking-wide font-bold text-gray-900">
heading
<%= yield :heading %>
</div>
</div>
<div class="relative bg-white rounded-xl shadow-xl mb-8 min-h-28">
<%= yield %>
</div>
...

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>

In RoR, is there an easy way to prevent the view from outputting <p> tags?

I'm new to Ruby and Rails and I have a simple controller that shows an item from the database in a default view. When it is displaying in HTML it is outputting <p> tags along with the text content. Is there a way to prevent this from happening? I suppose if there isn't, is there at least a way to set the default css class for the same output in a statement such as this:
<% #Items.each do |i| %>
<%= i.itemname %>
<div class="menu_body">
Link-1
</div>
<% end %>
So the problem is with the <%= i.itemname %> part. Is there a way to stop it from wrapping it in its own <p> tags? Or set the css class for the output?
Thanks!
You need to enclose it with the HTML tag of your choice. Also if required you can escape bad code by using <%=h i.itemname %> Example:
<% #Items.each do |i| %>
<div><%=h i.itemname %></div>
<div class="menu_body">
Link-1
</div>
<% end %>
Edit: Ryan Bigg is right. Rails doesn't output a <p> tag. Sorry for the wrong info.
You canchange the public/stylesheets/scaffold.css if you want.
Or if you want to change it for a single page say items/index.html.erb
<style>
p{
/* your style here *?
}
</style>

Two-part Rails layouts

My web pages consist of two parts, let's say top and bottom (except header and footer -- those are consistent across pages). What is the best practice to dynamically generate those parts depending on the action?
One approach I have come up with is to have view for the top and partial for the bottom; in the layout call yield for the top and render partial for the bottom. The name of the partial is dynamically substituted depending on the action.
Not sure it is the best way to do it.
I think your idea is fine. In your views you could do:
<%- content_for :top do -%>
[…]
<%- end -%>
<%- content_for :bottom do -%>
<%= render #partial_name %>
<%- end -%>
Of course you should check whether the partial exist and provide some default behavior. But I think you're aware of that anyway.
And then in your layout:
<div id="top">
<%= yield :top %>
</div>
<div id="bottom">
<%= yield :bottom %>
</div>
Here is a very simplified version of a view DSL I've used in the past. Worked well for us. In reality we parameterized the the helper methods so we could choose from many layout partials on the fly (to have pages with sidebars, multiple columns, etc).
# app/views/shared/_screen.erb
<div id="screen">
<div class="screen_header">
<%= yield :screen_header %>
</div>
<div class="screen_body">
<%= yield :screen_body
</div>
<div class="bottom">
<%= yield :footer %>
</div>
</div>
# app/helpers/screen_helper.rb
module ScreenHelper
def screen(&block)
yield block
concat(render :partial => 'shared/screen')
end
def screen_header
content_for :screen_header do
yield
end
end
def screen_body
content_for :screen_body do
yield
end
end
def footer
content_for :footer do
yield
end
end
end
# app/views/layouts/application.erb
# only showing the body tag
<body>
<%= yield :layout
<body>
# Example of a page
# any of the sections below (except screen) may be used or omitted as needed.
# app/views/users/index.html.erb
<% screen do %>
<% screen_header do %>
Add all html and/or partial renders for the header here.
<%end%>
<% screen_body do %>
Add all html and/or partial renders for the main content here.
<% end %>
<% footer do %>
Add all the html and/or partial renders for the footer content here.
<% end %>
<% end %>

Resources