Blaze
Edit on GitHub | Discuss on the Meteor Forums
After reading this guide, you’ll know:
このガイドを読むと、以下のことがわかります:
- How to use the Spacebars language to define templates rendered by the Blaze engine.
- Best practices for writing reusable components in Blaze.
- How the Blaze rendering engine works under the hood and some advanced techniques for using it.
- How to test Blaze templates.
Blaze is Meteor’s built in reactive rendering library. Usually, templates are written in Spacebars, a variant of Handlebars designed to take advantage of Tracker, Meteor’s reactivity system. These templates are compiled into JavaScript UI components that are rendered by the Blaze library.
Blaze is not required to build applications in Meteor—you can also easily use React or Angular to develop your UI. However, this particular article will take you through best practices in building an application in Blaze, which is used as the UI engine in all of the other articles.
Spacebars
Spacebars is a handlebars-like templating language, built on the concept of rendering a reactively changing data context. Spacebars templates look like simple HTML with special “mustache” tags delimited by curly braces: {{ }}
.
As an example, consider the Todos_item
template from the Todos example app:
1 | <template name="Todos_item"> |
This template expects to be rendered with an object with key todo
as data context (we’ll see below how to enforce that). We access the properties of the todo
using the mustache tag, such as {{todo.text}}
. The default behavior is to render that property as a string; however for some attributes (such as checked={{todo.checked}}
) it can be resolved as a boolean value.
Note that simple string interpolations like this will always escape any HTML for you, so you don’t need to perform safety checks for XSS.
Additionally we can see an example of a template helper—{{checkedClass todo}}
calls out to a checkedClass
helper defined in a separate JavaScript file. The HTML template and JavaScript file together define the Todos_item
component:
1 | Template.Todos_item.helpers({ |
In the context of a Blaze helper, this
is scoped to the current data context at the point the helper was used. This can be hard to reason about, so it’s often a good idea to instead pass the required data into the helper as an argument (as we do here).
Apart from simple interpolation, mustache tags can be used for control flow in the template. For instance, in the Lists_show
template, we render a list of todos like this:
1 | {{#each todo in todos}} |
This snippet illustrates a few things:
- The
{{#each .. in}}
block helper which repeats a block of HTML for each element in an array or cursor, or renders the contents of the{{else}}
block if no items exist. - The template inclusion tag,
{{> Todos_item (todoArgs todo)}}
which renders theTodos_item
component with the data context returned from thetodosArg
helper.
You can read about the full syntax in the Spacebars README. In this section we’ll attempt to cover some of the important details beyond just the syntax.
Data contexts and lookup
We’ve seen that {{todo.title}}
accesses the title
property of the todo
item on the current data context. Additionally, ..
accesses the parent data context (rarely a good idea), list.todos.[0]
accesses the first element of the todos
array on list
.
Note that Spacebars is very forgiving of null
values. It will not complain if you try to access a property on a null
value (for instance foo.bar
if foo
is not defined), but instead simply treats it also as null. However there are exceptions to this—trying to call a null
function, or doing the same within a helper will lead to exceptions.
Calling helpers with arguments
You can provide arguments to a helper like checkedClass
by simply placing the argument after the helper call, as in: {{checkedClass todo true "checked"}}
. You can also provide a list of named keyword arguments to a helper with {{checkedClass todo noClass=true classname="checked"}}
. When you pass keyword arguments, you need to read them off of the hash
property of the final argument. Here’s how it would look for the example we just saw:
1 | Template.Todos_item.helpers({ |
Note that using keyword arguments to helpers is a little awkward, so in general it’s usually easier to avoid them. This feature was included for historical reasons to match the way keyword arguments work in Handlebars.
You can also pass the output of a helper to a template inclusion or other helper. To do so, use parentheses to show precedence:
1 | {{> Todos_item (todoArgs todo)}} |
Here the todo
is passed as argument to the todoArgs
helper, then the output is passed into the Todos_item
template.
Template inclusion
You “include” a sub-component with the {{> }}
syntax. By default, the sub-component will gain the data context of the caller, although it’s usually a good idea to be explicit. You can provide a single object which will become the entire data context (as we did with the object returned by the todoArgs
helper above), or provide a list of keyword arguments which will be put together into one object, like so:
1 | {{> subComponent arg1="value-of-arg1" arg2=helperThatReturnsValueOfArg2}} |
In this case, the subComponent
component can expect a data context of the form:
1 | { |
Attribute Helpers
We saw above that using a helper (or data context lookup) in the form checked={{todo.checked}}
will add the checked property to the HTML tag if todo.checked
evaluates to true. Also, you can directly include an object in the attribute list of an HTML element to set multiple attributes at once:
1 | <a {{attributes}}>My Link</a> |
1 | Template.foo.helpers({ |
Rendering raw HTML
Although by default a mustache tag will escape HTML tags to avoid XSS, you can render raw HTML with the triple-mustache: {{{ }}}
.
1 | {{{myHtml}}} |
1 | Template.foo.helpers({ |
You should be extremely careful about doing this, and always ensure you aren’t returning user-generated content (or escape it if you do!) from such a helper.
Block Helpers
A block helper, called with {{# }}
is a helper that takes (and may render) a block of HTML. For instance, we saw the {{#each .. in}}
helper above which repeats a given block of HTML once per item in a list. You can also use a template as a block helper, rendering its content via the Template.contentBlock
and Template.elseBlock
. For instance, you could create your own {{#if}}
helper with:
1 | <template name="myIf"> |
Builtin Block Helpers
There are a few builtin block helpers that are worth knowing about:
If / Unless
The {{#if}}
and {{#unless}}
helpers are fairly straightforward but invaluable for controlling the control flow of a template. Both operate by evaluating and checking their single argument for truthiness. In JS null
, undefined
, 0
, ''
, []
, and false
are considered “falsy”, and all other values are “truthy”.
1 | {{#if something}} |
Each-in
The {{#each .. in}}
helper is a convenient way to step over a list while retaining the outer data context.
1 | {{#each todo in todos}} |
Let
The {{#let}}
helper is useful to capture the output of a helper or document subproperty within a template. Think of it just like defining a variable using JavaScript let
.
1 | {{#let name=person.bio.firstName color=generateColor}} |
Note that name
and color
(and todo
above) are only added to scope in the template, they are not added to the data context. Specifically this means if you call a helper, they will not be on this
. So if you need to access them in a helper, you should pass them in as an argument (like we do with (todoArgs todo)
above).
Each and With
There are also two Spacebars built in helpers, {{#each}}
, and {{#with}}
, which we do not recommend using (see use each-in below). These block helpers change the data context within a template, which can be difficult to reason about.
Like {{#each .. in}}
, {{#each}}
iterates over an array or cursor, changing the data context within its content block to be the item in the current iteration. {{#with}}
simply changes the data context inside itself to the provided object. In most cases it’s better to use {{#each .. in}}
and {{#let}}
instead, just like it’s better to declare a variable than use the JavaScript with
keyword.
Strictness
Spacebars has a very strict HTML parser. For instance, you can’t self-close a div
(<div/>
) in Spacebars, and you need to close some tags that a browser might not require you to (such as a <p>
tag). Thankfully, the parser will warn you when it can’t understand your code with an exact line number for the error.
Escaping
To insert literal curly braces: {{ }}
and the like, add a pipe character, |
, to the opening braces:
1 | <!-- will render as <h1>All about {{</h1> --> |
Reusable components in Blaze
In UI/UX article we discussed the merits of creating reusable components that interact with their environment in clear and minimal ways.
Although Blaze, which is a simple template-based rendering engine, doesn’t enforce a lot of these principles (unlike other frameworks like React and Angular) you can enjoy most of the same benefits by following some conventions when writing your Blaze components. This section will outline some of these “best practices” for writing reusable Blaze components.
Examples below will reference the Lists_show
component from the Todos example app.
Validate data context
In order to ensure your component always gets the data you expect, you should validate the data context provided to it. This is just like validating the arguments to any Meteor Method or publication, and lets you write your validation code in one place and then assume that the data is correct.
You can do this in a Blaze component’s onCreated()
callback, like so:
1 | Template.Lists_show.onCreated(function() { |
We use an autorun()
here to ensure that the data context is re-validated whenever it changes.
Name data contexts to template inclusions
It’s tempting to just provide the object you’re interested in as the entire data context of the template (like {{> Todos_item todo}}
). It’s better to explicitly give it a name ({{> Todos_item todo=todo}}
). There are two primary reasons for this:
- When using the data in the sub-component, it’s a lot clearer what you are accessing;
{{todo.title}}
is clearer than{{title}}
. - It’s more flexible, in case you need to give the component more arguments in the future.
For instance, in the case of the Todos_item
sub-component, we need to provide two extra arguments to control the editing state of the item, which would have been a hassle to add if the item was used with a single todo
argument.
Additionally, for better clarity, always explicitly provide a data context to an inclusion rather than letting it inherit the context of the template where it was rendered:
1 | <!-- bad: inherits data context, who knows what is in there! --> |
{{#each .. in}}
Prefer For similar reasons to the above, it’s better to use {{#each todo in todos}}
rather than the older {{#each todos}}
. The second sets the entire data context of its children to a single todo
object, and makes it difficult to access any context from outside of the block.
The only reason not to use {{#each .. in}}
would be because it makes it difficult to access the todo
symbol inside event handlers. Typically the solution to this is simply to use a sub-component to render the inside of the loop.
Pass data into helpers
Rather than accessing data in helpers via this
, it’s better to pass the arguments in directly from the template. So our checkedClass
helper takes the todo
as an argument and inspects it directly, rather than implicitly using this.todo
. We do this for similar reasons to why we always pass arguments to template inclusions, and because “template variables” (such as the iteratee of the {{#each .. in}}
helper) are not available on this
.
Use the template instance
Although Blaze’s simple API doesn’t necessarily encourage a componentized approach, you can use the template instance as a convenient place to store internal functionality and state. The template instance can be accessed via this
inside Blaze’s lifecycle callbacks and as Template.instance()
in event handlers and helpers. It’s also passed as the second argument to event handlers.
We suggest a convention of naming it instance
in these contexts and assigning it at the top of every relevant helper. For instance:
1 | Template.Lists_show.helpers({ |
Use a reactive dict for state
The reactive-dict
package lets you define a simple reactive key-value dictionary. It’s a convenient way to attach internal state to a component. We create the state
dictionary in the onCreated
callback, and attach it to the template instance:
1 | Template.Lists_show.onCreated(function() { |
Once the state dictionary has been created we can access it from helpers and modify it in event handlers (see the code snippet above).
Attach functions to the instance
If you have common functionality for a template instance that needs to be abstracted or called from multiple event handlers, it’s sensible to attach it as functions directly to the template instance in the onCreated()
callback:
1 | import { |
Then you can call that function from within an event handler:
1 | Template.Lists_show.events({ |
Scope DOM lookups to the template instance
It’s a bad idea to look up things directly in the DOM with jQuery’s global $()
. It’s easy to select some element on the page that has nothing to do with the current component. Also, it limits your options on rendering outside of the main document (see testing section below).
Instead, Blaze gives you a way to scope a lookup to within the current template instance. Typically you use this either from a onRendered()
callback to setup jQuery plugins (called via Template.instance().$()
or this.$()
), or from event handlers to call DOM functions directly (called via Template.instance().$()
or using the event handler’s second argument like instance.$()
). For instance, when the user clicks the add todo button, we want to focus the <input>
element:
1 | Template.Lists_show.events({ |
.js-
selectors for event maps
Use When you are setting up event maps in your JS files, you need to ‘select’ the element in the template that the event attaches to. Rather than using the same CSS class names that are used to style the elements, it’s better practice to use classnames that are specifically added for those event maps. A reasonable convention is a class starting with js-
to indicate it is used by the JavaScript. For instance .js-todo-add
above.
Passing HTML content as a template argument
If you need to pass in content to a sub-component (for instance the content of a modal dialog), you can use the custom block helper to provide a block of content. If you need more flexibility, typically just providing the component name as an argument is the way to go. The sub-component can then just render that component with:
1 | {{> Template.dynamic templateName dataContext}} |
This is more or less the way that the kadira:blaze-layout
package works.
Pass callbacks
If you need to communicate up the component hierarchy, it’s best to pass a callback for the sub-component to call.
For instance, only one todo item can be in the editing state at a time, so the Lists_show
component manages the state of which is edited. When you focus on an item, that item needs to tell the list’s component to make it the “edited” one. To do that, we pass a callback into the Todos_item
component, and the child calls it whenever the state needs to be updated in the parent:
1 | {{> Todos_item (todoArgs todo)}} |
1 | Template.Lists_show.helpers({ |
onRendered()
for 3rd party libraries
Use As we mentioned above, the onRendered()
callback is typically the right spot to call out to third party libraries that expect a pre-rendered DOM (such as jQuery plugins). The onRendered()
callback is triggered once after the component has rendered and attached to the DOM for the first time.
Occasionally, you may need to wait for data to become ready before it’s time to attach the plugin (although typically it’s a better idea to use a sub-component in this use case). To do so, you can setup an autorun
in the onRendered()
callback. For instance, in the Lists_show_page
component, we want to wait until the subscription for the list is ready (i.e. the todos have rendered) before we hide the launch screen:
1 | Template.Lists_show_page.onRendered(function() { |
Writing smart components with Blaze
Some of your components will need to access state outside of their data context—for instance, data from the server via subscriptions or the contents of client-side store. As discussed in the data loading and UI articles, you should be careful and considered in how use such smart components.
All of the suggestions about reusable components apply to smart components. In addition:
onCreated
Subscribe from You should subscribe to publications from the server from an onCreated
callback (within an autorun
block if you have reactively changing arguments). In the Todos example app, in the Lists_show_page
template we subscribe to the todos.inList
publication based on the current _id
FlowRouter param:
1 | Template.Lists_show_page.onCreated(function() { |
We use this.subscribe()
as opposed to Meteor.subscribe()
so that the component automatically keeps track of when the subscriptions are ready. We can use this information in our HTML template with the built-in {{Template.subscriptionsReady}}
helper or within helpers using instance.subscriptionsReady()
.
Notice that in this component we are also accessing the global client-side state store FlowRouter
, which we wrap in a instance method called getListId()
. This instance method is called both from the autorun
in onCreated
, and from the listIdArray
helper:
1 | Template.Lists_show_page.helpers({ |
Fetch in helpers
As described in the UI/UX article, you should fetch data in the same component where you subscribed to that data. In a Blaze smart component, it’s usually simplest to fetch the data in a helper, which you can then use to pass data into a reusable child component. For example, in the Lists_show_page
:
1 | {{> Lists_show (listArgs listId)}} |
The listArgs
helper fetches the data that we’ve subscribed to above:
1 | Template.Lists_show_page.helpers({ |
Reusing code in Blaze
It’s common to want to reuse code between two otherwise unrelated components. There are two main ways to do this in Blaze.
Composition
If possible, it’s usually best to try and abstract out the reusable part of the two components that need to share functionality into a new, smaller component. If you follow the patterns for reusable components, it should be simple to reuse this sub-component everywhere you need this functionality.
For instance, suppose you have many places in your application where you need an input to blur itself when you click the “esc” key. If you were building an autocomplete widget that also wanted this functionality, you could compose a blurringInput
inside your autocompleteInput
:
1 | <template name="autocompleteInput"> |
1 | Template.autocompleteInput.helpers({ |
By making the blurringInput
flexible and reusable, we can avoid re-implementing functionality in the autocompleteInput
.
Libraries
It’s usually best to keep your view layer as thin as possible and contain a component to whatever specific task it specifically needs to do. If there’s heavy lifting involved (such as complicated data loading logic), it often makes sense to abstract it out into a library that simply deals with the logic alone and doesn’t deal with the Blaze system at all.
For example, if a component requires a lot of complicated D3 code for drawing graphs, it’s likely that that code itself could live in a separate module that’s called by the component. That makes it easier to abstract the code later and share it between various components that need to all draw graphs.
Global Helpers
Another way to share commonly used view code is a global Spacebars helper. You can define these with the Template.registerHelper()
function. Typically you register helpers to do simple things (like rendering dates in a given format) which don’t justify a separate sub-component. For instance, you could do:
1 | Template.registerHelper('shortDate', (date) => { |
1 | <template name="myBike"> |
Understanding Blaze
Although Blaze is a very intuitive rendering system, it does have some quirks and complexities that are worth knowing about when you are trying to do complex things.
Re-rendering
Blaze is intentionally opaque about re-rendering. Tracker and Blaze are designed as “eventual consistency” systems that end up fully reflecting any data change eventually, but may take a few re-runs or re-renders in getting there, depending on how they are used. This can be frustrating if you are trying to carefully control when your component is re-rendered.
The first thing to consider here is if you actually need to care about your component re-rendering. Blaze is optimized so that it typically doesn’t matter if a component is re-rendered even if it strictly shouldn’t. If you make sure that your helpers are cheap to run and consequently rendering is not expensive, then you probably don’t need to worry about this.
The main thing to understand about how Blaze re-renders is that re-rendering happens at the level of helpers and template inclusions. Whenever the data context of a component changes, it necessarily must re-run all helpers and data accessors (as this
within the helper is the data context and thus will have changed).
Additionally, a helper will re-run if any reactive data source accessed from within that specific helper changes.
You can often work out why a helper has re-run by tracing the source of the reactive invalidation:
1 | Template.myTemplate.helpers({ |
Controlling re-rendering
If your helper or sub-component is expensive to run, and often re-runs without any visible effect, you can short circuit unnecessary re-runs by using a more subtle reactive data source. The peerlibrary:computed-field
package helps achieve this pattern.
Attribute helpers
Setting tag attributes via helpers (e.g. <div {{attributes}}>
) is a neat tool and has some precedence rules that make it more useful. Specifically, when you use it more than once on a given element, the attributes are composed (rather than the second set of attributes simply replacing the first). So you can use one helper to set one set of attributes and a second to set another. For instance:
1 | <template name="myTemplate"> |
1 | Template.myTemplate.helpers({ |
Lookup order
Another complicated topic in Blaze is name lookups. In what order does Blaze look when you write {{something}}
? It runs in the following order:
- Helper defined on the current component
- Binding (eg. from
{{#let}}
or{{#each in}}
) in current scope - Template name
- Global helper
- Field on the current data context
Blaze and the build system
As mentioned in the build system article, the blaze-html-templates
package scans your source code for .html
files, picks out <template name="templateName">
tags, and compiles them into a JavaScript file that defines a function that implements the component in code, attached to the Template.templateName
symbol.
This means when you render a Blaze template, you are simply running a function on the client that corresponds to the Spacebars content you defined in the .html
file.
What is a view?
One of the most core concepts in Blaze is the “view”, which a building block that represents a reactively rendering area of a template. The view is the machinery that works behind the scenes to track reactivity, do lookups, and re-render appropriately when data changes. The view is the unit of re-rendering in Blaze. You can even use the view tree to walk the rendered component hierarchy, but it’s better to avoid this in favor of communicating between components using callbacks, template arguments, or global data stores.
You can read more about views in the Blaze docs.