Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,82 @@ In mustache.js an object of partials may be passed as the third argument to
`Mustache.render`. The object should be keyed by the name of the partial, and
its value should be the partial text.

### Dynamic Partials

It is quite common to want to render a partial for each item in a collection.
Sometimes this will be a common partial but often it will be dependent on each
item in the collection.

The implicit partial notation of `{{>.}}` renders a partial who's name comes
from the `partial` key of the current item. The current item becomes the
context of the partial.

View:

{
"beatles": [
{ "name": "John", "partial": "dead" },
{ "name": "Paul", "partial": "alive" },
{ "name": "George", "partial": "dead" },
{ "name": "Ringo", "partial": "alive" }
]
}

Template:

base.mustache
{{#beatles}}
{{>.}}
{{/beatles}}

alive.mustache
* Keep it up {{name}}

dead.mustache
* Rest in peace {{name}}

Output:

* Rest in peace John
* Keep it up Paul
* Rest in peace George
* Keep it up Ringo

#### Partial Collections

As this is such a common pattern, there is a shortcut that renders the named
partial for each item in a collection. The same example can therefore also be
accomplished with the following:

View:

{
"beatles": [
{ "name": "John", "partial": "dead" },
{ "name": "Paul", "partial": "alive" },
{ "name": "George", "partial": "dead" },
{ "name": "Ringo", "partial": "alive" }
]
}

Template:

base.mustache
{{@beatles}}

alive.mustache
* Keep it up {{name}}

dead.mustache
* Rest in peace {{name}}

Output:

* Rest in peace John
* Keep it up Paul
* Rest in peace George
* Keep it up Ringo

### Set Delimiter

Set Delimiter tags start with an equals sign and change the tag delimiters from
Expand Down
24 changes: 22 additions & 2 deletions mustache.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var Mustache;
var nonSpaceRe = /\S/;
var eqRe = /\s*=/;
var curlyRe = /\s*\}/;
var tagRe = /#|\^|\/|>|\{|&|=|!/;
var tagRe = /@|#|\^|\/|>|\{|&|=|!/;

// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
// See https://github.com/janl/mustache.js/issues/189
Expand Down Expand Up @@ -300,7 +300,10 @@ var Mustache;
};

Renderer.prototype._partial = function (name, context) {
var fn = this._partialCache[name];
// If the partial name is a dot, render based on the 'partial' key of
// the current context, otherwise use default behaviour.
var cache = this._partialCache,
fn = (name === '.' ? cache[context.lookup("partial")] : cache[name]);

if (fn) {
return fn(context);
Expand All @@ -309,6 +312,20 @@ var Mustache;
return "";
};

Renderer.prototype._partialCollection = function (name, context) {
var value = context.lookup(name), buffer = "";

// If we have an array, render the partial defined by the 'partial'
// key for each item in the array.
if (isArray(value)) {
for (var i = 0, len = value.length; i < len; ++i) {
buffer += this._partial('.', context.push(value[i]));
}
}

return buffer;
};

Renderer.prototype._name = function (name, context, escape) {
var value = context.lookup(name);

Expand Down Expand Up @@ -355,6 +372,9 @@ var Mustache;
case ">":
body.push("r._partial(" + quote(token.value) + ", c)");
break;
case "@":
body.push("r._partialCollection(" + quote(token.value) + ", c)");
break;
case "text":
body.push(quote(token.value));
break;
Expand Down
6 changes: 6 additions & 0 deletions test/_files/partial_dynamic_collection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
({
items: [
{ content: 'Hello', partial: 'partial' },
{ content: 'Hello', partial: 'partial2' }
]
})
1 change: 1 addition & 0 deletions test/_files/partial_dynamic_collection.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{@items}}
1 change: 1 addition & 0 deletions test/_files/partial_dynamic_collection.partial
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
I am partial 1
1 change: 1 addition & 0 deletions test/_files/partial_dynamic_collection.partial2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
I am partial 2
2 changes: 2 additions & 0 deletions test/_files/partial_dynamic_collection.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
I am partial 1
I am partial 2
7 changes: 7 additions & 0 deletions test/_files/partial_dynamic_collection_implicit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
({
title: 'Dynamic partial collection',
items: [
{ content: 'Hello', partial: 'partial' },
{ content: 'Hello', partial: 'partial2' }
]
})
7 changes: 7 additions & 0 deletions test/_files/partial_dynamic_collection_implicit.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Header
{{#items}}
Before
{{>.}}
After
{{/items}}
Footer
1 change: 1 addition & 0 deletions test/_files/partial_dynamic_collection_implicit.partial
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
I am partial 1
1 change: 1 addition & 0 deletions test/_files/partial_dynamic_collection_implicit.partial2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
I am partial 2
8 changes: 8 additions & 0 deletions test/_files/partial_dynamic_collection_implicit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Header
Before
I am partial 1
After
Before
I am partial 2
After
Footer
4 changes: 3 additions & 1 deletion test/parse_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ var expectations = {
"{{#a}}{{/a}}hi{{#b}}{{/b}}\n" : [ { type: '#', value: 'a', tokens: [] }, { type: 'text', value: 'hi' }, { type: '#', value: 'b', tokens: [] }, { type: 'text', value: '\n' } ],
"{{a}}\n{{b}}\n\n{{#c}}\n{{/c}}\n" : [ { type: 'name', value: 'a' }, { type: 'text', value: '\n' }, { type: 'name', value: 'b' }, { type: 'text', value: '\n\n' }, { type: '#', value: 'c', tokens: [] } ],
"{{#foo}}\n {{#a}}\n {{b}}\n {{/a}}\n{{/foo}}\n"
: [ { type: "#", value: "foo", tokens: [ { type: "#", value: "a", tokens: [ { type: "text", value: " " }, { type: "name", value: "b" }, { type: "text", value: "\n" } ] } ] } ]
: [ { type: "#", value: "foo", tokens: [ { type: "#", value: "a", tokens: [ { type: "text", value: " " }, { type: "name", value: "b" }, { type: "text", value: "\n" } ] } ] } ],
"{{>.}}" : [ { type: '>', value: '.' } ],
"{{@collection}}" : [ { type: '@', value: 'collection' } ]
};

var spec = {};
Expand Down
11 changes: 8 additions & 3 deletions test/render_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ testNames.forEach(function (testName) {
var template = getContents(testName, "mustache");
var expect = getContents(testName, "txt");
var partial = getContents(testName, "partial");
var partial2 = getContents(testName, "partial2");

spec["knows how to render " + testName] = function () {
Mustache.clearCache();

var output;
if (partial) {
output = Mustache.render(template, view, {partial: partial});
var output, partials = {};

if (partial) { partials['partial'] = partial; }
if (partial2) { partials['partial2'] = partial2; }

if (partial || partial2) {
output = Mustache.render(template, view, partials);
} else {
output = Mustache.render(template, view);
}
Expand Down