What is the difference between render and yield in Rails - ruby-on-rails

Can someone explain the difference between "<%= render %>" and "<%= yield %> with <% content_for :partial do %>/<% end %>"? specifically how the routing changes when switching from one to another, the benefits of using one over the other, when is it practical to use one over the other. THIS is the closest explanation I have found, but isn't quite clear enough for me.
I have been trying for several days to wrap my head around this, but it seems that each configuration I try either comes close, or errors out.
If theres are three views, aaa and bbb and ccc, and each has an index.html.erb, but bbb and ccc have a _content.html.erb partial (signified by the underscore) how can you accomplish getting the bbb or ccc partial in aaa using either render or yield?
The following works:
aaa's index.html.erb :
<div">
<%= render 'bbb/content' %>
</div>
and bbbs _content.html/erb :
<p>Content from bbb.</p>
BUT this does NOT:
aaa's index.html.erb :
<div">
<%= yield :container %>
</div>
and bbbs _content.html/erb :
<% content_for :container do %>
<p>Content from bbb.</p> ### viewed in aaa
<% end>
and cccs _content.html.erb would have nothing, or the content_for, but I still dont get aaa's index.html to be populated with content.
If I use the render, I can explicitly place the content in. But I thought that the benefit of using the yield :whatever would allow me to choose what to populate it with, and I can't get it to populate anything as soon as I change it from render to yield. Do I also have to update the routes file? If so, how do I choose which one to populate it with? Does that mean its in the controller? and needs an action?
I also have though that it depends on which file is initially routed to, but like I said, I think I need to understand the difference between the two before I can begin to use the partials to my advantage.

First of all, yield is ruby, render is rails. Usually one uses a common layout for the application whose inner content changes according to action/context. The problem usually lies in defining where our layout ends and context-specific template begins. Take, for instance, the HTML title tag. Let's say you have an application called Cities. In most cases, you want your page title to be "Cities" all the time. But, if you're for instance, inside Amsterdam page, then you would like the have "Amsterdam" as your page title.
# application.html.erb
<html>
<head>
<%= content_for?(:page_title) ? yield(:page_title) : "Cities" %>
......
# city/index.html.erb
<% content_for :page_title do %>
<%= #city.name %>
<% end %>
<div class="bla"...
Within Rails you usually define your application title in your application layout. One strategy for changing the page title would be to use content_for in the specific cities template and change accordingly.
Render, on the other hand, accomplishes different rendering strategies. Straight. When you call render, it renders. content_for/yield doesn't render automatically, it is stored somewhere and then fills up the missing spots in the due places. So, you can think of it as more as a "store/search/replace" in comparison to render, which just plain renders.
Good rule of thumb to use one over the other is: if the template you are writing needs to present different information per context, strongly consider using content_for.

yield
Ruby code (Proc class) and takes your block and does what it is supposed to do with it. Yield is also fast compared with other Ruby based ways of doing the same thing.
I'd assume (and I only) use it in the layouts because it's quick and I mindlessly do what's normal in Rails. yield is also used to pass content to a specific spot in your layout. I often have <%= yield :head %> in the head, just above the head tag, so that I can pass random weirdness that sometimes comes up.
Common Uses:
Mostly just used in layouts
(if you are fancy/inclined to do so in a Model) as a true Ruby Proc
statement.
render
Rails code that you pass arguments to that, as the docs say, "Renders the content that will be returned to the browser as the response body". partials, actions, text, files...etc.
Common Uses:
Used in both views and the controller.

When your controller method exits, it renders the associated file. So the edit controller renders edit.html.erb. It uses the specified layout or application.html.erb if none is specified.
Within your layout file, when you call yield it will fill in the information from your render. If you call yield with a parameter, it will look for a content_for section in your render file matching that parameter. I'm not completely sure, but I don't think you can call yield from outside of your layout file, and I don't think it will fill in any information except that found in your render file.
Anywhere in your layout file or your rendered file, you can render a partial by calling render with the partial name minus the underscore.
I hope that helps.
Edit to answer question in comment:
yield and render perform similar functions however yield only looks in the render file whereas render specifies which file to render. Also, render outputs the entire file, but yield with a parameter can output just a subsection of the file.

Here's a visual to put them both in perspective:
The render method is called at the end of a controller action and orchestrates what block is passed to the method that is actually rendering the application.html.erb by yielding the passed block.
https://richstone.io/debunk/

Related

Using Rails helpers to render partials

From my understanding, helpers are mainly used to clean up views from some view-specific logic.
But on my currently new project (legacy application), I've stumbled upon a lot of helpers that look like this
def itemprepare
render :partial => 'items/itemlist_summary'
end
Is this correct? Rendering a partial to me seems like something you would want to do in the view, as it doesn't include any logic that needs to be abstracted.
Should I just inline all of these helpers?
Rendering a partial doesn't belong in a helper. Helpers should help you to do things that contain logic. Logic doesn't belong in the controller unless it's logic to render partials and decide if something should be displayed or not.
Although you generally shouldn't use helper methods to render partials, I can see how in some situations that might be necessary. For those circumstances, you need to use the concat method:
def itemprepare
concat(render(:partial => 'items/itemlist_summary'))
end
Like Ajedi32 says, partials use belongs to views but sometimes it's useful to use them in helpers. I hope it's useful to show what I've done in my application:
I've been following the excellent article Thinking of Rails Helper to help DRY our view. I'm using Jquery Mobile with a fixed header, nav-bar, navigation panel and a footer.
In every page I need to include the footer and the navigation panel, so usually it would have been:
<div data-role="footer">
<h4>Page Footer</h4>
</div><!-- /footer -->
<%= render "shared/nav_panel" %>
</div><!-- /page -->
at the end of each page.
Then I refactored the render partial into the application helper and now it is:
# app/helpers/application_helper.rb
def page_footer
footer = content_tag :div , :"data-role" => "footer" do
content_tag :h4, "Page Footer"
end
nav_panel = render(:partial => 'shared/nav_panel')
footer + nav_panel
end
and in the view I just call:
<%= page_footer %>
This is just a short example; in reality the app has a footer that changes according to the logged-in status, user language, etc..
We have a couple of helpers like that in our project, but most of them are in our custom gem. Wrapping partial rendering with helper prevents application from knowing how the information is rendered and we can easily extend logic, change partial, or do whatever we want inside this helper as long as it renders requested part of the view. Sometimes these partial require some data that resides inside the gem itself and there is no need to expose it to application. So application calls helper method (sometimes without any parameters at all) that forms required parameters and locals and passes them to partial.
But when you're just rendering partial inside your application and you don't need any extensive logic around that rendering I don't think there's much use from creating new helper for every partial.

Rails: What does it actually mean to "render a template"

I've become a bit confused about the idea of "rendering" a "template" due to the way an author speaks about it in a book I'm reading.
My original understanding of "rendering a template" was that it meant that Rails is providing the content that is viewed on the screen/presented to the viewer (in the way that a partial is rendered) but the book I'm reading seems to be using the concept of "rendering a template" to also mean something else. Let me explain in context
This book (rails 3 in action) sets up a page layout using the conventional layouts/application.html.erb file, and then it "yields" to different view pages, such as views/tickets/show.html.erb which adds more content to the screen. that's all straightforward..
Within this view views/tickets/show.html.erb, there is a rendering of a partial (which is also a straightforward concept).
<div id='tags'><%= render #ticket.tags %></div>
Now within this partial there is, using ajax, a call to a "remove" method in the "tags_controller.rb" which is designed to allow authorized users to remove a "tag" from a "ticket" in our mock project management application.
<% if can?(:tag, #ticket.project) || current_user.admin? %>
<%= link_to "x", remove_ticket_tag_path(#ticket, tag),
:remote => true,
:method => :delete,
:html => { :id => "delete-#{tag.name.parameterize}" } %>
<% end %>
Now here is the "remove" action in the tags controller (which disassociates the tag from the ticket in the database)...
def remove
#ticket = Ticket.find(params[:ticket_id])
if can?(:tag, #ticket.project) || current_user.admin?
#tag = Tag.find(params[:id])
#ticket.tags -= [#tag]
#ticket.save
end
end
end
At the end of this remove action, the author originally included render :nothing => true , but then he revised the action because, as he says, "you’re going to get it to render a template." Here's where I get confused
The template that he gets this action to render is "remove.js.erb", which only has one line of jquery inside it, whose purpose is to remove the "tag" from the page (i.e. the tag that the user sees on the screen) now that it has been disassociated from the ticket in the database.
$('#tag-<%= #tag.name.parameterize %>').remove();
When I read "rendering a template" I expect the application to be inserting content into the page, but the template rendered by the "remove" action in the controller only calls a jquery function that removes one element from the page.
If a "template" is "rendered", I'm expecting another template to be removed (in order to make room for the new template), or I'm expecting content to be "rendered" in the way that a partial is rendered. Can you clarify what is actually happening when a "template" is "rendered" in the situation with the jquery in this question? Is it actually putting a new page in front of the user (I expected some sort of physical page to be rendered)
You're nearly there! Rendering a template is indeed always about producing content, but for a slightly wider description of content. It could be a chunk of html, for example an ajax call to get new items might produce some html describing the new items, but it doesn't have to be.
A template might produce javascript as it does in your second example. Personally I am trying to avoid this and instead pass JSON back to the client and let the client side js perform the required work.
Another type of rendering you might perform is to produce some JSON. APIs will often do this, but you might also do this on a normal page. For example rather than rendering some javascript to delete tag x you might render the json
{ to_delete: "tag-123"}
and then have your jQuery success callback use that payload to know which element to remove from the DOM, by having this in your application.js file
$('a.delete_tag').live('ajax:success', function(data){
var selector = '#' + data.to_delete;
$(selector).remove()
}
(Assuming that your delete links had the class 'delete_tag')
Rendering JSON like this isn't really a template at all, since you'd usually do this via
render :json => {:to_delete => "tag-#{#tag.name.parameterize}"}
although I suppose you could use an erb template for this (I can't imagine why though).
My understanding is that js.erb is "rendered" by executing the javascript functions within it. Very often something like the below is done:
jQuery(document).ready(function() {
jQuery('#element').html('<%= escape_javascript(render pages/content) %>');
});
There's a really succinct overview of rendering at http://guides.rubyonrails.org/layouts_and_rendering.html that may help as it also goes into the details of the ActionController::Base#render method and what happens behind the scenes when you use render :nothing (for example). Render but can be used for files or inline code as well -- not just 'templates' in the traditional sense.

New to RoR, confused about where to put markup

I'm very new to RoR, moving from PHP. I have what I hope is a simple question.
Say I have a Model called Article, stored in the table articles.
If I want to display a list of articles on several pages in my site, I might have some simple markup such as;
<% Article.find_each do |article| %>
<div class="article_list_wrapper">
<h1><%= article.title %></h1>
<p><%= article.body %></p>
</div>
<% end %>
Where would I put this markup? I mean in what file; if I put it in a view, then I will have to copy it if I want a similar list in a different part of the site.
Putting it in the Model itself seems intuitive to me, the Article object should know how to render itself, right? But that seems totally against the CMV idea.
And what if I wanted to have several ways of rendering the same object? Like a list view, a full page view etc.
Many thanks, just trying to get into the correct habits from the start.
You would put that portion of HTML markup into what is known as a partial -- something that can be rendered as a small piece of a larger layout. You're right, you wouldn't want to store the format in the model, because you might wish to render that specific HTML for web browsers and render very similar XML or JSON for clients using an API and render some other very similar content into ASN.1 or YAML for further other clients. The "view" really should stay associated with the views.
With this specific example, you would probably go one further and provide the articles to render via a variable rather than letting the view find the articles itself:
Controller:
def flubber:
#articles = Article.find_each(some_criteria)
end
View:
<div class="article_list_wrapper">
<%= render :partial => "article_list", :collection => #articles %>
</div>
And then provide a View for the corresponding controller with the name _article_list:
<h1><%= article.title %></h1>
<p><%= article.body %></p>
If the <div class="..."> is going to be identical in every single use of this partial, you could either pass a :template to the partial, or put it into a view helper, or put the entire render with the :template argument in a view helper. (Either the first or last approach here is what I'd do -- the <div class="..."> doesn't seem so important to require its own file, but if it were more complicated, it might -- and then I wouldn't want to give the partial :layout argument every time I needed it.)
MVC (Model-View-Controller):
The Model has (primarily) the database piece and business logic.
The Controller controls application flow.
The View shows the info, e.g. the browser view.
So the markup you show which has a bunch of HTML goes in the view.
That is app/models/views/article.rb
The Controller is the part that gets the info.
You should actually have #article = Article.all in your controller and then just use #article.each instead of Article.find_each in your view.
If you have conditions you can apply them directly on the Controller find but the better approach is to learn about how you can use model finders with scopes and then use those scopes from the controller. Always try and push things up from View to Controller to View and follow the 'fat Model, thin Controller' approach when practical.
For reuse partials can help but as better understanding of rails will help more.
See railscasts by Ryan Bates. Incredible tutorials.
You might also like my own bookmarks for rails at my (rails) linker app.
You can use partials for this.

Rails partials with single-table inheritance

I want to use partials in rails along with single-table inheritance. I currently have this working:
render partial: #vehicle
# which renders the relevant view, depending on object type, eg:
# views/trucks/_truck.haml
# views/car/_car.haml
I want to leave these default views in place, and create an additional compact view for each object, perhaps like this
# example code only, I want to write something like:
render partial: 'compact', locals: {vehicle: #vehicle}
# and then have this render (for example) with
# views/trucks/_compact.haml
# views/car/_compact.haml
I can happily rename things or change the file names or locations, but what is the simplest way to support two kinds of views (compact and default)?
There will be many more classes later, so looking for very clean, elegant code.
(rails 3.0.5+ on ruby 1.9.2)
To get exactly what you asked for you should do this:
render partial: "#{#vehicle.type.tableize}/#{#vehicle.type.underscore}", object: #vehicle
and you will get rendered:
views/trucks/_truck.html.haml
and the object will be accessible as:
#truck
There might be a better way, but there is always this approach:
render partial: "#{#vehicle.class.to_s.tableize}/compact", locals:{vehicle: #vehicle}
(or it might need to be _compact, instead of just compact, but you get the idea)
I've done something similar, but rather than having the partials in two separate files, I've combined them, and used the locals argument to pass a flag:
# The hash syntax for render is redundant, you can simply pass your instance
# Render the long-form of your partial
render #vehicle
# When using render _instance_, the second argument becomes the locals declaration
# Render the compact form
render #vehicle, compact: true
And then, in my partial...
<% if defined? compact %>
<!-- HTML for compact view -->
<% else %>
<!-- HTML for extended view -->
<% end %>
The advantages of this approach are that you're only maintaining one partial file for each vehicle type, and your code remains pristine.
The disadvantage is that it's a slight departure from the "traditional" usage of partials.

How do I implement Section-specific navigation in Ruby on Rails?

I have a Ruby/Rails app that has two or three main "sections". When a user visits that section, I wish to display some sub-navigation. All three sections use the same layout, so I can't "hard code" the navigation into the layout.
I can think of a few different methods to do this. I guess in order to help people vote I'll put them as answers.
Any other ideas? Or what do you vote for?
You can easily do this using partials, assuming each section has it's own controller.
Let's say you have three sections called Posts, Users and Admin, each with it's own controller: PostsController, UsersController and AdminController.
In each corresponding views directory, you declare a _subnav.html.erb partial:
/app/views/users/_subnav.html.erb
/app/views/posts/_subnav.html.erb
/app/views/admin/_subnav.html.erb
In each of these subnav partials you declare the options specific to that section, so /users/_subnav.html.erb might contain:
<ul id="subnav">
<li><%= link_to 'All Users', users_path %></li>
<li><%= link_to 'New User', new_user_path %></li>
</ul>
Whilst /posts/_subnav.html.erb might contain:
<ul id="subnav">
<li><%= link_to 'All Posts', posts_path %></li>
<li><%= link_to 'New Post', new_post_path %></li>
</ul>
Finally, once you've done this, you just need to include the subnav partial in the layout:
<div id="header">...</div>
<%= render :partial => "subnav" %>
<div id="content"><%= yield %></div>
<div id="footer">...</div>
Partial render. This is very similar to the helper method except perhaps the layout would have some if statements, or pass that off to a helper...
As for the content of your submenus, you can go at it in a declarative manner in each controller.
class PostsController < ApplicationController
#...
protected
helper_method :menu_items
def menu_items
[
['Submenu 1', url_for(me)],
['Submenu 2', url_for(you)]
]
end
end
Now whenever you call menu_items from a view, you'll have the right list to iterate over for the specific controller.
This strikes me as a cleaner solution than putting this logic inside view templates.
Note that you may also want to declare a default (empty?) menu_items inside ApplicationController as well.
Warning: Advanced Tricks ahead!
Render them all. Hide the ones that you don't need using CSS/Javascript, which can be trivially initialized in any number of ways. (Javascript can read the URL used, query parameters, something in a cookie, etc etc.) This has the advantage of potentially playing much better with your cache (why cache three views and then have to expire them all simultaneously when you can cache one?), and can be used to present a better user experience.
For example, let's pretend you have a common tab bar interface with sub navigation. If you render the content of all three tabs (i.e. its written in the HTML) and hide two of them, switching between two tabs is trivial Javascript and doesn't even hit your server. Big win! No latency for the user. No server load for you.
Want another big win? You can use a variation on this technique to cheat on pages which might but 99% common across users but still contain user state. For example, you might have a front page of a site which is relatively common across all users but say "Hiya Bob" when they're logged in. Put the non-common part ("Hiya, Bob") in a cookie. Have that part of the page be read in via Javascript reading the cookie. Cache the entire page for all users regardless of login status in page caching. This is literally capable of slicing 70% of the accesses off from the entire Rails stack on some sites.
Who cares if Rails can scale or not when your site is really Nginx serving static assets with new HTML pages occasionally getting delivered by some Ruby running on every thousandth access or so ;)
You could use something like the navigation plugin at http://rpheath.com/posts/309-rails-plugin-navigation-helper
It doesn't do sub-section navigation out of the box, but with a little tweaking you could probably set it up to do something similar.
I suggest you use partials. There are a few ways you can go about it. When I create partials that are a bit picky in that they need specific variables, I also create a helper method for it.
module RenderHelper
#options: a nested array of menu names and their corresponding url
def render_submenu(menu_items=[[]])
render :partial => 'shared/submenu', :locals => {:menu_items => menu_items}
end
end
Now the partial has a local variable named menu_items over which you can iterate to create your submenu. Note that I suggest a nested array instead of a hash because a hash's order is unpredictable.
Note that the logic deciding what items should be displayed in the menu could also be inside render_submenu if that makes more sense to you.
I asked pretty much the same question myself: Need advice: Structure of Rails views for submenus? The best solution was probably to use partials.
There is another possible way to do this: Nested Layouts
i don't remember where i found this code so apologies to the original author.
create a file called nested_layouts.rb in your lib folder and include the following code:
module NestedLayouts
def render(options = nil, &block)
if options
if options[:layout].is_a?(Array)
layouts = options.delete(:layout)
options[:layout] = layouts.pop
inner_layout = layouts.shift
options[:text] = layouts.inject(render_to_string(options.merge({:layout=>inner_layout}))) do |output,layout|
render_to_string(options.merge({:text => output, :layout => layout}))
end
end
end
super
end
end
then, create your various layouts in the layouts folder, (for example 'admin.rhtml' and 'application.rhtml').
Now in your controllers add this just inside the class:
include NestedLayouts
And finally at the end of your actions do this:
def show
...
render :layout => ['admin','application']
end
the order of the layouts in the array is important. The admin layout will be rendered inside the application layout wherever the 'yeild' is.
this method can work really well depending on the design of the site and how the various elements are organized. for instance one of the included layouts could just contain a series of divs that contain the content that needs to be shown for a particular action, and the CSS on a higher layout could control where they are positioned.
There are few approaches to this problem.
You might want to use different layouts for each section.
You might want to use a partial included by all views in a given directory.
You might want to use content_for that is filled by either a view or a partial, and called in the global layout, if you have one.
Personally I believe that you should avoid more abstraction in this case.

Resources