How to structure a layout template in Haml
This solution is for Ruby on Rails only:
You can use yield(:location)
and the content_for(:location)
methods. "Using the content_for
Method" has more information.
layout.haml:
!!!
%html
%head
%title= yield(:title)
= yield(:head)
%body
= yield
view.haml:
- content_for(:title, 'My title')
- content_for(:head) do
= javascript_include_tag :foo
%h1 My view!
Using layouts in HAML files independently of Rails
You're mixing up two distinct Rails feature: partials (using render
) and layouts (using yield
).
You can add a rails-like version of either (or both) of them to a Haml only program.
Partials
In a rails view, you can use render :partial_name
to cause the file _partial_name.html.haml
to be rendered at that point in the containing view (actually Rails allows you to use any templating language supported and it will find to correct filename extension to use, but I'll stick to just Haml here). Outside of Rails render
isn't available, but it can be added fairly easily.
A simple render
method would just find the appropriate haml file, render it, and return the html string for inclusion in the parent:
def render(partial)
# assuming we want to keep the rails practice of prefixing file names
# of partials with "_"
Haml::Engine.new(File.read("_#{partial}.html.haml")).render
end
The first argument to Haml::Engine.render
is a scope object, which we can use to add methods available inside the haml template. It defaults to Object.new
. In a simple case like this, however, we can define the render
method in the top level, and it will be available in the scope of the Haml template. We simply put our render
method in the script before the call to Haml::Engine.new(...).render
, and call it like this in our template:
!!!
%html
%head
%title Hello
%body
=render :the_partial
Now the file _the_partial.html.haml
will appear rendered at the appropriate point of the output.
Local variables
We can take this a step further. Rails allows you to pass in a hash of local variables to a partial. Haml will also accept a hash of variables to be passed as local variables, as the second argument to the Haml render
method. So if we expand our render method to look like:
def render(partial, locals = {})
Haml::Engine.new(File.read("_#{partial}.html.haml")).render(Object.new, locals)
end
we can use a partial that looks something like:
%p You passed in #{foo}
and call it from our template with:
%body
=render :partial, :foo => "bar"
which will render
<body>
<p>You passed in bar</p>
</body>
Layouts
In Rails, you can specify a layout for your views, so that all your pages can share the same
header, menu area etc. This is done by specifying a layout file, within which you call yield
to render the actual view in question. Layouts are slightly more tricky to add to haml, but can still be done.
Hamls render
method also accepts a block, so a simple solution would be to render the layout file, and pass a block that renders the view file:
Haml::Engine.new(File.read("layout.html.haml")).render do
Haml::Engine.new(File.read("view.html.haml")).render
end
This would give the contents of layout.html.haml
rendered with the contents of view.html.haml
rendered where the layout file contained =yield
.
content_for
Rails is a bit more flexible than that though. It allows you to call yield
multiple times in your layout file, naming a specific region in each case, and to specify the contents to be added at each region using the content_for
method within your views. So in your layout file:
!!!
%html
%head
= yield :title
%body
=yield
and in your view:
-content_for :title do
%title Hello
%p
Here's a paragraph.
The way Rails actually works is to render the view part first, storing all the different sections, and then rendering the layout, passing a block that provides the appropriate chunk whenever yield
is called in the layout. We can replicate this using a little helper class to provide the content_for
method and keep track of the rendered chunks for each region:
class Regions
def initialize
@regions_hash={}
end
def content_for(region, &blk)
@regions_hash[region] = capture_haml(&blk)
end
def [](region)
@regions_hash[region]
end
end
Here we're using the capture_haml
method to get the rendered haml without it going direct to the output. Note that this doesn't capture the unnamed part of the view.
We can now use our helper class to render the final output.
regions = Regions.new
unnamed = Haml::Engine.new(File.read("view_named.html.haml")).render(regions)
output = Haml::Engine.new(File.read("layout_named.html.haml")).render do |region|
region ? regions[region] : unnamed
end
Now the variable output
contains the final rendered output.
Note that the code here doesn't provide all the flexibility that's included with rails, but hopefully it's enough to show you where to start customising Haml to meet your needs.
How to use HAML to generate standalone HTML files via a layout template
I'd look at using Jekyll with a HAML workflow something like this http://mikeferrier.com/2011/04/29/blogging-with-jekyll-haml-sass-and-jammit/
Specifying a layout and a template in a standalone (not rails) ruby app, using slim or haml
The layout.slim file looks like:
h1 Hello
.content
== yield
The contents.slim file looks like:
= name
This can be shortened, but I separated to individual steps for explanation purposes.
require 'slim'
# Simple class to represent an environment
class Env
attr_accessor :name
end
# Intialize it
env = Env.new
# Set the variable we reference in contents.slim
env.name = "test this layout"
# Read the layout file in as a string
layout = File.open("layout.slim", "rb").read
# Read the contents file in as a string
contents = File.open("contents.slim", "rb").read
# Create new template object with the layout
l = Slim::Template.new { layout }
# Render the contents passing in the environment: env
# so that it can resolve: = name
c = Slim::Template.new { contents }.render(env)
# Render the layout passing it the rendered contents
# as the block. This is what yield in layout.slim will get
puts l.render{ c }
This will output:
<h1>Hello</h1><div class="content">test this layout</div>
How to add a stylesheet to the layout's head of a HAML template in Sinatra?
There are 2 ways you can go about it. One is to use Sinatra's own content_for
gem, or bundle ActionView, which will give you access to Rails' content_for
method.
The second option is to do a manual check in the layout, and include the CSS there:
# in your HAML template:
- if request.path_info == '/hello-world'
%link{:rel => :stylesheet, :type => :"text/css", :href => "/assets/css/my_stylesheet"}
How to specify a path for HAML views?
You need to set the :views
configuration:
set :views, Proc.new { File.join(root, "my_templates") }
get '/' do
haml :index, layout: :layout
end
If you want to put your templates in a directory that is not a top level directory, then you need to do this:
set :views, Proc.new{ File.join root, "my_templates", "haml_templates" }
Then sinatra will look for the views in your_app/my_templates/haml_templates
Selectively using yield_content in Padrino application.haml template
In your main file, you should be able to use content_for?
, like this:
- if content_for?(:headcontent)
= yield_content :headcontent
- else
something else
Best strategy to use HAML template with Backbone.js
I know you already mentioned it but I would suggest using haml-js with Jammit. Simply include haml.js in your javascripts and in your assets.yml add template_function: Haml
as well as including your template files in to a package. e.g.
javascript_templates:
- app/views/**/*.jst.haml
Then in your views you can include this package (= include_javascripts :javascript_templates
) and Jammit will package any .jst.haml files in to window.JST['file/path']
. (If you view page source you should see a javascript file like <script src="/assets/javascript_templates.jst" type="text/javascript"></script>
)
To use these templates simply call one of those JSTs Jammit created. i.e.
$('div').html(JST['file/path']({ foo: 'Hello', bar: 'World' }));
And Jammit will use the Haml-js template function function to render the template.
Note: Be sure to point to the github repo of Jammit in your Gemfile to get the latest version that supports newline characters necessary for haml-js to work.
Related Topics
Library Not Loaded: /Usr/Local/Opt/Readline/Lib/Libreadline.6.Dylib (Loaderror)
How to Get an Array with Column Names of a Table
Listing the Names of Associated Models
Ruby Regex What Does the \1 Mean for Gsub
How to Read Lines from File into Array
Rails Plugin for API Key + Secret Key Signing
Why Is This String Key in a Hash Converted to a Symbol
How to Use an Actionview::Helper in a Ruby Script, Outside of Rails
How to Call Rake Tasks That Are Defined in the Standard Rakefile from an Other Ruby Script
Rails Flash Message Remains for Two Page Loads
Convert Datetime String to Utc in Rails
Rails: Logging for Code in the Lib Directory
How to Get Request's Target Controller and Action with Rails 3
Ruby Classes: Initialize Self VS. @Variable
Generate a Migration File from Schema.Rb