Partials
edit this pageA partial is a template snippet which can be inserted into templates, or indeed other partials. They help to keep templates uncluttered and easy to read. In this example, we're creating a {{>thumbnail}}
partial:
<!-- the main template -->
<div class='gallery'>
{{#items}}
{{>thumbnail}}
{{/items}}
</div>
<!-- the partial -->
<figure class='thumbnail'>
<img src='assets/thumbnails/{{id}}.jpg'>
<figcaption>{{description}}</figcaption>
</figure>
- Creating partials
- Partial expressions
- Partial context
- Recursive partials
- Injecting partials
- Updating partials
Creating partials
There are several ways to use partials.
As an initialisation option
You can specify partials when you create a Ractive instance:
ractive = new Ractive({
el: myContainer,
template: myTemplate,
partials: { thumbnail: myThumbnailPartial },
data: { items: myItems }
});
Here, myThumbnailPartial
can be a string template or a parsed template.
As a globally available partial
Equally, you can make partials globally available by adding them to Ractive.partials:
Ractive.partials.thumbnail = myThumbnailPartial;
Again, this can be a string template or parsed template - if a string, it will be parsed when it first gets used and stored as a parsed template thereafter.
As a script tag
This method is particularly convenient if you don't want to load templates via AJAX:
<script id='thumbnail' type='text/ractive'>
<figure class='thumbnail'>
<img src='assets/thumbnails/{{id}}.jpg'>
<figcaption>{{description}}</figcaption>
</figure>
</script>
The type
attribute isn't important, as long as it exists and isn't text/javascript
. All that matters is that it's a script tag whose id
attribute matches the name of the partial. It's fairly common to see Ractive templates in script tags with type="text/ractive"
to clearly indicate that they are meant to be handled by Ractive. It can be useful, though, to specify type="text/html"
since many text editors support HTML templates in script tags for templating purposes.
Internally, when a template requests the {{>thumbnail}}
partial, Ractive will look for it on ractive.partials, then Ractive.partials, and if both of those fail it will then look for an element with an id
of thumbnail
. If it exists, it will parse its content and store it on Ractive.partials, to make subsequent lookups quicker.
Inline partials
It is also possible to embed partials within templates. Suppose you have a single mainTemplate.html
file. You can define any partials within it by wrapping them in comments that follow this structure:
<div class='gallery'>
{{#items}}
{{>thumbnail}}
{{/items}}
</div>
<!-- {{>thumbnail}} -->
<figure class='thumbnail'>
<img src='assets/thumbnails/{{id}}.jpg'>
<figcaption>{{description}}</figcaption>
</figure>
<!-- {{/thumbnail}} -->
In this case, the thumbnail
partial won't be globally available - it will only be available to Ractive instances that use this template.
Partial expressions
Partial references may be a name that already exists as a partial, or they may also be any expression that resolves to a partial name. This can be used to select an appropriate partial based on the current context and more. The full power of Ractive's expressions are available. If the value of the expression changes, the partial fragment will be re-rendered using the new partial.
If a partial expression is a simple reference and a partial exists with the same name as the reference, the expression will not be evaluated. Instead, the partial with the same name as the reference will be used. For instance, if there are partials named foo
and bar
, a data member foo: 'bar'
, and a partial section {{>foo}}
, the partial named foo
will be used for the section because name-matching takes precedent over expression evaluation.
Example: Item specialization
{{#list}}{{>.type}}{{/}}
<!-- {{>person}} -->
{{.name}} is a person. {{.pronoun}} has {{.fingerCount}} fingers.
<!-- {{/person}} -->
<!-- {{>animal}} -->
{{.name}} is a {{.specie}}. {{.pronoun}} has {{.legCount}} legs.
<!-- {{/animal}} -->
This example uses a combination of patial expressions and restricted references in the iteration of list
to render a different template based on the type
of item in the list. If there was an item { type: 'person', name: 'John', pronoun: 'He', fingerCount: 9 }
in the list, its output would be John is a person. He has 9 fingers.
. { type: 'animal', name: 'Alfred', specie: 'starfish', pronoun: 'It', legCount: 5 }
would output Alfred is a starfish. It has 5 legs.
.
Example: Partials on-the-fly
<script id="template" type="text/ractive">
Add a partial: <textarea value="{{tpl}}" /><button on-click="add()">Add</button><br/>
{{#list}}{{>makePartial(.id, .template)}}{{/}}
</script>
new Ractive({
template: '#template',
el: 'main',
data: {
list: [],
tpl: '',
makePartial: function(id, template) {
var key = 'partial-' + id;
if (!!this.partials[key]) return key;
this.partials[key] = template;
return key;
}
},
add: function() {
this.push('list', { id: Math.random(), template: this.get('tpl') });
this.set('tpl', '');
}
});
This example uses a function to generate a partial on-the-fly and return the new generated partial name to be rendered. Note that if the template
value for any of the items in list
changes, its corresponding partial will not be updated in this instance. If you need to achieve something like that, you could modify the function to wrap the partial content in a conditional and modify the cacheing logic to re-render the target partial when its template changed.
Invalid names
Since the partial name may be an expression, JavaScript keywords cannot be used in partial expressions. This means that {{>delete}}
is not valid. Keywords and other otherwise-invalid names may be used with a string expression such as {{>'delete'}}
or {{>'invalid-name here'}}
.
Partial context
Partial sections may be given explicit context by adding an expression after the name in the form of {{>[name expression] [context expression]}}
. The partial will then be executed with the given context instead of the context in which the partial section appears. This has the same effect as wrapping the partial section in a #with
section.
{{#list}}{{>somePartial { item: ., magicNumber: 42 }}}{{/}}
In this example, {{item}}
in somePartial
will resolve to the current item in list
and magicNumber
will resolve to 42
. Ancestor references, members, object literals, and any other expressions that resolve to an object may be used as a context expression.
Recursive partials
Partials can be used recursively:
<div class='fileSystem'>
{{#root}}
{{>folder}}
{{/root}}
</div>
<!-- {{>folder}} -->
<ul class='folder'>
{{#files}}
{{>file}}
{{/files}}
</ul>
<!-- {{/folder}} -->
<!-- {{>file}} -->
<li class='file'>
<img class='icon-{{type}}'>
<span>{{filename}}</span>
<!-- if this is actually a folder, embed the folder partial -->
{{# type === 'folder' }}
{{>folder}}
{{/ type === 'folder' }}
</li>
<!-- {{/file}} -->
rv = new Ractive({
el: 'container',
template: '#myTemplate',
data: {
root: {
files: [
{ type: 'jpg', filename: 'hello.jpg' },
{ type: 'mp3', filename: 'NeverGonna.mp3' },
{ type: 'folder', filename: 'subfolder', files: [
{ type: 'txt', filename: 'README.txt' },
{ type: 'folder', filename: 'rabbithole', files: [
{ type: 'txt', filename: 'Inception.txt' }
]}
]}
]
}
}
});
In the example above, subfolders use the {{>folder}}
partial, which uses the {{>file}}
partial for each file, and if any of those files are folders, the {{>folder}}
partial will be invoked again, and so on until there are no more files.
Beware of cyclical data structures! Ractive makes no attempt to detect cyclicality, and will happily continue rendering partials until the Big Crunch (or your browser exceeds its maximum call stack size. Whichever is sooner).
Injecting partials
One good use of partials is to vary the shape of a template according to some condition, the same way you might use dependency injection elsewhere in your code.
For example, you might offer a different view to mobile users:
<div class='main'>
<div class='content'>
{{>content}}
</div>
<div class='sidebar'>
{{>sidebar}}
</div>
</div>
isMobile = /mobile/i.test( navigator.userAgent ); // please don't do this in real life!
ractive = new Ractive({
el: myContainer,
template: myTemplate,
partials: {
content: isMobile ? mobileContentPartial : desktopContentPartial,
sidebar: isMobile ? mobileSidebarPartial : desktopSidebarPartial
}
});
Or you might make it possible to extend a subclass without overriding its template:
<div class='modal-background'>
<div class='modal'>
{{>modalContent}}
</div>
</div>
// Create a Modal subclass
Modal = Ractive.extend({
template: modalTemplate,
init: function () {
var self = this, resizeHandler;
resizeHandler = function () {
self.center();
};
// when the window resizes, keep the modal horizontally and vertically centred
window.addEventListener( 'resize', resizeHandler );
// clean up after ourselves later
this.on( 'teardown', function () {
window.removeEventListener( 'resize', resizeHandler );
});
// manually call this.center() the first time
this.center();
},
center: function () {
// centering logic goes here
}
});
helloModal = new Modal({
el: document.body,
partials: {
modalContent: '<p>Hello!</p><a class="modal-button" proxy-tap="close">Close</a>'
}
});
helloModal.on( 'close', function () {
this.teardown();
});
Updating Partials
Partials may be updated after they are rendered if they are held within a conditional section. They will be re-parsed each time they are re-rendered, if necessary, so string, pre-parsed, and function partials may be used.
{{^toggle}}{{>rickroll}}{{/}}
ractive.partials.rickroll = 'I wouldn\'t do that to you, chum.';
ractive.set('toggle', true);
ractive.set('toggle', false);
This is a little hacky, but it brings an element of dynamism that can be very useful.