context-workshop
v0.1.1
Published
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.
Downloads
5
Readme
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-workshop
What 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.data
at 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.data
andview.locals
- may be specified as view "front-matter" that is parsed in
onLoad
middleware - usually includes properties like
title
andlayout
to override "global" data at the view (page) level
render
locals- local data object specified when calling the
.render()
method. - useful for specifying data that may not exist on
view.locals
orview.data
- local data object specified when calling the
helper
locals- 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.data
view.locals
render.locals
view.data
helper-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
locals
object - 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
view
and optionallocals
object - calls the
view.context
before 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
context
option:
- 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 thehelperContext
option:
- 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 assemble
Running
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
app-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 && verb
Running tests
Install dev dependencies:
$ npm install -d && npm test
Author
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.