Using Blade with Meteor

bminer edited this page Mar 12, 2013 · 91 revisions

Special thanks to mlohr for this wiki page.

Blade vs. Handlebars

The Blade smart package takes a slightly different approach from the Handlebars package. In Handlebars, you can define files, and within files are various tags: head, body, and the special template tag. You can have <body> tags in multiple files, and the content is concatenated and served with the initial page load. Also, any content in a <template> tag is stored as an HTML template.

Blade takes a slightly different approach. To the Blade smart package, each *.blade file is a template, except for the head.blade template. This is often a source of confusion, so now that we cleared that up, let's get started...

Adding Blade to your Meteor project

At the time of this writing, Blade is not a part of the Meteor core smart package list. You can, however, add Blade to your project by installing Meteorite and adding the Blade Atmosphere project.

Adding Blade the Meteorite way

You can install the Atmosphere smart package using Meteorite. Information on using and installing Meteorite can be found here. Once Meteorite is installed, execute this command from your Meteor project directory:

mrt add blade

The old way

The old way to add Blade to your project is to symlink the Blade smart package directory into your Meteor packages directory like this:

ln -s /path/to/.../blade/meteor /path/to/.../meteor/packages/blade

In Debian/Ubuntu, the command is this:

ln -s /usr/lib/node_modules/blade/meteor /usr/lib/meteor/packages/blade

Then, execute meteor add blade in your Meteor project directory.

See Blade's README file for more information.

Getting Started

Within your Meteor project directory, I strongly recommend that you create a views/ directory for your Blade templates. You can place *.blade files anywhere in your project directory, but you may run into issues when you include a file that is stored outside of the views/ directory.

The name of your Blade template will be generated based upon its location within your Meteor project directory. The template name is generated as follows:

  1. Get the path of the *.blade file relative to your Meteor project directory
  2. If the file is stored in views/, remove views/ from the name. If the file is stored in client/views/, remove client/views/ from the name.
  3. Remove the .blade file extension from the name

Examples:

  • /home/you/meteor_project/hello.blade --> hello
  • /home/you/meteor_project/views/hello.blade --> hello (Note: watch out for namespacing issues)
  • /home/you/meteor_project/client/views/foo/hello.blade --> foo/hello (Note: watch out for namespacing issues)
  • /home/you/meteor_project/foo/bar/world.blade --> foo/bar/world

The Blade smart package renders the head template during the initial page load, placing the rendered content into the <head> tag of the initial HTML document. head templates may not contain references to Session, Meteor, or Template, as these variables are not available until after the initial page load. Similarly, helpers do not work within the head template. You can always load another template at runtime if you want your initial page to contain dynamic content. All Blade files/templates will be defined under the Template global variable, as normal. That is, a template in ./views/foo/bar.blade is accessable via Template["foo/bar"].

Just like the <body> tags in Handlebars, the body.blade template will be rendered after the initial page load, and its HTML will be directly inserted into the <body> tag just before Meteor calls the Meteor.startup routine.

Example of a regular template:

Create templates in your views/ directory:

views/test.blade

h1 This is only a test
h2 Cool!
input(type="button" value="Click Me")

You can render that template from another Blade file by using include:

views/homepage.blade

#container
  include 'test'

(This is equivalent to the Handlebars syntax {{> test}}.) You can also access your templates programatically through Meteor's Template object. Template.test() should output

<h1>This is only a test</h1><h2>Cool!</h2><input type="button" value="Click Me"/>

With jQuery, you could render the template like this:

$("body").append(Meteor.render(Template.test) );

Example of body and head templates:

views/body.blade

h1 Hello world!
p The content contained with this file will be placed into the <body> tag of the initial HTML document
p Before Blade 3.0.0 stable, you could not place dynamic content here. Now you can!
p For example, `include` statements, view helpers, etc. work just fine in `body.blade` templates.

views/head.blade

title Blade is cool!

Example using isolates

views/question.blade

isolate
  - console.log("Render name")
  h1 Hey, I have a question for you, #{Session.get("name")}.
isolate
  - console.log("Render question")
  p.question= Session.get("question")

Each isolate will be rendered separately. Check the console if you wish. :)

Converting a Handlebars template to Blade

The following Handlebars template can be converted to Blade quite easily...

foo.html

<template name="foo">
 {{#each bars}}
   <p>{{bar}}</p>
 {{/each}}
</template>

might look like this in Blade...

views/foo.blade

foreach bars as bar
  p=bar

Another Example

foo.html

<template name="foo">
 {{#each bars}}
   {{> bar}}
 {{/each}}
</template>

might look like this in Blade...

views/foo.blade

foreach bars as bar
  include "bar" exposing bar

Binding events

Just like in Handlebars, you can call the events function of a template and pass an event map. The this argument to the event handler will be the data context of the element that triggered the event (in Blade, at the time of this writing, the data context is the Object passed to the Template during rendering).

Inline event bindings also work with Meteor.

Using Blade and Handlebars in your Meteor Project

You can use a mix of Handlebars templates and Blade templates, although you should be aware that the syntax for including templates or partials will likely be incompatible. To include a Handlebars template into Blade, access the Template global variable. For example, to "include" the Handlebars template foobar, you can't simply write include "foobar" in your Blade template, since "foobar" is not a Blade template. Instead, this one-liner should do:

!= Template.foobar({"optional_data_object":"goes here"})

Limitations when using Blocks

The use of blocks are not recommended in Meteor for a few reasons. The limitation is this: If a parent template (let's call it parent.blade) or any included template (let's call it include.blade) contains a block declaration, any "reactive" features in include.blade will be disabled.

Here's why this limitation exists...

A block is a marker in the template that tells Blade where more HTML can go. You then modify those blocks later in your template. You can even declare a block in a parent template and have it modified later in an included template.

Suppose, however, you put a block in an isolate region. Since the block can be modified elsewhere (possibly by an included template with its own data dependencies), it is very difficult (nearly impossible) to track the data dependencies of the block. And, therefore, if would be very difficult to track the data dependencies of the isolate region containing the block. To explain this another way, Spark defines an isolate region to be a section of a template that can be rendered separately from the rest of the template; that is, its HTML can be rendered by simply re-rendering that section of the template. However, if a block declaration is inserted into an isolate block, Spark's definition of an isolate region is violated. A block in Blade can be modified later in a separate section of the template, outside of the isolate region.

This is a problem. So, to "solve" this problem, Blade turns off template reactivity if you try to put a block in an isolate region. In fact, as mentioned before, Blade prevents you from having any "reactive" stuff in an included template that contains a block declaration. Even if the parent template contains a block declaration, "reactive" stuff for included templates is disabled.

Troubleshooting and Known Issues

  • Meteor does not necessarily load compiled Blade templates before your application code. By default, Meteor loads and bundles source files alphabetically. So, if you put some JavaScript code in client/index.js and your views in views/... and if your code tries to render a view, you end up with a problem. Your code will be loaded before your view templates (because client/... is loaded before views/...). This bug doesn't happen in Handlebars because of a hack in app/lib/packages.js. Obviously, a workaround for this is to put your code in a folder called zCode. Since it starts with the letter z, it will be loaded last. Plus, it makes you sound more French? See Meteor issue #181 Alternatively, if you wrap your code in Meteor.startup calls, this problem can also be avoided.
  • The Blade smart package cannot minify compiled templates. Meteor issue #180
  • References to global variables (like Session, Meteor, or Template) are not allowed in head templates. Helpers don't work either. This is by design, and it is not a bug. Handlebars also has this restriction.