can-stache
Version:
Live binding handlebars templates
574 lines (473 loc) • 16.1 kB
JavaScript
var stache = require('../can-stache');
var stacheTestHelpers = require("./helpers")(document);
var QUnit = require('steal-qunit');
var SimpleMap = require('can-simple-map');
var DefineList = require('can-define/list/list');
var DefineMap = require('can-define/map/map');
var parser = require('can-view-parser');
var string = require('can-string');
var stacheTestHelpers = require("../test/helpers")(document);
QUnit.module("can-stache partials");
QUnit.test("Deeply nested partials", function(assert) {
var t = {
template: "{{#nest1}}{{#nest2}}{{>partial}}{{/nest2}}{{/nest1}}",
expected: "Hello!",
partials: {
partial: stache('{{#nest3}}{{name}}{{/nest3}}')
},
data: {
nest1: {
nest2: {
nest3: {
name: 'Hello!'
}
}
}
}
};
assert.deepEqual(stacheTestHelpers.getText(t.template,t.data, {partials: t.partials}), t.expected);
});
QUnit.test("Partials correctly set context", function(assert) {
var t = {
template: "{{#users}}{{>partial}}{{/users}}",
expected: "foo - bar",
partials: {
partial: stache('{{ name }} - {{ ../company }}')
},
data: {
users: [{
name: 'foo'
}],
company: 'bar'
}
};
assert.deepEqual(stacheTestHelpers.getText(t.template,t.data, {partials: t.partials}), t.expected);
});
QUnit.test("Using a renderer function as a partial", function(assert) {
var template = stache("{{> other}}");
var partial = stache("hello there");
var map = new SimpleMap({ other: null });
var frag = template(map);
assert.equal(stacheTestHelpers.cloneAndClean(frag).firstChild.nodeValue, "", "Initially it is a blank textnode");
map.set("other", partial);
assert.equal(stacheTestHelpers.cloneAndClean(frag).firstChild.nodeValue, "hello there", "partial rendered");
});
QUnit.test("partials are not working within an {{#each}} (#2174)", function(assert) {
var data = new SimpleMap({
items : new DefineList([{
name : 'foo'
}]),
itemRender: stache('{{name}}')
});
var renderer = stache('<div>{{#each items}}{{name}}{{/each}}</div>');
var frag = renderer(data);
data.get('items').get(0).set('name', 'WORLD');
assert.equal( stacheTestHelpers.cloneAndClean(frag).firstChild.innerHTML, "WORLD", "updated to world");
data.get('items').splice(0, 0, {
name : 'HELLO'
});
assert.equal( stacheTestHelpers.cloneAndClean(frag).firstChild.innerHTML, "HELLOWORLD");
});
QUnit.test("partials should leave binding to helpers and properties (#2174)", function(assert) {
stache.registerPartial('test', '<input id="one"> {{name}}');
var renderer = stache('{{#each items}}{{>test}}{{/each}}');
var data = new SimpleMap({ items: new DefineList([]) });
var frag = renderer(data);
data.get('items').splice(0, 0, {name: 'bob'});
// simulate the user entering text
frag.querySelector("input").setAttribute('value', 'user text');
// re-render the partial for the 0th element
data.set('items.0.name', 'dave');
assert.equal(frag.querySelector("input").getAttribute('value'), 'user text');
});
QUnit.test("content within {{#if}} inside partial surrounded by {{#if}} should not display outside partial (#2186)", function(assert) {
stache.registerPartial('partial', '{{#showHiddenSection}}<div>Hidden</div>{{/showHiddenSection}}');
var renderer = stache('<div>{{#showPartial}}{{>partial}}{{/showPartial}}</div>');
var data = new SimpleMap({
showPartial: true,
showHiddenSection: false
});
var frag = renderer(data);
data.set('showHiddenSection', true);
data.set('showPartial', false);
assert.equal( stacheTestHelpers.cloneAndClean(frag).firstChild.innerHTML, '');
});
QUnit.test( "named partials don't render (canjs/can-stache/issues/3)", function(assert) {
var renderer = stache( "{{<foo}}bar{{/foo}}<div></div>" );
var data = new SimpleMap( {} );
var frag = renderer( data );
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).firstChild ), "" );
});
QUnit.test( "named partials can be inserted (canjs/can-stache/issues/3)", function(assert) {
var renderer = stache( "{{<foo}}bar{{/foo}} <span>Test:</span><div>{{>foo}}</div>" );
var data = new SimpleMap( {} );
var frag = renderer( data );
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild ), "bar" );
});
QUnit.test( "named partials can be inserted with an initial scope (canjs/can-stache/issues/3)", function(assert) {
var renderer = stache( "{{<personPartial}}{{lname}}, {{fname}}{{/personPartial}} <span>Test:</span><div>{{>personPartial person}}</div>" );
var data = new SimpleMap({
person: {
fname: "Darryl",
lname: "Anka"
}
});
var frag = renderer( data );
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild ), "Anka, Darryl" );
});
QUnit.test( "named partials work with live binding (canjs/can-stache/issues/3)", function(assert) {
var renderer = stache( "{{<foo}}{{.}}{{/foo}}<span>Test: {{nested.prop.test}}</span>{{#each greatJoy}}<div>{{>foo}}</div>{{/each}}" );
var data = new SimpleMap({
nested: new SimpleMap({
prop: new SimpleMap({
test: "works?"
})
}),
greatJoy: new DefineList([
"happy",
"thrilled",
"ecstatic"
])
});
var frag = renderer( data );
var div = document.createElement( "div" );
div.appendChild( frag );
assert.equal( stacheTestHelpers.innerHTML( div.getElementsByTagName( "span" )[ 0 ] ), "Test: works?", "Named partial property rendered" );
assert.equal( div.getElementsByTagName( "div" ).length, 3, "Named partial list rendered");
data.get( "nested").get("prop").set("test", "works!" );
assert.equal( stacheTestHelpers.innerHTML( div.getElementsByTagName( "span" )[ 0 ] ), "Test: works!", "Named partial updates when attr is updated" );
data.get( "greatJoy").set(0, "quite happy" );
assert.equal(
stacheTestHelpers.innerHTML(
stacheTestHelpers.cloneAndClean( div.getElementsByTagName( "div" )[ 0 ] ) ),
"quite happy",
"Named partial list updates when list item attr is updated" );
data.get( "greatJoy" ).push( "Nintendo Sixty-FOOOOOOOOOOUR" );
assert.equal( div.getElementsByTagName( "div" ).length, 4, "Named partial list updates with new item" );
});
QUnit.test('stache can accept an intermediate with a named partial (canjs/can-stache/issues/3)', function(assert) {
var template = "{{<foo}}bar{{/foo}} <span>Test:</span><div>{{>foo}}</div>";
var intermediate = parser( template, {}, true );
var renderer = stache(intermediate);
var data = new SimpleMap( {} );
var frag = renderer( data );
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild ), "bar" );
});
QUnit.test('named partials can reference each other (canjs/can-stache/issues/3)', function(assert) {
var template = "{{<foo}}hello {{>bar}}{{/foo}} {{<bar}}world{{/bar}} <span>Test:</span><div>{{>foo}}</div>";
var intermediate = parser( template, {}, true );
var renderer = stache(intermediate);
var data = new SimpleMap( {} );
var frag = renderer( data );
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild ), "hello world" );
});
QUnit.test( "recursive named partials work (canjs/can-stache/issues/3)", function(assert) {
var renderer = stache( "{{<foo}}<li>{{name}}<ul>{{#each descendants}}{{>foo}}{{/each}}</ul></li>{{/foo}} <ul>{{#with ychromosome}}{{>foo}}{{/with}}</ul>" );
var data = new SimpleMap({
ychromosome: {
name: "AJ",
descendants: [
{
name: "tim",
descendants: []
},
{
name: "joe",
descendants: [
{
name: "chad",
descendants: []
},
{
name: "goku",
descendants: [
{
name: "gohan",
descendants: []
}
]
}
]
},
{
name: "sam",
descendants: []
}
]
}
});
var frag = renderer( data );
var fraghtml = stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild );
assert.equal( (fraghtml.match(/<li>/g) || []).length, 7 );
assert.ok( fraghtml.indexOf( "<li>goku<ul><li>gohan<ul><\/ul><\/li><\/ul><\/li>" ) !== -1 );
});
QUnit.test("Templates can refer to themselves with {{>scope.view .}} (#159)", function(assert) {
var thing = new DefineMap({
child: {
hasThing: true,
child: {
hasThing: false,
child: {
hasThing: true
}
}
}
});
var renderer = stache(
"{{#child}}" +
"<span>" +
"{{#if hasThing}}" +
"{{>scope.view .}}" +
"{{/if}}" +
"</span>" +
"{{/child}}"
);
var view = stacheTestHelpers.cloneAndClean( renderer(thing) );
assert.equal(view.firstChild.firstChild.innerHTML, "", "Got the second span");
assert.equal(view.firstChild.firstChild.firstChild.firstChild, undefined, "It stopped there");
});
QUnit.test("Self-referential templates assume 'this'", function(assert) {
var thing = new DefineMap({
child: {
hasThing: true,
child: {
hasThing: false,
child: {
hasThing: true
}
}
}
});
var renderer = stache(
"{{#child}}" +
"<span>" +
"{{#if hasThing}}" +
"{{>scope.view}}" +
"{{/if}}" +
"</span>" +
"{{/child}}"
);
var view = stacheTestHelpers.cloneAndClean( renderer(thing) );
assert.equal(view.firstChild.firstChild.innerHTML, "", "Got the second span");
assert.equal(view.firstChild.firstChild.firstChild.firstChild, undefined, "It stopped there");
});
QUnit.test("Self-referential templates work with partial templates", function(assert) {
var thing = new DefineMap({
child: {
hasThing: true,
child: {
hasThing: false,
child: {
hasThing: true
}
}
}
});
var renderer = stache(
"{{<somePartial}}" +
"foo" +
"{{/somePartial}}" +
"{{#child}}" +
"<span>" +
"{{#if hasThing}}" +
"{{>somePartial}}" +
"{{>scope.view}}" +
"{{/if}}" +
"</span>" +
"{{/child}}"
);
var view = stacheTestHelpers.cloneAndClean( renderer(thing) );
assert.equal(view.firstChild.firstChild.nodeValue, "foo", "Got the second span");
});
QUnit.test("Self-referential templates can be given scope", function(assert) {
var thing = new DefineMap({
child: {
someProp: 1,
hasThing: true,
child: {
hasThing: false,
child: {
hasThing: true
}
}
}
});
var renderer = stache(
"{{#child}}" +
"<span>" +
"{{someProp}}" +
"{{#if hasThing}}" +
"{{>scope.view someProp}}" +
"{{/if}}" +
"</span>" +
"{{/child}}"
);
var view = stacheTestHelpers.cloneAndClean( renderer(thing) );
assert.equal(view.firstChild.firstChild.nodeValue, "1", "It got the passed scope");
});
QUnit.test("Partials with custom context", function(assert) {
var template;
var div = document.createElement('div');
template = stache("{{>dude dudes}}");
var data = new SimpleMap({
dudes: [
{ name: "austin" },
{ name: "justin" }
]
});
var dom = template(data,{
partials: {
dude: stache("{{#this}}<span>{{name}}</span>{{/this}}")
}
});
div.appendChild(dom);
var spans = div.getElementsByTagName('span');
assert.equal(spans.length, 2, 'Got two dudes');
assert.equal(stacheTestHelpers.innerHTML(spans[0]), 'austin', 'custom context inside');
assert.equal(stacheTestHelpers.innerHTML(spans[1]), 'justin', 'custom context inside');
});
QUnit.test("Partials with nested custom context and parent lookup", function(assert) {
var template;
var div = document.createElement('div');
template = stache("{{#theData}}{{>dude dudes}}{{/theData}}");
var dom = template({
theData: new SimpleMap({
hello: "Hello",
dudes: [
{ name: "austin" },
{ name: "justin" }
]
})
},{
helpers: {
cap: function (name) {
return string.capitalize(name());
}
},
partials: {
dude: stache("{{#this}}<span>{{../../hello}} {{name}}</span>{{/this}}")
}
});
div.appendChild(dom);
var spans = div.getElementsByTagName('span');
assert.equal(spans.length, 2, 'Got two dudes');
assert.equal(stacheTestHelpers.innerHTML(spans[0]), 'Hello austin', 'correct context');
assert.equal(stacheTestHelpers.innerHTML(spans[1]), 'Hello justin', 'and parent lookup worked also');
});
QUnit.test("Partials with custom context and helper", function(assert) {
var template;
var div = document.createElement('div');
template = stache("{{>dude dudes}}");
var data = new SimpleMap({
dudes: new DefineList([
{ name: "austin" },
{ name: "justin" }
])
});
var dom = template(data,{
helpers: {
cap: function (name) {
return string.capitalize(name());
}
},
partials: {
dude: stache("{{#this}}<span>{{cap name}}</span>{{/this}}")
}
});
div.appendChild(dom);
var spans = div.getElementsByTagName('span');
assert.equal(spans.length, 2, 'Got two dudes');
assert.equal(stacheTestHelpers.innerHTML(spans[0]), 'Austin', 'correct context');
assert.equal(stacheTestHelpers.innerHTML(spans[1]), 'Justin', 'and helpers worked also');
});
/*
QUnit.test("can pass values to partials as let scope", function(){
var address = stache("<label>{{street}}, {{city}}</label>");
var view = stache("<div>{{>address street=user1.street city=user2.city}}</div>");
var frag = view({
user1: {street: "Stave", city: "Chicago"},
address: address
});
QUnit.equal(stacheTestHelpers.cloneAndClean(frag).firstChild.firstChild.innerHTML, "Stave, Chicago");
});*/
QUnit.test("inline partials are accessible from call expressions", function(assert){
var view = stache(
"{{<addressPartial}}<address>{{this.street}}</address>{{/addressPartial}}"+
"<div>{{ addressPartial(street=user.street) }}</div>"
);
var frag = view({
user: {
street: "Stave"
}
});
assert.equal(stacheTestHelpers.cloneAndClean(frag).firstChild.firstChild.innerHTML, "Stave");
});
QUnit.test("recursive inline partials are accessible from call expressions", function(assert) {
assert.expect(1);
var view = stache(
"{{<folderPartial}}"+
"<span>{{this.name}}</span>"+
"{{#if(this.folder)}}<div>{{ folderPartial(this.folder) }}</div>{{/if}}"+
"{{/folderPartial}}"+
"<div>{{ folderPartial(this.folder) }}</div>"
);
var frag = view({
folder: {
name: "Parent",
folder: {
name: "Child",
folder: null
}
}
});
var spans = stacheTestHelpers.cloneAndClean(frag).firstChild.getElementsByTagName("span");
var spanText = [].slice.call(spans,0).map(function(span){
return span.innerHTML;
});
assert.deepEqual(spanText, ["Parent", "Child"]);
});
QUnit.test("Scope being overwritten in partials", function(assert) {
var template;
var div = document.createElement('div');
var data = new SimpleMap({
people: new DefineList([{
name: 'matt'
}, {
name: 'justin'
}])
});
template = stache("{{#data}}{{>person people}}{{/data}}");
var dom = template({
data: data
},{
partials: {
person: stache("{{#each(this)}}<span>{{name}}</span>{{/each}}")
}
});
div.appendChild(dom);
var spans = div.getElementsByTagName('span');
assert.equal(spans.length, 2, 'Got two people');
assert.equal(stacheTestHelpers.innerHTML(spans[0]), 'matt', 'correct context');
assert.equal(stacheTestHelpers.innerHTML(spans[1]), 'justin', 'correct context');
// Update the list of people
data.set('people', new DefineList([{
name: 'kevin'
}, {
name: 'austin'
}]));
assert.equal(stacheTestHelpers.innerHTML(spans[0]), 'kevin', 'correct context');
assert.equal(stacheTestHelpers.innerHTML(spans[1]), 'austin', 'correct context');
});
QUnit.test(" call partials stored in LetContext as Call Expressions #649", function(assert) {
var renderer = stache( "{{<foo}}bar{{/foo}} {{ let bar = scope.templateContext.partials.foo }} <p>{{ bar() }}</p>" );
var frag = renderer();
assert.equal( stacheTestHelpers.innerHTML( stacheTestHelpers.cloneAndClean(frag).lastChild ), "bar" );
});
QUnit.test("Named Partials render string result in stringOnly state", function(assert) {
var view = stache(
'{{<addressView}}.someClass { color: #000; }{{/addressView}}' +
'<style>{{addressView()}}</style>'
);
var fragment = view();
assert.equal(fragment.firstChild.innerHTML, '.someClass { color: #000; }', "Partial text is rendered");
});