One of assemble's biggest strengths is granular control over context. This workshop explains how context is created, as well as where, when and why the context works the way it does at each point in the render cycle.
(TOC generated by verb using markdown-toc)
Install with npm:
$ npm install --save context-workshop
Context is an object that is created in-memory for rendering templates. Context is made up of other data objects that are created and modified throughout the render cycle. Below we'll discuss which data objects are used, where they come from and the order in which they're merged. We're also going to learn how to customize the context object and how to use the context in your own custom helpers.
This repository also contains examples that may be run from the command line with assemble. See below for more information on installing and running the examples.
app.cache.data (from app.data())
site (this is controlled by the user)view.locals
app.cache.data at the individual view levelviews.addView()view.data (front-matter)
app.cache.data and view.localsonLoad middlewaretitle and layout to override "global" data at the view (page) levelrender locals
.render() method.view.locals or view.datahelper locals
{{partial "foo" locals}})There is a default order of operations when it comes to merging the data context. The order is customizable by the user.
app.cache.dataview.localsrender.localsview.datahelper-localsThe context is created by merging the objects in the specified order through the various methods discribed in Customizing context:
// merges the view context first
// e.g.: `merge(view.locals, locals, view.data)`
var context = view.context(locals);
// merges the `app.cache.data`
context = merge({}, app.cache.data, context);
In addition to the main context, helpers may use the this.ctx() method to merge in helper locals that are passed.
The built-in singular helpers like {{partial}} use this method to ensure helper locals are used.
This will result in the locals object being merged onto the context when rendering the "button" partial.
The default behaviour for merging the helper context is:
// merge the current "view" front-matter with current context built above
context = merge({}, context, page.data);
// merge in the partial locals and front-matter
context = merge({}, context, button.locals, button.data);
// merge in helper locals and options.hash
context = merge({}, context, locals, options.hash);
Customize how the context object is created.
view.context
locals objectreturn merge(view.locals, locals, view.data)app.context
view and optional locals objectview.context before merging datareturn merge({}, this.cache.data, view.context(locals))options.context: Customize how the context object is created.
context option:app.option('context', function(view, locals) {
// this is the app
return merge({}, this.cache.data, view.context(), locals);
});
options.helperContext: Custom how the helper context is created.
this.ctx() method in helpers through the helperContext option:app.option('helperContext', function(view, locals, options) {
return merge({}, view.context(), locals);
});
Clone this project and install the npm modules to run the examples:
# clone the project
$ git clone https://github.com/assemble/context-workshop
# cd into the folder
$ cd context-workshop
# install npm modules
$ npm install
# install assemble globally if not already installed
$ npm install --global assemble
Each example may be run by using assemble:
$ assemble <example>
To view a list of examples run the default assemble command:
$ assemble
To interactively choose an example to run use the -i option:
$ assemble -i
Assemble will use app.cache.data when rendering views (pages).
To add data to app.cache.data use the app.data() api. See base-data for all the available options for app.data().
To run this example:
$ assemble app-cache-data

Code snippet from example assemblefile.js
// add app-cache-data
app.data({title: 'Site Title'});
// create a simple "button" partial
app.partial('button', {content: 'button: <%= title %>'});
// create a simple "home" page containing 3 "button" partials
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button") %>',
'three: <%= partial("button") %>'
].join('\n')
});
// render the "home" page with no additional data
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.error(err);
console.log(res.content);
});
Render locals is the data object that is passed into the .render() method when rendering views.
The following example will show how the render locals will override data from app.cache.data when the context is created.
To run this example:
$ assemble render-locals

Code snippet from example assemblefile.js
// add app-cache-data
app.data({title: 'Site Title'});
// create a simple "button" partial
app.partial('button', {content: 'button: <%= title %>'});
// create a simple "home" page containing 3 "button" partials
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button") %>',
'three: <%= partial("button") %>'
].join('\n')
});
// render the "home" page with no additional data
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.log(err);
console.log(res.content);
home.render({title: 'Render Locals Title'}, function(err, res) {
if (err) return console.log(err);
console.log(res.content);
});
});
View locals is the data object that is on view objects that will be used to override app.cache.data.
The following example will show how the view locals will override data from app.cache.data, but is overridden by "render locals" when the context is created.
To run this example:
$ assemble view-locals

Code snippet from example assemblefile.js
// add app-cache-data
app.data({title: 'Site Title'});
// Add a "button" partial with view locals data.
// This data is only overridden by "render locals" if the button is rendered directly with `.render` and "render locals" are passed into `.render`.
app.partial('button', {
content: 'button: <%= title %>',
locals: {title: 'Button Locals Title'}
});
// Add a "home" page with view locals data that includes the 3 "button" partials.
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button") %>',
'three: <%= partial("button") %>'
].join('\n'),
locals: {title: 'Page Locals Title'}
});
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.error(err);
console.log(res.content);
home.render({title: 'Render Locals Title'}, function(err, res) {
if (err) return console.error(err);
console.log(res.content);
});
});
View data is the data object that is on view objects that will be used to override app.cache.data.
The following example will show how the view data will override data from app.cache.data and "render locals" when the context is created.
To run this example:
$ assemble view-data

Code snippet from example assemblefile.js
// add app-cache-data
app.data({title: 'Site Title'});
// Add a "button" partial with view locals data and view data.
// The view data will override `app.cache.data`, "render locals", and "view locals".
app.partial('button', {
content: 'button: <%= title %>',
locals: {title: 'Button Locals Title'},
data: {title: 'Button Data Title'}
});
// Add a "home" page with view locals data and view data that includes the 3 "button" partials.
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button") %>',
'three: <%= partial("button") %>'
].join('\n'),
locals: {title: 'Page Locals Title'},
data: {title: 'Page Data Title'}
});
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.error(err);
console.log(res.content);
home.render({title: 'Render Locals Title'}, function(err, res) {
if (err) return console.error(err);
console.log(res.content);
});
});
Helper locals is the data object that is passed into the built-in view helpers. This data will override all other data for that specific view. The following example will show how the helper locals will override all other data when rendering a partial view.
To run this example:
$ assemble helper-locals

Code snippet from example assemblefile.js
// add app-cache-data
app.data({title: utils.cyan('Site Title')});
// Add a "button" partial with view locals data and view data.
// The view data will override `app.cache.data`, "render locals", and "view locals".
// When "helper locals" is passed to the "partial" helper, all data on the view will be overridden.
app.partial('button', {
content: 'button: <%= title %>',
locals: {title: 'Button Locals Title'},
data: {title: 'Button Data Title'}
});
// Add a "home" page with view locals data and view data that includes the 3 "button" partials.
// Button "one" will be rendered without passing any helper locals.
// Button "two" will be rendered with the "home" page's data passed as the helper locals.
// Button "three" will be rendered with a "custom" property from the "render locals" passed as the helper locals.
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button", obj) %>', // "obj" is the built-in global object from engine-base
`three: <%= partial("button", {title: 'Helper Locals Title'}) %>`
].join('\n'),
locals: {title: 'Page Locals Title'},
data: {title: 'Page Data Title'}
});
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.error(err);
console.log(res.content);
home.render({title: 'Render Locals Title'}, function(err, res) {
if (err) return console.error(err);
console.log(res.content);
});
});
Context is customizable by adding optional functions to the app.options object.
This examples shows the ways to customize the context.
To run this example:
$ assemble customizing

Code snippet from example assemblefile.js
// Add a context option
app.option('context', function(view, locals) {
// override all the other data with the "render locals"
return extend({}, this.cache.data, view.context(), locals);
});
// add app-cache-data
app.data({title: 'Site Title'});
// create a simple "button" partial
app.partial('button', {content: 'button: <%= title %>'});
// create a simple "home" page containing 3 "button" partials
app.page('home', {
content: [
'title: <%= title %>',
'one: <%= partial("button") %>',
'two: <%= partial("button") %>',
'three: <%= partial("button") %>'
].join('\n')
});
// render the "home" page with no additional data
var home = app.pages.getView('home');
home.render(function(err, res) {
if (err) return console.error(err);
console.log(res.content);
});
Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
(This document was generated by verb-generate-readme (a verb generator), please don't edit the readme directly. Any changes to the readme must be made in .verb.md.)
To generate the readme and API documentation with verb:
$ npm install -g verb verb-generate-readme && verb
Install dev dependencies:
$ npm install -d && npm test
Jon Schlinkert
Copyright © 2016, Jon Schlinkert. Released under the MIT license.
This file was generated by verb, v0.9.0, on July 18, 2016.