Layouts
Our HTML still isn’t quite valid: <html>
and <body>
tags are mandatory,
even though browsers still render our HTML happily without them.
We could add these wrapping tags to each and every one of our templates, every time we create a new one. However, that’s quite some repetition, and should we ever want to change anything about them (which is likely) we’d have to change all of our templates.
For building web applications it is handy to have “wrapping” templates. And that’s something Sinatra supports, too. They’re called “layout” templates.
Here’s how it works:
get '/monstas/:name' do
template = "<h1>Hello <%= name %></h1>"
layout = "<html><body><%= yield %></body></html>"
erb template, { :locals => params, :layout => layout }
end
Don’t forget to restart your application and refresh the page. If you inspect
the source code of the web page (right click on the page, and select “View Page
Source”, or whatever that’s called in your browser) you’ll see that it now has
the <html>
and <body>
tags from our layout template.
The only real change that we have made is that we’ve added the :layout
option
to the options hash. Because this is too much stuff to fit on one line we have
also sticked the template and layout into local variables.
As you can see the rendered “content” (from our main template) is being wrapped by, or inserted into, our layout template.
What about that yield
thing in our layout template though?
yield
is a keyword in Ruby that calls a block that was given to a method.
Whenever you pass a block to a method (such as each
, collect
, select
, and
so on) this method can then call the block by using the keyword yield
.
Because we’ve never implemented a method that took a block we’ve also never
discussed this keyword.
Hmmmmm. Ok, that’s how it works under the hood, yes. However, in this context,
all you need to remember is that, in a layout template, <%= yield %>
marks
the place where the other template (the one that is being wrapped) is supposed
to be inserted.
Does this make sense?
Imagine you have an application with, say, 10 get
routes. Each of them
renders a different template. Say, there’s one for the homepage, one for
a user signup page, one for a user profile page, and so on. Each of these
templates is supposed to be wrapped into the same layout, which has the
enclosing <html>
, <body>
, and other tags, which are all common to each
of these pages. Maybe there’s also a common header menu at the top, and a
common footer at the end of the page.
Each route will then render its own template, and specify the layout template
to be used, which will replace the <%= yield %>
tag with the template, and
wrap it.
That’s pretty handy.