template-extension
A smart package for Meteor that allows you to:
- iterate over all defined templates easily.
- attach multiple created/rendered/destroyed hooks to a template.
- attach a created/rendered/destroyed hook to all templates.
- override a template but keep its helpers and events.
- inherit the helpers from another template.
- inherit the events from another template.
- extend abstract templates and overwrite their events/helpers.
- use
template.parent(numLevels, includeBlockHelpers)
to access a parent template instance. - use
template.get(propName)
to get the value of the first property namedpropName
on the current or an ancestor template instance. - use
template.set(propName, value)
to set the value of the first property namedpropName
on the current or an ancestor template instance. - pass a function to
Template.parentData(fun)
to get the first data context which passes the test.
Table of Contents generated with DocToc
- Installation
- Compatibility
- Template.forEach(callback)
- Template.forEachCurrentlyRenderedInstance(callback)
- Template.onCreated / Template.onRendered / Template.onDestroyed
- hooks(options)
- replaces(templateName)
- inheritsHelpersFrom(templateName), inheritsEventsFrom(templateName), and inheritsHooksFrom(templateName)
- clearEventMaps()
- copyAs(newTemplateName)
- templateInstance.parent(numLevels, includeBlockHelpers)
- templateInstance.parent(selector, includeBlockHelpers)
- templateInstance.get(fieldName)
- Template.parentData(fun)
- Contributors
Installation
$ meteor add aldeed:template-extension
Compatibility
- Use a 3.x.x release with Meteor 1.0.x or Meteor 1.1.x
- Use a 4.x.x release with Meteor 1.2+
Template.forEach(callback)
Call callback
once for each defined template. Generally, you'll want to call this in a Meteor.startup
function or sometime after all templates have been loaded.
Template.forEachCurrentlyRenderedInstance(callback)
Call callback
once for each template instance that is currently rendered.
Template.onCreated / Template.onRendered / Template.onDestroyed
Run a function whenever any template is created/rendered/destroyed.
1Template.onRendered(function () { 2 // Initialize all datepicker inputs whenever any template is rendered 3 this.$('.datepicker').datepicker(); 4});
hooks(options)
An alternative syntax to onCreated
, onRendered
, and onDestroyed
.
1Template.foo.hooks({ 2 created() { 3 console.log("foo created"); 4 }, 5 rendered() { 6 console.log("foo rendered"); 7 }, 8 destroyed() { 9 console.log("foo destroyed"); 10 }, 11});
replaces(templateName)
html
1<body> 2 {{> foo}} 3</body> 4 5<template name="foo"> 6 {{bar}} 7 <button type="button">Click</button> 8</template> 9 10<template name="foo2"> 11 {{bar}} 2 12 <button type="button">Click 2</button> 13</template>
client.js
1Template.foo.helpers({ 2 bar() { 3 return "TEST"; 4 }, 5}); 6 7Template.foo.events({ 8 'click button'(event, template) { 9 console.log("foo button clicked"); 10 }, 11}); 12 13Template.foo2.replaces("foo");
Whenever {{> foo}}
is used, the contents of the foo2
template will be shown instead. The bar
helper defined on "foo" will be used to resolve {{bar}}
. Clicking the button will still fire the event defined on "foo".
This is useful when a package you are using defines a template for something and you'd like to adjust some things in that template for your app.
NOTE: This simply swaps the render function. Helpers, callbacks, and events assigned to foo2
will not fire when {{> foo}}
is used. Only the foo
helpers, callbacks, and events are used.
inheritsHelpersFrom(templateName), inheritsEventsFrom(templateName), and inheritsHooksFrom(templateName)
html
1<body> 2 {{> foo}} 3 {{> foo2}} 4</body> 5 6<template name="foo"> 7 {{bar}} 8 <button type="button">Click</button> 9</template> 10 11<template name="foo2"> 12 {{bar}} 2 13 <button type="button">Click 2</button> 14</template>
client.js
1Template.foo.helpers({ 2 bar() { 3 return 'TEST'; 4 }, 5}); 6 7Template.foo.events({ 8 'click button'(event, template) { 9 console.log('foo button clicked'); 10 }, 11}); 12 13Template.foo.onCreated(function onCreated() { 14 console.log('foo created'); 15}); 16 17Template.foo2.inheritsHelpersFrom('foo'); 18Template.foo2.inheritsEventsFrom('foo'); 19Template.foo2.inheritsHooksFrom('foo');
In this example, both templates are rendered. Both use the bar
helper defined on "foo" to resolve {{bar}}
. Both fire the click event defined on "foo". The "foo2" template will inherit the foo.created
callback and log 'foo' to the console upon creation.
Additionally, these methods can be called with an array of template names: Template.foo2.inheritsHooksFrom(['foo', 'bar', 'baz']);
Because of the different ways in which helpers, events, and hooks are stored, the ability to override them is different. Helpers are stored internally as a hash, which means that if you inherit a "name" helper and then call myTemplate.helpers({ name: ... })
, the new "name" helper will overwrite the old helper. By contrast, events and hooks are stored in arrays, which means that you cannot override them easily. You may be able to achieve what you want with clearEventMaps()
or looping through the events or hooks to somehow remove those you don't want.
clearEventMaps()
After Template.foo.events({...})
has been called one or more times, you can remove all the added event handlers by calling Template.foo.clearEventMaps()
copyAs(newTemplateName)
html
1<body> 2 {{> foo}} 3 {{> bar}} 4</body> 5 6<template name="abstract_foo"> 7{{#each images}} 8 <img src="{{src}}" alt="{{title}}" /> 9{{/each}} 10</template>
client.js
1Template.abstract_foo.helpers({ 2 images() { 3 return []; 4 } 5}); 6 7Template.abstract_foo.copyAs(['foo', 'bar']); 8 9Template.foo.helpers({ 10 images() { 11 return Meteor.call('getFooImages'); 12 } 13}); 14 15Template.bar.helpers({ 16 images() { 17 return Meteor.call('getBarImages'); 18 } 19});
In this example, we defined "foo" and "bar" templates that get their HTML markup, events, and helpers from a base template, abstract_foo
. We then override the images
helper for "foo" and "bar" to provide template-specific images provided by different Meteor methods. Template.template.copyAs can accept either single template name (in string form), or an array of template names as shown in the above example.
Calling copyAs
is the same as calling inheritsHelpersFrom
, inheritsEventsFrom
, and inheritsHooksFrom
and also copying the render function.
If copyAs is invoked with a string, it returns the newly created template.
If copyAs is invoked with an array, it returns an array of newly created templates.
templateInstance.parent(numLevels, includeBlockHelpers)
On template instances you can now use parent(numLevels)
method to access a parent template instance.
numLevels
is the number of levels beyond the current template instance to look. Defaults to 1.
By default block helper template instances are skipped, but if includeBlockHelpers
is set to true,
they are not.
templateInstance.parent(selector, includeBlockHelpers)
You can also call templateInstance.parent
with function as first argument. The function, known as selector, will be
passed the current template being traversed. If it returns true, we return the template that is currently being traversed,
otherwise, we traverse further up. We traverse up until there are no more templates, in which case, we return null.
templateInstance.get(fieldName)
To not have to hard-code the number of levels when accessing parent template instances you can use
get(fieldName)
method which returns the value of the first property named fieldName
on the current
or ancestor template instances, traversed in the hierarchical order. It traverses block helper template
instances as well. This pattern makes it easier to refactor templates without having to worry about
changes to number of levels.
Template.parentData(fun)
Template.parentData
now accepts a function which will be used to test each data context when traversing
them in the hierarchical order, returning the first data context for which the test function returns true
.
This is useful so that you do not have to hard-code the number of levels when accessing parent data contexts,
but you can use a more logic-oriented approach. For example, search for the first data context which contains
a given field. Or:
1var data = Template.parentData(function (data) {return data instanceof MyDocument;});
Contributing
When submitting a pull request, please add tests for any new features and make sure that all existing tests still pass.
$ meteor test-packages ./
Contributors
- @aldeed
- @grabbou
- @mitar
- @jgladch
- @merlinpatt
- @JoeyAndres