context-workshop v0.1.1
context-workshop

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.
Table of Contents
- Install
- What is context?
- What objects are used to create the context?
- How are the objects merged?
- Customizing context
- Examples
- About
(TOC generated by verb using markdown-toc)
Install
Install with npm:
$ npm install --save context-workshopWhat is context?
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.
What objects are used to create the context?
app.cache.data(fromapp.data())- main data object that is useful for "global" data.
- usually includes properties like
site(this is controlled by the user)
view.locals- individual view "local" data
- overrides
app.cache.dataat the individual view level - added through middleware or when creating a view with
views.addView()
view.data(front-matter)- individual view data object
- overrides
app.cache.dataandview.locals - may be specified as view "front-matter" that is parsed in
onLoadmiddleware - usually includes properties like
titleandlayoutto override "global" data at the view (page) level
renderlocals- local data object specified when calling the
.render()method. - useful for specifying data that may not exist on
view.localsorview.data
- local data object specified when calling the
helperlocals- local data object specified when calling a view helper in another template
- works with the built-in "singular" view helper (e.g.
{{partial "foo" locals}}) - will override all other data.
How are the objects merged?
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-locals
The 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.
{{partial "button" locals}}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);Customizing context
Customize how the context object is created.
view.context- method that takes optional
localsobject - merges data by doing
return merge(view.locals, locals, view.data) - may override directly to change the behaviour
- method that takes optional
app.context- method that takes
viewand optionallocalsobject - calls the
view.contextbefore merging data - merges data by doing
return merge({}, this.cache.data, view.context(locals))
- method that takes
options.context: Customize how the context object is created.- may override functionality through the
contextoption:
- may override functionality through the
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.- may override the functionality used in the
this.ctx()method in helpers through thehelperContextoption:
- may override the functionality used in the
app.option('helperContext', function(view, locals, options) {
return merge({}, view.context(), locals);
});Examples
Installing
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 assembleRunning
Each example may be run by using assemble:
$ assemble <example>To view a list of examples run the default assemble command:
$ assembleTo interactively choose an example to run use the -i option:
$ assemble -iapp-cache-data
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
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
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
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
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);
});
});customizing
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);
});About
Contributing
Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Building docs
(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 && verbRunning tests
Install dev dependencies:
$ npm install -d && npm testAuthor
Jon Schlinkert
License
Copyright © 2016, Jon Schlinkert. Released under the MIT license.
This file was generated by verb, v0.9.0, on July 18, 2016.