A Meteor package: Replace already defined templates, inherit helpers and events from other templates
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
.gitignore
.jshintrc
.travis.yml
.versions
LICENSE
README.md
package.js
tests.html
tests.js

README.md

Build Status

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 named propName on the current or an ancestor template instance.
  • use template.set(propName, value) to set the value of the first property named propName 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

$ 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.

Template.onRendered(function () {
  // Initialize all datepicker inputs whenever any template is rendered
  this.$('.datepicker').datepicker();
});

hooks(options)

An alternative syntax to onCreated, onRendered, and onDestroyed.

Template.foo.hooks({
  created() {
    console.log("foo created");
  },
  rendered() {
    console.log("foo rendered");
  },
  destroyed() {
    console.log("foo destroyed");
  },
});

replaces(templateName)

html

<body>
  {{> foo}}
</body>

<template name="foo">
  {{bar}}
  <button type="button">Click</button>
</template>

<template name="foo2">
  {{bar}} 2
  <button type="button">Click 2</button>
</template>

client.js

Template.foo.helpers({
  bar() {
    return "TEST";
  },
});

Template.foo.events({
  'click button'(event, template) {
    console.log("foo button clicked");
  },
});

Template.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

<body>
  {{> foo}}
  {{> foo2}}
</body>

<template name="foo">
  {{bar}}
  <button type="button">Click</button>
</template>

<template name="foo2">
  {{bar}} 2
  <button type="button">Click 2</button>
</template>

client.js

Template.foo.helpers({
  bar() {
    return 'TEST';
  },
});

Template.foo.events({
  'click button'(event, template) {
    console.log('foo button clicked');
  },
});

Template.foo.onCreated(function onCreated() {
  console.log('foo created');
});

Template.foo2.inheritsHelpersFrom('foo');
Template.foo2.inheritsEventsFrom('foo');
Template.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

<body>
  {{> foo}}
  {{> bar}}
</body>

<template name="abstract_foo">
{{#each images}}
   <img src="{{src}}" alt="{{title}}" />
{{/each}}
</template>

client.js

Template.abstract_foo.helpers({
  images() {
    return [];
  }
});

Template.abstract_foo.copyAs(['foo', 'bar']);

Template.foo.helpers({
  images() {
    return Meteor.call('getFooImages');
  }
});

Template.bar.helpers({
  images() {
    return Meteor.call('getBarImages');
  }
});

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:

var 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