If you are like me, you avoid creating page titles because it is kind of a pain. But in this episode I will show you a clean way to add titles to your pages.
Great screencast! I took your idea one step further and had my title helper return either the set title or a default if one isn't already set when called with no arguments. That way I can call the helper to set the title, or call it w/ no arguments when I want to use the title.
Yep, Zach's correct. Ruby gets confused when you don't wrap the :title parameter in parentheses so it spits out a syntax error. I'm guessing it's because it thinks the {} brackets are trying to represent a hash not a block.
Same goes for later on in the video where I use parenthesis when calling yield(:title) while adding the || symbol. This is because Ruby doesn't know where to put the parenthesis, it could be here:
yield(:title || "foo")
or here:
yield(:title) || "foo"
It defaults to the first one (IIRC) so we need to clarify.
Whenever there's any doubt, wrap the parameters in parenthesis.
@Pimpmaster, really? I've used non double quoted strings before. What kind of variable is @article.title? If it's not a string, that may be why. You may want to do the "to_s" conversion inside the title helper method.
Hey Ryan, love the screencasts. Quick question... I noticed in this one that as you typed keyboard shortcuts they were displayed in the upper right corner of the screen. Just wondering how you did that. Is this a feature of Textmate?
Hi Ryan,
I would have a suggestion for a screencast.
How do you debug? With ruby-debug or breakpointer?
What is the better choice? There are topics, that breakpointer is deprecated, isn't it?
May this would be a topic for a Railscast
I used to use breakpointer a lot, but the recent Ruby releases broke it, so I'm currently getting by with just echoing variables to the log/view. I really need to learn ruby-debug, and once I do you can expect there to be an episode on it. Thanks for the suggestion.
It's the 13th! Do you know what that means?!?! Time for a new screencast! Whereisit whereisit whereisit....
Great screencast as always Ryan, I actually find myself enjoying yours more then peepcode, so be sure that I'm going to convert the money I would have spent on their screencasts into donation money for yourself, great work!
I see the title as view logic. The reason is because if a different controller action renders the same view, it doesn't have to set the title again.
For example, on the "new.rhtml" page you don't want to have to set the title in both the "new" action and the "create" action in the controller (in case the validation fails and the "new.rhtml" view is rendered again).
Now, if the title is complex obviously you don't want to put all that logic in the view. In that case it should be moved into a helper method.
I suppose this is personal preference, but I find placing it in the view is better.
I've went a bit further: I have two helpers, access_denied() and action_denied() that put appropriate output; they now both have the title() call in them to set the title correctly, which is really nice. :)
I was setting @page_title in my controllers until I saw this screencast... thanks, Ryan!
I like the idea of using content_for, but I'm not sure I can perform some logic on it like I can with an instance variable. I'm using the title method in application_helper.rb, only setting an instance variable instead of content_for.
In my application.html.erb, I do:
<title>
<% if @page_title.nil? -%>
example.com | an example site
<% else -%>
<%= @page_title %> | example.com
<% end -%>
</title>
@Tim, yep, actually content_for is just setting an instance variable in the background. I believe it's called @content_for_title (or whatever you pass to the method). You can reference this instance variable directly if you need, or just stick with yield(:title) which basically does the same thing.
I like this method a lot - I just made one small change:
content_for(:title) { h(page_title) }
Adding the "h" method right to the helper ensures that no matter where I display it, it's safe. I often yield that title in a few other places, like in an h2 element somewhere on the page itself.
As a curious aside, the trick I like best here doesn't seem to be working for me in a new Rails 3 app:
yield(:title) || 'some default'
no longer works because content_for returns a blank string when there's nothing for it to do for the particular symbol. From the debugger:
(rdb:1) eval content_for(:title)
""
I'm not quite sure how to code up a better hack. The following came to mind:
%title= yield_or_default(:title, 'some default')
but then you end up trying to call yield in your helper method on a symbol and not a block and it just freaks out, because that's not your layout's yield!
def yield_or_default(symbol, default)
output = yield(symbol) # blarf!
output = default if output.blank?
output
end
for some reason content for and yield did not work for me on rails3.0.1... May you enlight me why you prefer yield over instance variables?
this is how I adopted your approach, I like it because its DRY yet very flexible.
module ApplicationHelper
def title(page_title, element = "h1")
@page_title = page_title
raw('<' + element + '>' + page_title + '</' + element + '>')
end
end
Actually the default value for the title doesn't work for me (ruby 1.8.7 rails 3.0.3) because yield(:title) is not nil but blank, easy to solve
<%= yield(:title).blank? ? 'default' : yield(:title) %>
When you have time, could you include Ben Greene's helper (or some similar snippet) in your code post? It would save Rails 3 users a lot of time hunting down why their code displays as "Shoppery - - Mozilla Firefox" in the title
Used @page_title all the time :)
This screencast is useful for me. Can you create screencast about using HTML Tidy in Ruby on Rails? Thank you!
Great screencast! I took your idea one step further and had my title helper return either the set title or a default if one isn't already set when called with no arguments. That way I can call the helper to set the title, or call it w/ no arguments when I want to use the title.
Great idea Jake. I normally don't like giving methods a dual purpose, but in this case I think it works very well.
Hi! Could you make a cast for i18n and l10n? would be nice!
thanks! congratulations!
I was wondering why..
content_for(:title) { page_title }
works fine but
content_for :title { page_title }
doesn't - it gives a 500 error. Sorry.. I'm new to Rails/Ruby.
Another great tutorial Ryan!
Rob,
He says so during the screencast. It's due to the syntax reading past :title, I believe.
So use it with brackets for now.
Yep, Zach's correct. Ruby gets confused when you don't wrap the :title parameter in parentheses so it spits out a syntax error. I'm guessing it's because it thinks the {} brackets are trying to represent a hash not a block.
Same goes for later on in the video where I use parenthesis when calling yield(:title) while adding the || symbol. This is because Ruby doesn't know where to put the parenthesis, it could be here:
yield(:title || "foo")
or here:
yield(:title) || "foo"
It defaults to the first one (IIRC) so we need to clarify.
Whenever there's any doubt, wrap the parameters in parenthesis.
This is one of my favorite screencasts. I just redid all my views and they sure look purty!
Cheers!
another great screencast! After every casts I have to go through all my code and change/implement what I've just learned ;-)
I'd like to see more casts where you use helper methods, cause I think they are useful but I don't know when...
cheers
I just realized one caveat here. This particulat helper only takes a double-quoted string, so something like:
<%= title @article.title %>
Does not work because you will explicitly have to set it to a string
<%= title @article.title.to_s %>
This is not the case if you use the classic instance variable solution.
Woops! Ignore those = on the ERB tags.
@Pimpmaster, really? I've used non double quoted strings before. What kind of variable is @article.title? If it's not a string, that may be why. You may want to do the "to_s" conversion inside the title helper method.
Hi
thanks a lot for your work. Thats really what I was looking for, made a !little! donation today, hope that I can continue to donate :-D
Hopefully you have a lot of fun by doing that and you continue making such great "snippets".
greetinx
Thanks for donating Rafael! It really helps. I enjoy making the screencasts as well.
Hey Ryan, love the screencasts. Quick question... I noticed in this one that as you typed keyboard shortcuts they were displayed in the upper right corner of the screen. Just wondering how you did that. Is this a feature of Textmate?
I'm displaying the key commands with a little utility called KeyCastr.
http://stephendeken.net/software/keycastr/
Hi Ryan,
I would have a suggestion for a screencast.
How do you debug? With ruby-debug or breakpointer?
What is the better choice? There are topics, that breakpointer is deprecated, isn't it?
May this would be a topic for a Railscast
greetinx
I used to use breakpointer a lot, but the recent Ruby releases broke it, so I'm currently getting by with just echoing variables to the log/view. I really need to learn ruby-debug, and once I do you can expect there to be an episode on it. Thanks for the suggestion.
It's the 13th! Do you know what that means?!?! Time for a new screencast! Whereisit whereisit whereisit....
Great screencast as always Ryan, I actually find myself enjoying yours more then peepcode, so be sure that I'm going to convert the money I would have spent on their screencasts into donation money for yourself, great work!
I do:
<title><%= h @page_title || "Set title in: views/#{params[:controller]}/#{params[:action]}.rhtml" %></title>
why dont you set the title in the controller so it can be used by several views?
I agree with foobar, seting title in view is kind of wrong, logic should go in controller
I see the title as view logic. The reason is because if a different controller action renders the same view, it doesn't have to set the title again.
For example, on the "new.rhtml" page you don't want to have to set the title in both the "new" action and the "create" action in the controller (in case the validation fails and the "new.rhtml" view is rendered again).
Now, if the title is complex obviously you don't want to put all that logic in the view. In that case it should be moved into a helper method.
I suppose this is personal preference, but I find placing it in the view is better.
Great site. This could probably have the refactoring tag added t it.
I've went a bit further: I have two helpers, access_denied() and action_denied() that put appropriate output; they now both have the title() call in them to set the title correctly, which is really nice. :)
I was setting @page_title in my controllers until I saw this screencast... thanks, Ryan!
I like the idea of using content_for, but I'm not sure I can perform some logic on it like I can with an instance variable. I'm using the title method in application_helper.rb, only setting an instance variable instead of content_for.
In my application.html.erb, I do:
<title>
<% if @page_title.nil? -%>
example.com | an example site
<% else -%>
<%= @page_title %> | example.com
<% end -%>
</title>
Can the same effect be achieved with content_for?
@Tim, yep, actually content_for is just setting an instance variable in the background. I believe it's called @content_for_title (or whatever you pass to the method). You can reference this instance variable directly if you need, or just stick with yield(:title) which basically does the same thing.
I like this method a lot - I just made one small change:
content_for(:title) { h(page_title) }
Adding the "h" method right to the helper ensures that no matter where I display it, it's safe. I often yield that title in a few other places, like in an h2 element somewhere on the page itself.
@Jeff Dean, Thanks for the tip about the "h" method. I was getting XHTML warnings about my titles before and that cleaned them right up.
@Ryan Bates, I think I have learned more about Ruby and Ruby on Rails from you than anyone on the planet. Thanks!
Very useful tip, thanks!
You could use some valid defaults for a large site too:
controller.action_name.titlecase
controller.controller_name.titlecase
"Show Directories"
"Edit Directories"
etc. etc.
Thanks for this! Great help.
I found myself in one application I am working on repeating the page title in the title block but also in the h1 element.
So I modified it slightly to help me with this: http://pastie.org/527089
hi Ryan,
plz have a look at plugin - http://github.com/kpumuk/meta-tags/tree/master, that uses idea from your railscast. Perfect thing :)
As a curious aside, the trick I like best here doesn't seem to be working for me in a new Rails 3 app:
yield(:title) || 'some default'
no longer works because content_for returns a blank string when there's nothing for it to do for the particular symbol. From the debugger:
(rdb:1) eval content_for(:title)
""
I'm not quite sure how to code up a better hack. The following came to mind:
%title= yield_or_default(:title, 'some default')
but then you end up trying to call yield in your helper method on a symbol and not a block and it just freaks out, because that's not your layout's yield!
def yield_or_default(symbol, default)
output = yield(symbol) # blarf!
output = default if output.blank?
output
end
Come up with a decent work-around, here's a blog post explaining it:
http://www.zetetic.net/blog/2010/05/18/pretty-page-title-in-rails-3
And here's a straight gist:
http://gist.github.com/405129
Thanks again for all the Railscasts, Ryan!
Billy
This doesn't work in Rails 3. Try this:
<title><%= content_for?(:title) ? yield(:title) : '0to255' %></title>
for some reason content for and yield did not work for me on rails3.0.1... May you enlight me why you prefer yield over instance variables?
this is how I adopted your approach, I like it because its DRY yet very flexible.
module ApplicationHelper
def title(page_title, element = "h1")
@page_title = page_title
raw('<' + element + '>' + page_title + '</' + element + '>')
end
end
<title>Web App Title<%= raw((@page_title.blank? ? ' — Default View Title' : ' — ' + @page_title))%></title
How would one go about doing this with HAML?
Thanks once again Ryan - very professional!
Actually the default value for the title doesn't work for me (ruby 1.8.7 rails 3.0.3) because yield(:title) is not nil but blank, easy to solve
<%= yield(:title).blank? ? 'default' : yield(:title) %>
but still odd...
In HAML/Rails3...
%title<
Shopper - #{ content_for?(:title) ? yield(:title) : 'The Place to Buy Stuff' }
As some have noted,
yield(:title) || "Default"
no longer works in Rails 3. I recommend adding this method to a Helper:
def yield_or_default(section, default = "")
content_for?(section) ? yield(section) : default
end
and then calling
yield_or_default(:title, "Default")
Thanks for the great work, Ryan.
--Ben
Thanks for all the Railscasts, Ryan!
When you have time, could you include Ben Greene's helper (or some similar snippet) in your code post? It would save Rails 3 users a lot of time hunting down why their code displays as "Shoppery - - Mozilla Firefox" in the title
A bit annoying, but yield does not work from helpers. Taken from the API docs:
`Note: yield can still be used to retrieve the stored content, but calling yield doesn
Great idea! Thanks a lot Ryan!!! Before I put header global variable in a controller (((
For layout:
and view:
It doesn't work on rails 3.1.1
Solve:
<title><%= content_for?(:title) ? yield(:title) + " - Company" : "Company" %></title>
This works well if you want to add the company name at the end of the title string instead of in the beginning like the video shows.
This episode has been updated for Rails 5 as a blog post. Pretty Page Title in Rails 5