{"id":18434685,"url":"https://github.com/assemble/context-workshop","last_synced_at":"2025-04-14T08:24:00.394Z","repository":{"id":57206543,"uuid":"61726689","full_name":"assemble/context-workshop","owner":"assemble","description":"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.","archived":false,"fork":false,"pushed_at":"2016-07-18T14:28:31.000Z","size":35,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-16T09:29:03.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/assemble.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-22T14:42:46.000Z","updated_at":"2017-09-25T14:23:24.000Z","dependencies_parsed_at":"2022-09-08T14:20:42.194Z","dependency_job_id":null,"html_url":"https://github.com/assemble/context-workshop","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assemble%2Fcontext-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assemble%2Fcontext-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assemble%2Fcontext-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assemble%2Fcontext-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/assemble","download_url":"https://codeload.github.com/assemble/context-workshop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248844300,"owners_count":21170549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T06:04:54.797Z","updated_at":"2025-04-14T08:24:00.336Z","avatar_url":"https://github.com/assemble.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# context-workshop [![NPM version](https://img.shields.io/npm/v/context-workshop.svg?style=flat)](https://www.npmjs.com/package/context-workshop) [![NPM downloads](https://img.shields.io/npm/dm/context-workshop.svg?style=flat)](https://npmjs.org/package/context-workshop) [![Build Status](https://img.shields.io/travis/assemble/context-workshop.svg?style=flat)](https://travis-ci.org/assemble/context-workshop)\n\nOne 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.\n\n## Table of Contents\n\n- [Install](#install)\n- [What is context?](#what-is-context)\n- [What objects are used to create the context?](#what-objects-are-used-to-create-the-context)\n- [How are the objects merged?](#how-are-the-objects-merged)\n- [Customizing context](#customizing-context)\n- [Examples](#examples)\n- [About](#about)\n  * [Related projects](#related-projects)\n  * [Contributing](#contributing)\n  * [Building docs](#building-docs)\n  * [Running tests](#running-tests)\n  * [Author](#author)\n  * [License](#license)\n\n_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_\n\n## Install\n\nInstall with [npm](https://www.npmjs.com/):\n\n```sh\n$ npm install --save context-workshop\n```\n\n## What is context?\n\nContext 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](#what-objects-are-used-to-create-the-context) are used, where they come from and the [order in which they're merged](#how-are-the-objects-merged). We're also going to learn how to [customize the context object](#customizing-context) and how to use the context in your own [custom helpers](#context-and-helpers).\n\nThis repository also contains [examples](./examples) that may be run from the command line with [assemble](https://github.com/assemble/assemble). [See below](#examples) for more information on installing and running the examples.\n\n***\n\n## What objects are used to create the context?\n\n* `app.cache.data` (from `app.data()`)\n  - main data object that is useful for \"global\" data.\n  - usually includes properties like `site` (this is controlled by the user)\n\n* `view.locals`\n  - individual view \"local\" data\n  - overrides `app.cache.data` at the individual view level\n  - added through middleware or when creating a view with `views.addView()`\n\n* `view.data` (front-matter)\n  - individual view data object\n  - overrides `app.cache.data` and `view.locals`\n  - may be specified as view \"front-matter\" that is parsed in `onLoad` middleware\n  - usually includes properties like `title` and `layout` to override \"global\" data at the view (page) level\n\n* `render` locals\n  - local data object specified when calling the `.render()` method.\n  - useful for specifying data that may not exist on `view.locals` or `view.data`\n\n* `helper` locals\n  - local data object specified when calling a view helper in another template\n  - works with the built-in \"singular\" view helper (e.g. `{{partial \"foo\" locals}}`)\n  - will override all other data.\n\n***\n\n## How are the objects merged?\n\nThere is a default order of operations when it comes to merging the data context. The order is [customizable](#customizing-context) by the user.\n\n* `app.cache.data`\n* `view.locals`\n* `render.locals`\n* `view.data`\n* `helper-locals`\n\nThe context is created by merging the objects in the specified order through the various methods discribed in [Customizing context](#customizing-context):\n\n```js\n// merges the view context first\n// e.g.: `merge(view.locals, locals, view.data)`\nvar context = view.context(locals);\n\n// merges the `app.cache.data`\ncontext = merge({}, app.cache.data, context);\n```\n\nIn addition to the main context, helpers may use the `this.ctx()` method to merge in helper locals that are passed.\nThe built-in singular helpers like `{{partial}}` use this method to ensure helper locals are used.\n\n```handlebars\n{{partial \"button\" locals}}\n```\n\nThis will result in the `locals` object being merged onto the context when rendering the \"button\" partial.\nThe default behaviour for merging the helper context is:\n\n```js\n// merge the current \"view\" front-matter with current context built above\ncontext = merge({}, context, page.data);\n// merge in the partial locals and front-matter\ncontext = merge({}, context, button.locals, button.data);\n// merge in helper locals and options.hash\ncontext = merge({}, context, locals, options.hash);\n```\n\n***\n\n## Customizing context\n\nCustomize how the context object is created.\n\n* `view.context`\n  - method that takes optional `locals` object\n  - merges data by doing `return merge(view.locals, locals, view.data)`\n  - may override directly to change the behaviour\n\n* `app.context`\n  - method that takes `view` and optional `locals` object\n  - calls the `view.context` before merging data\n  - merges data by doing `return merge({}, this.cache.data, view.context(locals))`\n\n* `options.context`: Customize how the context object is created.\n  - may override functionality through the `context` option:\n\n```js\napp.option('context', function(view, locals) {\n  // this is the app\n  return merge({}, this.cache.data, view.context(), locals);\n});\n```\n\n* `options.helperContext`: Custom how the helper context is created.\n  - may override the functionality used in the `this.ctx()` method in helpers through the `helperContext` option:\n\n```js\napp.option('helperContext', function(view, locals, options) {\n  return merge({}, view.context(), locals);\n});\n```\n\n## Examples\n\n### Installing\n\nClone this project and install the npm modules to run the examples:\n\n```sh\n# clone the project\n$ git clone https://github.com/assemble/context-workshop\n# cd into the folder\n$ cd context-workshop\n# install npm modules\n$ npm install\n# install assemble globally if not already installed\n$ npm install --global assemble\n```\n\n### Running\n\nEach example may be run by using `assemble`:\n\n```sh\n$ assemble \u003cexample\u003e\n```\n\nTo view a list of examples run the default assemble command:\n\n```sh\n$ assemble\n```\n\nTo interactively choose an example to run use the `-i` option:\n\n```sh\n$ assemble -i\n```\n\n### [app-cache-data](examples/01-app-cache-data/assemblefile.js#L15)\n\nAssemble will use `app.cache.data` when rendering views (pages).\nTo add data to `app.cache.data` use the `app.data()` api. See [base-data](https://github.com/node-base/base-data) for all the available options for `app.data()`.\n\nTo run this example:\n\n```sh\n$ assemble app-cache-data\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308022/6e9e6ef0-3931-11e6-82d2-a595b0f798fc.png)\n\nCode snippet from example [assemblefile.js](./examples/01-app-cache-data/assemblefile.js)\n\n```js\n// add app-cache-data\napp.data({title: 'Site Title'});\n\n// create a simple \"button\" partial\napp.partial('button', {content: 'button: \u003c%= title %\u003e'});\n\n// create a simple \"home\" page containing 3 \"button\" partials\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\") %\u003e',\n    'three: \u003c%= partial(\"button\") %\u003e'\n  ].join('\\n')\n});\n\n// render the \"home\" page with no additional data\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.error(err);\n  console.log(res.content);\n});\n```\n\n### [render-locals](examples/02-render-locals/assemblefile.js#L15)\n\nRender locals is the data object that is passed into the `.render()` method when rendering views.\nThe following example will show how the render locals will override data from `app.cache.data` when the context is created.\n\nTo run this example:\n\n```sh\n$ assemble render-locals\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308107/b3287bce-3931-11e6-8c56-2676d4515c24.png)\n\nCode snippet from example [assemblefile.js](./examples/02-render-locals/assemblefile.js)\n\n```js\n// add app-cache-data\napp.data({title: 'Site Title'});\n\n// create a simple \"button\" partial\napp.partial('button', {content: 'button: \u003c%= title %\u003e'});\n\n// create a simple \"home\" page containing 3 \"button\" partials\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\") %\u003e',\n    'three: \u003c%= partial(\"button\") %\u003e'\n  ].join('\\n')\n});\n\n// render the \"home\" page with no additional data\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.log(err);\n  console.log(res.content);\n\n  home.render({title: 'Render Locals Title'}, function(err, res) {\n    if (err) return console.log(err);\n    console.log(res.content);\n  });\n});\n```\n\n### [view-locals](examples/03-view-locals/assemblefile.js#L15)\n\nView locals is the data object that is on view objects that will be used to override `app.cache.data`.\nThe 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.\n\nTo run this example:\n\n```sh\n$ assemble view-locals\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308141/cf0ef08e-3931-11e6-9bbf-008ad3efcbc8.png)\n\nCode snippet from example [assemblefile.js](./examples/03-view-locals/assemblefile.js)\n\n```js\n// add app-cache-data\napp.data({title: 'Site Title'});\n\n// Add a \"button\" partial with view locals data.\n// This data is only overridden by \"render locals\" if the button is rendered directly with `.render` and \"render locals\" are passed into `.render`.\n\napp.partial('button', {\n  content: 'button: \u003c%= title %\u003e',\n  locals: {title: 'Button Locals Title'}\n});\n\n// Add a \"home\" page with view locals data that includes the 3 \"button\" partials.\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\") %\u003e',\n    'three: \u003c%= partial(\"button\") %\u003e'\n  ].join('\\n'),\n  locals: {title: 'Page Locals Title'}\n});\n\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.error(err);\n  console.log(res.content);\n\n  home.render({title: 'Render Locals Title'}, function(err, res) {\n    if (err) return console.error(err);\n    console.log(res.content);\n  });\n});\n```\n\n### [view-data](examples/04-view-data/assemblefile.js#L15)\n\nView data is the data object that is on view objects that will be used to override `app.cache.data`.\nThe following example will show how the view data will override data from `app.cache.data` and \"render locals\" when the context is created.\n\nTo run this example:\n\n```sh\n$ assemble view-data\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308172/eac00a7a-3931-11e6-8006-152eb63185a3.png)\n\nCode snippet from example [assemblefile.js](./examples/04-view-data/assemblefile.js)\n\n```js\n// add app-cache-data\napp.data({title: 'Site Title'});\n\n// Add a \"button\" partial with view locals data and view data.\n// The view data will override `app.cache.data`, \"render locals\", and \"view locals\".\napp.partial('button', {\n  content: 'button: \u003c%= title %\u003e',\n  locals: {title: 'Button Locals Title'},\n  data: {title: 'Button Data Title'}\n});\n\n// Add a \"home\" page with view locals data and view data that includes the 3 \"button\" partials.\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\") %\u003e',\n    'three: \u003c%= partial(\"button\") %\u003e'\n  ].join('\\n'),\n  locals: {title: 'Page Locals Title'},\n  data: {title: 'Page Data Title'}\n});\n\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.error(err);\n  console.log(res.content);\n\n  home.render({title: 'Render Locals Title'}, function(err, res) {\n    if (err) return console.error(err);\n    console.log(res.content);\n  });\n});\n```\n\n### [helper-locals](examples/05-helper-locals/assemblefile.js#L15)\n\nHelper 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.\nThe following example will show how the helper locals will override all other data when rendering a partial view.\n\nTo run this example:\n\n```sh\n$ assemble helper-locals\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308201/0bf8ee1e-3932-11e6-81e8-9eae38234e95.png)\n\nCode snippet from example [assemblefile.js](./examples/05-helper-locals/assemblefile.js)\n\n```js\n// add app-cache-data\napp.data({title: utils.cyan('Site Title')});\n\n// Add a \"button\" partial with view locals data and view data.\n// The view data will override `app.cache.data`, \"render locals\", and \"view locals\".\n// When \"helper locals\" is passed to the \"partial\" helper, all data on the view will be overridden.\napp.partial('button', {\n  content: 'button: \u003c%= title %\u003e',\n  locals: {title: 'Button Locals Title'},\n  data: {title: 'Button Data Title'}\n});\n\n// Add a \"home\" page with view locals data and view data that includes the 3 \"button\" partials.\n// Button \"one\" will be rendered without passing any helper locals.\n// Button \"two\" will be rendered with the \"home\" page's data passed as the helper locals.\n// Button \"three\" will be rendered with a \"custom\" property from the \"render locals\" passed as the helper locals.\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\", obj) %\u003e', // \"obj\" is the built-in global object from engine-base\n    `three: \u003c%= partial(\"button\", {title: 'Helper Locals Title'}) %\u003e`\n  ].join('\\n'),\n  locals: {title: 'Page Locals Title'},\n  data: {title: 'Page Data Title'}\n});\n\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.error(err);\n  console.log(res.content);\n\n  home.render({title: 'Render Locals Title'}, function(err, res) {\n    if (err) return console.error(err);\n    console.log(res.content);\n  });\n});\n```\n\n### [customizing](examples/06-customizing/assemblefile.js#L15)\n\nContext is customizable by adding optional functions to the `app.options` object.\nThis examples shows the ways to customize the context.\n\nTo run this example:\n\n```sh\n$ assemble customizing\n```\n\n![image](https://cloud.githubusercontent.com/assets/995160/16308576/88c480b0-3933-11e6-8873-2e40ddaefaf5.png)\n\nCode snippet from example [assemblefile.js](./examples/06-customizing/assemblefile.js)\n\n```js\n// Add a context option\napp.option('context', function(view, locals) {\n  // override all the other data with the \"render locals\"\n  return extend({}, this.cache.data, view.context(), locals);\n});\n\n// add app-cache-data\napp.data({title: 'Site Title'});\n\n// create a simple \"button\" partial\napp.partial('button', {content: 'button: \u003c%= title %\u003e'});\n\n// create a simple \"home\" page containing 3 \"button\" partials\napp.page('home', {\n  content: [\n    'title: \u003c%= title %\u003e',\n    'one:   \u003c%= partial(\"button\") %\u003e',\n    'two:   \u003c%= partial(\"button\") %\u003e',\n    'three: \u003c%= partial(\"button\") %\u003e'\n  ].join('\\n')\n});\n\n// render the \"home\" page with no additional data\nvar home = app.pages.getView('home');\nhome.render(function(err, res) {\n  if (err) return console.error(err);\n  console.log(res.content);\n});\n```\n\n## About\n\n### Contributing\n\nPull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).\n\n### Building docs\n\n_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_\n\nTo generate the readme and API documentation with [verb](https://github.com/verbose/verb):\n\n```sh\n$ npm install -g verb verb-generate-readme \u0026\u0026 verb\n```\n\n### Running tests\n\nInstall dev dependencies:\n\n```sh\n$ npm install -d \u0026\u0026 npm test\n```\n\n### Author\n\n**Jon Schlinkert**\n\n* [github/jonschlinkert](https://github.com/jonschlinkert)\n* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)\n\n### License\n\nCopyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).\nReleased under the [MIT license](https://github.com/assemble/context-workshop/blob/master/LICENSE).\n\n***\n\n_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on July 18, 2016._","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassemble%2Fcontext-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fassemble%2Fcontext-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassemble%2Fcontext-workshop/lists"}