Using multiple yields to insert content - ruby-on-rails

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>
...

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.

Rails: Using Yield, Calling Multiple View Pages

Problem: How Do You Render Multiple View Pages From Different Controllers?
In my views/layouts/application.html.erb, I call views/posts/index.html.erb with the yield method.
<div class='span10'>
<%= yield %>
</div>
On top of it, I wanted to call views/good_posts/index.html.erb with yield. So in that particular index file, I wrap around all the code with content_for method.
<%= content_for :good_post do %> all my content in good_post/index.html.erb <% end %>
I go back to application.html.erb, I tried to call the index file of good_post with yield method.
<div class='span10'>
<%= yield :good_post %>
</div>
<div class='span10'>
<%= yield %>
</div>
I thought this would result in good_post/index to be rendered on top of post/index, but it did not work; only post/index was correctly rendered as before. Could someone explain why this is, and tell me the correct way to approach this problem? I appreciate your help!
You should, as #cdesrosiers said, rename the index.html.erb file in good_posts to _index.html.erb. Then you can render this view in your application like this:
<div class='span10'>
<%= render 'good_posts/index' %>
<%= yield :good_post %>
</div>
<div class='span10'>
<%= yield %>
</div>
Personally, I would change the index.html.erb file in good_posts folder to _good_posts.html.erb file in app/views/posts folder. Your code will have better meaning, and you can know where to find it after, because it relates to posts. So, if you change this, use this code:
<div class='span10'>
<%= render 'posts/good_posts' %=
<%= yield :good_post %>
</div>
<div class='span10'>
<%= yield %>
</div>
Another you should change is content_for :good_post -> content_for :good_posts , because good posts maybe have many posts, so you should use post in pluralize.

Ruby on Rails: Can't insert content into yield block

I have a partial called _main_nav.html.erb in views/layout folder, this is content file:
<% content_for :main_navigation do %>
<h1>Something must here</h1>
<% end %>
In my _header.html.erb file, i used yield to insert content:
...
<%= yield :main_navigation %>
<nav id="utility" class="nav-collapse">
...
But when i visit home page, it does'n appear h1 element, i have wrong somewhere?
The issue is that you are trying to yield content from a view that isn't currently being rendered.
You would need to do:
_header.html.erb
...
<%= render "layouts/main_nav" %>
<%= yield :main_navigation %>
<nav id="utility" class="nav-collapse">
...
_main_nav.html.erb
<% content_for :main_navigation do %>
<h1>Something must here</h1>
<% end %>
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield

how to use jQuery in Rails helper

I am trying to use the tabs function from jQuery UI in a Rails app. I am using a helper for the navigation and I would like to keep it that way. The code in my helper is:
def links_for_navigation
html = ""
html = <<HTML
<ul>
<li>Courses</li>
<li>Parts</li>
<li>Categories</li>
</ul>
<div id="tabs-1"><% link_to "Courses", courses_path %>
<div id="tabs-2"><% link_to "Parts", parts_path %>
<div id="tabs-3"><% link_to "Categories", categories_path %>
HTML
end
My view pulls in the code with <% links_for_navigation %>
I added to my application.js:
jQuery(function() {
jQuery("#tabs").tabs();
});
And my application.html.erb has:
<%= stylesheet_link_tag 'courses', 'jquery-ui-1.8.13.custom.css' %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'jquery-1.5.1.min.js', 'jquery-ui-1.8.13.custom.min.js', 'application' %>
When I try to load the page I get cannot find string HTML before EOF. What am I doing wrong?
I recommend using a partial, rather than a helper method. The partial, I'll call it _nav.html.erb, would look like the following (note the id="tabs" on the surrounding div):
<div id="tabs">
<ul>
<li>
Courses
</li>
<li>
Parts
</li>
<li>
Categories
</li>
</ul>
<div id="tabs-1">
<%= link_to "Courses", courses_path %>
</div>
<div id="tabs-2">
<%= link_to "Parts", parts_path %>
</div>
<div id="tabs-3">
<%= link_to "Categories", categories_path %>
</div>
</div>
Then in the appropriate view file, you can insert the partial with a call to render:
<%= render :partial => 'nav' %>
I've never used jQuery tabs but try adding this to application.js, based on this example. The "#tabs" selector inside $() corresponds to the id="tabs" from above:
$(function() {
$( "#tabs" ).tabs();
});
I'll offer a couple tips/reminders as well:
Embedded ruby (ERB) tags needs to have an = if you want them to print something, i.e. <%= #user.name %> versus <% #user.name %>. The first will output a string into the file, the second will not. There are cases where you might not want to print something, so you would leave off the =.
Be sure to close all your HTML tags. Rails' ERB files can get messy anyways, so proper HTML structure is key.
Avoid using HTML outside of *.html.erb files. This isn't a golden rule, but it's a good rule of thumb.

Rails Views: How do you pass a variable to a partial layout?

In one of my views I apply a layout to a block of code:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
#... code for sign in form here
<% end %>
The layout is a div that has png shadows on all four sides.
Since I use this layout all over my site, I want to pass a variable to the layout that specifies the width of the shadowed div. I tried using content for in the code block:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
<% content_for :box_width %>640<% end %>
#... code for sign in form here
<% end %>
# In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield %>
</div>
</div>
</div>
</div>
This didn't work and instead resulted in a double render of the entire code block.
What's the best way to tackle this problem?
Figured it out.
From the API: "You can also yield multiple times in one layout and use block arguments to differentiate the sections."
Solution:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do | section | %>
<%- case section when :box_width -%>
#width goes here. I.e., 640px
<%- when :content -%>
#code block goes here
<% end -%>
<% end %>
#In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield :content %>
</div>
</div>
</div>
</div>
First you need to know the difference between layouts and partials. Partials are generally from the view but can also be used from the controller if you are using ajax. Layouts are almost always used in the controller.
First create a file in a shared folder such as application/ and in this folder put a file call it whatever you want but it will contain the material that you want to include all over your site. Then when you pass a variable to a partial it's called in the partial as a local variable. Also with partials you don't need to say render :partial => you just put render 'application/some_file'
So from the view you want this:
<%= render 'application/your_file', :div_size => '600' %>
And then from the partial in the folder such as application/your_file.html.erb do this:
<div style="width:<%= div_width %>px;">
content
</div>

Resources