Is <%= yield %> the Rails equivalent of MVC3's RenderContent()? - ruby-on-rails

I'm following the Ruby of Rails getting started guide, and I see this code in the layout file:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body style="background: #EEEEEE;">
<%= yield %>
</body>
</html>
Coming from an MVC3 background, is this the equivalent to the RenderContent() method one would invoke from the _layout.cshtml file?

The functionality is about the same in that context, yes. However, yield in general is a keyword in the ruby language, concerning blocks. You can find more information here: ruby blocks.
Building on that, you are able to provide content for different parts, using content_for(:something) and yield :something (the yield passes :something to the layout engine, the layout engine fills in the content for it).

Related

How would I display different headers in Rails in application.html.erb?

How would I display different versions of headers in Rails in my application.html.erb view? For instance, for my landing page, I have a pretty big header that includes a callout and some sign-up and sign-in buttons. For other static pages, I have a normal small header with a logo and a few links to the right. And for my dashboard when users log in, it will look a little different as well.
How can I display certain headers in an if/else statement where Rails will output the different versions of headers based on the current url or view in my application.html.erb?
To answer your question with an example this is what you may want to do.
Rails has a provision to use nested view templates using the content_for and yield tags.
Do the following thing to achieve what you want -
In app/views/layouts/application.html.erb - Add a ternary expression which acts as a if else. While rendering the views rails will look for a <% content_for :custom_header do %> in the view templates. If it doesn't find that it will render the partial app/views/layouts/_default_header.html.erb instead.
<html>
<head>
<title><%= #page_title or "Page Title" %></title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<!-- Code to conditionally load a header -->
<%= content_for?(:custom_header) ? yield(:custom_header) : render :partial => "layouts/default_header" %></div>
<!-- For the body -->
<%= yield %>
</body>
</html>
Now since you say that most pages will have a static small header, save the html code for that in a partial under app/views/layouts/_default_header.html.erb.
In you landing page (for example the the view app/views/welcome/index.html.erb) you can use the content_for tag with the identifier :custom_header that we have used in the application.html.erb
Add the following in your landing page.
<% content_for :custom_header do %>
<div class="custom-header">
<!-- Your complex header with sign in and signup links... -->
</div>
<% end %>
The ternary operator in the application.html.erb will pick up this content from the content_for tag in the landing page and insert the content in place of the yield(:custom_header).
Hope this helps.
It sounds like you may want to use a nested layout.

Undefined method ` yield' - Rails

I am getting this error:
undefined method ` yield' for #<#<Class:0x007fccb4710880>:0x007fccb43f0308>
when attempting to render page specific stylesheets using the content_for helper in the application layout.
My index.html.erb template code contains:
<% content_for :head do %>
<%= stylesheet_link_tag "inspection.css" %>
<% end %>
My application.html.erb code contains:
<!DOCTYPE html>
<html>
<head>
<title>Plastics</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
<%= yield :head %>
</head>
<body>
<div id="wrapper">
<%= yield %>
</div>
</body>
</html>
I am running Rails 3.2.15 on Ruby 2.0.0-p195
I have tried switching to Ruby 1.9.3-p429 and other versions of rails including 3.2.12, 3.2.13,..with no luck
I have used this many times in other applications without any issue.
I assume you've copied and pasted the "undefined method" error directly from your terminal. There appears to be a space between the open-quote and the word yield, which makes me think that Ruby is attempting to call a method with a name that begins with a space.
Ordinarily, that's not easy to do, but you can force it using send, as a demonstration:
Object.new.send(' yield')
# NoMethodError: undefined method ` yield' for #<Object:0x007fc413258968>
So: given the way you've written your layout, why is Ruby/ERb including a space at the beginning of what it thinks is the name of the method?
My guess is that there's some kind of rogue invisible character in there that's throwing off the parsing. Try retyping the two lines that include yield statements and see if it starts working.

Is the html structure necessary in an .erb file in Ruby on Rails?

I follow the Ruby on Rails tutorial by Michael Hartl and I'm stuck with an example that is not working for me.
I try to remove duplicate code from .erb files so that the code exists only in the application.html.erb file. With the old home.html.erb file everything works out well (when I do a GET for home the content is shown), but with the one I am supposed to use to eliminate duplicate code, no content is shown. After testing, I found out that even removing the title tag from the old file is enough to make the content dissapear.
Any ideas why that is happening? Is the tutorial wrong or did I miss something?
application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>Title | <%= yield(:title) %></title>
<title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
home.html.erb
<% provide(:title, 'Home') %>
<h1>Sample App</h1>
<p>
This is the home page for the
Ruby on Rails Tutorial
sample application.
</p>
old home.html.erb:
<% provide(:title, 'Home') %>
<!DOCTYPE html>
<html>
<head>
<title>Title | <%= yield(:title) %></title>
</head>
<body>
<h1>Sample App</h1>
<p>
This is the home page for the
Ruby on Rails Tutorial
sample application.
</p>
</body>
</html>
The problem with your layout file is that you have two <title> tags, one containing the title of your application, and one left open. If you remove the one left open, the problem will be resolved.
You should fill in the application layout in the controller. In other words, add layout 'application' to your controller. For example:
class StaticPagesController < ApplicationController
layout 'application'
def home
end
def help
end
def about
end
end

JavaScript is secretly running on my Rails application

My only line in the routes file is this:
root :to => 'spikes#index'
In the Javascript assets I have a simple function like this:
spike.js file:
$(function(){
alert("WAT");
document.write("abc");
});
In the Views->spikes->index.html.haml file I have NOTHING! it is empty.
I used to have the following code, but I removed it:
= javascript_include_tag 'spike'
But still when I run my Rails app, I see an alert and "abc" written on the browser.
Where is it calling it from? What on earth!?!
UPDATE: This is also my application.html.erb file:
<!DOCTYPE html>
<html>
<head>
<title>D3Spike</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
You probably have a <script> tag in app/views/layouts/application.html.erb (or maybe .haml, not .erb), which wraps all your pages. If you take that out for a second, you'll probably see the alert go away.
A couple things to understand: 1) the JavaScript code you showed us runs just by virtue of being included (paste it into a console in your browser to see what I mean) and 2) in Rails, the normal behavior is for all your JavaScript to be included all the time, which is perhaps not what you expected.

Yield layout in rails

I'm trying to add a layout to the application layout. I having trouble trying to figure out all the different layout solutions. First I tried just a layout inside a layout because I didn't fully grasp what partials are or if they are layouts also? Maybe I can start with that question. What is the difference between a layout and a partial.
Here is what I have right now. I'm trying to just separate out my header code which has a navigation and some other elements into a separate layout. I want this layout to be on all views. Meaning it should be a layout inside the applications layout along with other views that are been called when their controllers are called. Which is covered in my code with the <%= yield %>. That yeild works but the :header one does not.
Application Layout app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>home</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<div id="header"><%= yield(:header) %></div>
<%= yield %>
</body>
</html>
Header layout app/views/layouts/application.html.erb
<% content_for :header %>
<p>HEADER TEXT</p>
<% end %>
Why would the code above not work?
I also saw code like this that I tried but it gave me an error.
<%= render layouts/header %>
Can someone please explain all these different methods.
Thanks.
It's good practice to separate your header and footer into partials which you would live in the views/layouts folder as '_header.html.erb' and '_footer.html.erb' respectively.
You can then optionally wrap each partial with specific div's which is what you're trying to do with the header (you could do the same with the body too), and it would end up looking like this:
<!DOCTYPE html>
<html>
<head>
<title>home</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<div id="header">
<%= render 'layouts/header' %>
</div>
<%= yield %>
<div id="footer">
<%= render 'layouts/footer' %>
</div>
</body>
</html>
That will do what you want it to do.
Note that 'layouts/header' and 'layouts/footer' have '' around them.
To answer your question on what is the difference between a layout and a partial, well a layout is something that will used throughout your application, such as a consistent header or footer. A partial can be a layout, but it doesn't have to be, so you can partial specific to other views across your site.
<% content_for :header do %>
<p>HEADER TEXT</p>
<% end %>
You forgot the do
So - first - The yield syntax may or may not be correct, but I've never seen it, and I don't like it. I only ever call yield once in a file.
If you want to render a header in your application template file, that's certainly possible - my advice would be to place it in your template file directly - after all, that's what template files are for. If you want to completely encapsulate your header for some reason or another in seperate files, what you need is partials. You're going to do something like this:
<body>
<%= render :partial => "shared/header" %>
<%= yield %>
</body>
Which will render your header content, stored in /shared/_header.html.erb into the layout here.
Check out this guide here for more info

Resources