Permalink
Browse files

Improvements and fixes for layout templates.

Some reorganization of samples
New layout templates sample page
  • Loading branch information...
1 parent 34f9aff commit ac145e0170a4bf616908a3ceaba0ca2d0a24fa0c @BorisMoore committed Mar 11, 2012
View
3 demos/resources/demos.css
@@ -8,4 +8,5 @@ a { color: #55b}
pre { font-size:10pt; font-weight:bold; }
.inset { padding-left: 18px; }
.box { border: 1px solid #777; padding: 5px; margin: 10px 0 30px; }
-.desc { font-style:italic; margin: 0 0 15px; }
+.desc { font-style:italic; margin: 0 0 15px; }
+pre { border-left: 3px solid #aaa; padding:10px; margin-bottom: 30px;}
View
4 demos/step-by-step/02_compiling-templates-from-strings.html
@@ -32,11 +32,15 @@
});
var details = true;
+
function switchTemplates() {
details = !details;
+
$( this ).text( details ? "Show titles only" : "Show full details" );
+
/* Render using the other named template */
var templateName = details ? "detailTemplate" : "titleTemplate";
+
$( "#movieList" ).html( $.render [templateName ]( movies ));
}
View
8 demos/step-by-step/08_custom-tags.html
@@ -22,11 +22,11 @@
if ( this.props.reverse ) {
// Render in reverse order
for ( var i = array.length; i; i-- ) {
- ret += this.tmpl.render( array[ i - 1 ] );
+ ret += this.renderContent( array[ i - 1 ] );
}
} else {
// Render in original order
- ret += this.tmpl.render( array );
+ ret += this.renderContent( array );
}
return ret;
}
@@ -64,11 +64,11 @@
if ( this.props.reverse ) {
// Render in reverse order
for ( var i = array.length; i; i-- ) {
- ret += this.tmpl.render( array[ i - 1 ] );
+ ret += this.renderContent( array[ i - 1 ] );
}
} else {
// Render in original order
- ret += this.tmpl.render( array );
+ ret += this.renderContent( array );
}
return ret;
}
View
70 demos/step-by-step/10_helper-functions.html → demos/step-by-step/09_helper-functions.html
@@ -10,37 +10,9 @@
<body>
<a href="index.html">Home</a><br />
-<h3>Helper functions and comparison tests</h3>
+<h3>Helper functions</h3>
-<script id="movieTemplate1" type="text/x-jquery-tmpl">
- <tr>
- <td>{{:title}}</td>
- <td>
- {{if ~nullOrEmpty(languages) tmpl="#messageTmpl"/}}
- </td>
- <td>
- {{if languages==null tmpl="#messageTmpl"/}}
- </td>
- </tr>
-</script>
-
-<script id="messageTmpl" type="text/x-jquery-tmpl">
- <b>Warning:</b> <em>No alternate languages</em>
-</script>
-
-<b>Sample 1</b>
-
-<pre>
-{{if ~nullOrEmpty(languages)}}...{{/if}}
- ...
-{{if languages==null}}...{{/if}}
-</pre>
-
-<table><tbody class="header"><tr><th>Title</th><th>{{if ~nullOrEmpty(a)}}</th><th>{{if a==null}}</th></tr></tbody>
- <tbody id="movieList1"></tbody>
-</table>
-
-<script id="movieTemplate2" type="text/x-jquery-tmpl">
+<script id="movieTemplate" type="text/x-jsrender">
<tr>
<td>{{:title}}</td>
<td>
@@ -51,22 +23,17 @@
</tr>
</script>
-<b>Sample 2</b>
-
<pre>
{{:~format(name, "upper")}}
{{if ~nextToLast(#view)}}...
-
$.views.helpers({
format: function( val, format ){
...
- },
-
- test: function( val, format ){
- ...
+ return val.toUpperCase();
+ ...
},
nextToLast: function( view ) {
@@ -78,17 +45,12 @@
</pre>
<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
- <tbody id="movieList2"></tbody>
+ <tbody id="movieList"></tbody>
</table>
<script type="text/javascript">
-
$.views.helpers({
- nullOrEmpty: function( value ) {
- return !value || !value.length;
- },
-
format: function( val, format ) {
var ret;
switch( format ) {
@@ -108,31 +70,17 @@
}
});
- var movies = [
- {
- title: "Meet Joe Black",
- languages: null
- },
- {
- title: "The Mighty",
- languages: []
- },
- {
+ var movie = {
title: "Eyes Wide Shut",
languages: [
{ name: "French" },
{ name: "German" },
{ name: "Spanish" }
]
- }
- ];
-
- $( "#movieList1" ).html(
- $( "#movieTemplate1" ).render( movies )
- );
+ };
- $( "#movieList2" ).html(
- $( "#movieTemplate2" ).render( movies )
+ $( "#movieList" ).html(
+ $( "#movieTemplate" ).render( movie )
);
</script>
View
68 demos/step-by-step/10_comparison-tests.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+ <script src="../../jsrender.js" type="text/javascript"></script>
+ <link href="../resources/demos.css" rel="stylesheet" type="text/css" />
+
+ <link href="../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="index.html">Home</a><br />
+
+<h3>Helper functions and comparison tests</h3>
+
+<script id="movieTemplate" type="text/x-jsrender">
+ <tr>
+ <td>{{:title}}</td>
+ <td>
+ {{if !(languages && languages.length) tmpl="#messageTmpl"/}}
+ </td>
+ <td>
+ {{if languages==null tmpl="#messageTmpl"/}}
+ </td>
+ </tr>
+</script>
+
+<script id="messageTmpl" type="text/x-jsrender">
+ <b>Warning:</b> <em>No alternate languages</em>
+</script>
+
+<pre>
+{{if !(languages && languages.length)}}...{{/if}}
+
+{{if languages==null}}...{{/if}}
+</pre>
+
+<table><tbody class="header"><tr><th>Title</th><th>{{if !(languages && languages.length)}}</th><th>{{if a==null}}</th></tr></tbody>
+ <tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+ var movies = [
+ {
+ title: "Meet Joe Black",
+ languages: null
+ },
+ {
+ title: "The Mighty",
+ languages: []
+ },
+ {
+ title: "Eyes Wide Shut",
+ languages: [
+ { name: "French" },
+ { name: "German" },
+ { name: "Spanish" }
+ ]
+ }
+ ];
+
+ $( "#movieList" ).html(
+ $( "#movieTemplate" ).render( movies )
+ );
+
+</script>
+
+</body>
+</html>
View
79 demos/step-by-step/09_helper-tags.html → ...p-by-step/11_default-values-scenario.html
@@ -13,35 +13,45 @@
<body>
<a href="index.html">Home</a><br />
-<h3>Registering 'helper' tags.</h3>
+<!---------------------- First Example ---------------------->
-<pre>
-{{get languages defaultValue="No languages!"/}}
-
-{{yesNo languages yes="Alternate languages available:" no="No alternate languages"/}}
-
-$.views.tags({
-
- get: function( value ){
- return value || this.defaultValue;
- },
+<h3>Example Scenario: providing default values for data.</h3>
- yesNo: function( value ){
- return value ? this.yes : this.no;
- }
-
-});
+<div class="subhead">The simplest (and best) way: Javascript expression '||':</div>
+<pre>
+{{:languages||'Languages unavailable'}}
</pre>
+<table><tbody class="header"><tr><th>Title</th><th>{{:path}}</th></tr></tbody>
+ <tbody id="movieList1"></tbody>
+</table>
+
<script id="movieTemplate1" type="text/x-jsrender">
<tr>
<td>{{:title}}</td>
<td>
- {{:languages}}
+ {{:languages||'Languages unavailable'}}
</td>
</tr>
</script>
+<!---------------------- Second Example ---------------------->
+
+<div class="subhead">Creating a special custom tag:</div>
+<pre>
+{{get languages defaultValue="No languages!"/}}
+
+$.views.tags({
+ get: function( value ){
+ return value || this.props.defaultValue;
+ }
+});
+</pre>
+
+<table><tbody class="header"><tr><th>Title</th><th>{{get path default="..."}}</th></tr></tbody>
+ <tbody id="movieList2"></tbody>
+</table>
+
<script id="movieTemplate2" type="text/x-jsrender">
<tr>
<td>{{:title}}</td>
@@ -51,6 +61,23 @@
</tr>
</script>
+<!---------------------- Third Example ---------------------->
+
+<div class="subhead">Creating a multi-purpose utility tag:</div>
+<pre>
+{{yesNo languages yes="Alternate languages available:" no="No alternate languages"/}}
+
+$.views.tags({
+ yesNo: function( value ){
+ return value ? this.props.yes : this.props.no;
+ }
+});
+</pre>
+
+<table><tbody class="header"><tr><th>Title</th><th>{{yesNo path yes="..." no="..."}}</th></tr></tbody>
+ <tbody id="movieList3"></tbody>
+</table>
+
<script id="movieTemplate3" type="text/x-jsrender">
<tr>
<td>{{:title}}</td>
@@ -61,27 +88,17 @@
</tr>
</script>
-<table><tbody class="header"><tr><th>Title</th><th>{{:path}}</th></tr></tbody>
- <tbody id="movieList1"></tbody>
-</table>
-
-<table><tbody class="header"><tr><th>Title</th><th>{{get path default="..."}}</th></tr></tbody>
- <tbody id="movieList2"></tbody>
-</table>
-
-<table><tbody class="header"><tr><th>Title</th><th>{{yesNo path yes="..." no="..."}}</th></tr></tbody>
- <tbody id="movieList3"></tbody>
-</table>
+<!---------------------- Code ---------------------->
<script type="text/javascript">
$.views.tags({
get: function( value ){
- return value || this.defaultValue;
+ return value || this.props.defaultValue;
},
- yesNo: function( value, hash ){
- return value ? this.yes : this.no;
+ yesNo: function( value ){
+ return value ? this.props.yes : this.props.no;
}
});
View
116 demos/step-by-step/12_layout-templates.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+ <script src="../../jsrender.js" type="text/javascript"></script>
+ <link href="../resources/demos.css" rel="stylesheet" type="text/css" />
+
+ <link href="../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="index.html">Home</a><br />
+
+<h3>Using layout templates to render arrays along with headers or footers.</h3>
+
+<div class="subhead">Top-level layout:</div>
+<pre>
+$.templates( "moviesLayout", {
+ markup: "#movieTemplate",
+ layout: true
+});
+
+$( "#movieList" ).html(
+ $.render.moviesLayout( movies )
+);
+
+header
+ {{for #data}}
+ item
+ {{/for}}
+footer
+</pre>
+
+
+<div class="subhead">Nested layout:</div>
+<pre>
+{{for languages layout=true}}
+ header
+ {{for #data}}
+ item
+ {{/for}}
+ footer
+{{/for}}
+</pre>
+
+<script id="movieTemplate" type="text/x-jsrender">
+ <tbody class="header">
+ <tr><th colspan="2">{{:length}} movies available:</th></tr>
+ <tr><th>Title</th><th>Languages</th></tr>
+ </tbody>
+ <tbody>
+ {{for #data}}
+ <tr>
+ <td>{{:title}}</td>
+ <td>
+ {{for languages layout=true}}
+ <div>
+ {{if #data}}
+ <strong>{{:length}}</strong> languages available:
+ {{else}}
+ Original version only
+ {{/if}}
+
+ {{for #data}}
+ <em>{{:name}}</em>
+ {{/for}}
+ </div>
+ <div>
+ {{if #data}}
+ Additional languages also in stock
+ {{/if}}
+ </div>
+ {{/for}}
+ </td>
+ </tr>
+ {{/for}}
+</script>
+
+<table id="movieList"></table>
+
+<script type="text/javascript">
+
+ var movies = [
+ {
+ title: "Meet Joe Black"
+ },
+ {
+ title: "Eyes Wide Shut",
+ languages: [
+ { name: "French" },
+ { name: "Mandarin" },
+ { name: "Spanish" }
+ ]
+ },
+ {
+ title: "The Inheritance",
+ languages: [
+ { name: "English" },
+ { name: "Russian" }
+ ]
+ }
+ ];
+
+ $.templates( "moviesLayout", {
+ markup: "#movieTemplate",
+ layout: true
+ });
+
+ $( "#movieList" ).html(
+ $.render.moviesLayout( movies )
+ );
+</script>
+
+</body>
+</html>
+
+
View
0 demos/step-by-step/11_allow-code.html → demos/step-by-step/13_allow-code.html
File renamed without changes.
View
2 demos/step-by-step/11b_allow-code.html → demos/step-by-step/13b_allow-code.html
@@ -24,8 +24,8 @@
...
}
}}
-
</pre>
+
<script id="movieTemplate" type="text/x-jsrender">
<tr>
<td>{{:title}}</td>
View
0 demos/step-by-step/12_without-jquery.html → demos/step-by-step/14_without-jquery.html
File renamed without changes.
View
12 demos/step-by-step/index.html
@@ -18,10 +18,12 @@
<a href="06_template-composition.html">Template composition</a><br />
<a href="07_paths.html">Accessing paths</a><br />
<a href="08_custom-tags.html">Custom tags</a><br />
-<a href="09_helper-tags.html">'Helper' tags</a><br />
-<a href="10_helper-functions.html">'Helper' functions, and comparison tests</a><br />
-<a href="11_allow-code.html">Allowing code - program flow</a><br />
-<a href="11b_allow-code.html">Allowing code - returning content</a><br />
-<a href="12_without-jquery.html">JsRender without jQuery</a><br />
+<a href="09_helper-functions.html">'Helper' functions</a><br />
+<a href="10_comparison-tests.html">Comparison tests</a><br />
+<a href="11_default-values-scenario.html">Default values scenario</a><br />
+<a href="12_layout-templates.html">Layout templates</a><br />
+<a href="13_allow-code.html">Allowing code - program flow</a><br />
+<a href="13b_allow-code.html">Allowing code - returning content</a><br />
+<a href="14_without-jquery.html">JsRender without jQuery</a><br />
</body>
</html>
View
29 jsrender.js
@@ -153,7 +153,7 @@ function renderTag( tag, parentView, converter, content, tagObject ) {
extend( tagObject.ctx, parentView.ctx);
}
- ret = tagFn.apply( tagObject, args.length > 5 ? slice.call( args, 5 ) : [parentView.data] );
+ ret = tagFn.apply( tagObject, args.length > 5 ? slice.call( args, 5 ) : [] );
return ret || ( ret === undefined ? "" : ret.toString()); // (If ret is the value 0 or false or null, will render to string)
}
@@ -268,7 +268,7 @@ function converters( name, converterFn ) {
function renderContent( data, context, parentView, path, index ) {
// Render template against data as a tree of subviews (nested template), or as a string (top-level template).
// tagName parameter for internal use only. Used for rendering templates registered as tags (which may have associated presenter objects)
- var i, l, dataItem, newView, itemWrap, itemsWrap, itemResult, parentContext, tmpl,
+ var i, l, dataItem, newView, itemWrap, itemsWrap, itemResult, parentContext, tmpl, layout,
props = {},
swapContent = index === TRUE,
self = this,
@@ -288,6 +288,13 @@ function renderContent( data, context, parentView, path, index ) {
}
parentView = parentView || jsv.topView;
parentContext = parentView.ctx;
+ layout = tmpl.layout;
+ if ( data === parentView ) {
+ // Inherit the data from the parent view.
+ // This may be the contents of an {{if}} block
+ data = parentView.data;
+ layout = TRUE;
+ }
// Set additional context on views created here, (as modified context inherited from the parent, and be inherited by child views)
// Note: If no jQuery, extend does not support chained copies - so limit extend() to two parameters
@@ -313,7 +320,7 @@ function renderContent( data, context, parentView, path, index ) {
itemsWrap = context.link && sub.onRenderItems;
if ( tmpl ) {
- if ( $.isArray( data ) && !tmpl.layout ) {
+ if ( $.isArray( data ) && !layout ) {
// Create a view for the array, whose child views correspond to each data item.
// (Note: if index and parentView are passed in along with parent view, treat as
// insert -e.g. from view.addViews - so parentView is already the view item for array)
@@ -328,7 +335,7 @@ function renderContent( data, context, parentView, path, index ) {
} else {
// Create a view for singleton data object.
newView = swapContent ? parentView : View( context, path, parentView, data, tmpl, index );
- result += (data || tmpl.layout) ? tmpl.fn( data, newView, jsv ) : "";
+ result += (data || layout) ? tmpl.fn( data, newView, jsv ) : "";
}
parentView.topKey = newView.index;
return itemsWrap ? itemsWrap( result, path, newView.index, tmpl, props ) : result;
@@ -653,8 +660,7 @@ function compile( name, tmpl, parent, options ) {
// tmpl is already compiled, so use it, or if different name is provided, clone it
if ( tmplOrMarkup.fn ) {
if ( name && name !== tmplOrMarkup.name ) {
- tmpl = extend( {}, tmplOrMarkup );
- tmpl.name = name;
+ tmpl = extend( extend( {}, tmplOrMarkup ), options);
} else {
tmpl = tmplOrMarkup;
}
@@ -775,7 +781,10 @@ tags({
}
view.onElse = undefined; // If condition satisfied, so won't run 'else'.
tagObject.path = "";
- return tagObject.renderContent( view.data );
+ return tagObject.renderContent( view );
+ // Test is satisfied, so render content, while remaining in current data context
+ // By passing the view, we inherit data context from the parent view, and the content is treated as a layout template
+ // (so if the data is an array, it will not iterate over the data
};
return view.onElse( this, arguments );
},
@@ -785,11 +794,15 @@ tags({
},
"for": function() {
var i,
+ self = this,
result = "",
args = arguments,
l = args.length;
+ if ( self.props.layout ) {
+ self.tmpl.layout = TRUE;
+ }
for ( i = 0; i < l; i++ ) {
- result += this.renderContent( args[ i ]);
+ result += self.renderContent( args[ i ]);
}
return result;
},
View
20 test/unit/jsrender-tests-no-jquery.js
@@ -182,7 +182,7 @@ test("{{for}}", function() {
jsviews.templates( {
forTmpl: "header_{{for people}}{{:name}}{{/for}}_footer",
layoutTmpl: {
- markup: "header_{{for}}{{:name}}{{/for}}_footer",
+ markup: "header_{{for #data}}{{:name}}{{/for}}_footer",
layout: true
},
pageTmpl: '{{for people tmpl="layoutTmpl"/}}',
@@ -191,7 +191,7 @@ test("{{for}}", function() {
});
equal( jsviews.render.forTmpl({ people: people }), "header_JoBill_footer", '{{for people}}...{{/for}}' );
- equal( jsviews.render.layoutTmpl( people ), "header_JoBill_footer", 'layout: true... "header_{{for}}{{:name}}{{/for}}_footer"' );
+ equal( jsviews.render.layoutTmpl( people ), "header_JoBill_footer", 'layout: true... "header_{{for #data}}{{:name}}{{/for}}_footer"' );
equal( jsviews.render.pageTmpl({ people: people }), "header_JoBill_footer", '{{for people tmpl="layoutTmpl"/}}' );
equal( jsviews.templates( "{{for people towns}}{{:name}}{{/for}}" ).render({ people: people, towns: towns }), "JoBillSeattleParisDelhi", "concatenated targets: {{for people towns}}" );
@@ -270,7 +270,7 @@ test("render", function() {
equal( tmpl1.render( person ), "A_Jo_B", 'tmpl1.render( data );' );
equal( jsviews.render.myTmpl8( person ), "A_Jo_B", 'jsviews.render.myTmpl8( data );' );
- jsviews.templates( "myTmpl9", "A_{{for}}inner{{:name}}content{{/for}}_B" );
+ jsviews.templates( "myTmpl9", "A_{{for #data}}inner{{:name}}content{{/for}}_B" );
equal( jsviews.templates.myTmpl9.tmpls[0].render( person ), "innerJocontent", 'Access nested templates: jsviews.templates["myTmpl9[0]"];' );
equal( jsviews.templates( tmplString ).render( person ), "A_Jo_B", 'Compile from string: var html = jsviews.templates( tmplString ).render( data );' );
@@ -380,9 +380,9 @@ test("template encapsulation", function() {
equal( $.trim( jsviews.render.encapsulate1({ people: people })), "false:tostring BILLJO not2:true:tostring", 'jsviews.templates( "myTmpl", tmplObjWithNestedItems);' );
jsviews.templates({
- willFail: "{{lower a/}}",
- encapsulate2: {
- markup: "{{lower a/}} {{:~concat2(a, 'b', ~not2(false))}} {{for tmpl='nestedTmpl1'/}} {{for tmpl='nestedTmpl2'/}}",
+ useLower: "{{lower a/}}",
+ tmplWithNestedResources: {
+ markup: "{{lower a/}} {{:~concat2(a, 'b', ~not2(false))}} {{for #data tmpl='nestedTmpl1'/}} {{for #data tmpl='nestedTmpl2'/}}",
helpers: {
not2: function( value ) {
return !value;
@@ -434,8 +434,8 @@ test("template encapsulation", function() {
return "contextualNot2" + !value;
}
};
- equal( jsviews.render.encapsulate2({ a: "aVal" }), "aval aValbtrue% (double:aVal&aVal) (override outer double:AVAL|AVAL)", 'jsviews.templates( "myTmpl", tmplObjWithNestedHelpers);' );
- equal( jsviews.render.willFail({ a: "aVal" }), "", 'jsviews.templates( "myTmpl", tmplObjWithNestedHelpers);' );
- equal( jsviews.render.encapsulate2({ a: "aVal" }, context), "aval aValbcontextualNot2true% (double:aVal&aVal) (override outer double:contextualUpperAVAL|contextualUpperAVAL)", 'jsviews.templates( "myTmpl", tmplObjWithNestedHelpers);' );
- equal( jsviews.templates.encapsulate2.templates.templateWithDebug.fn.toString().indexOf("debugger;"), 90, 'Can set debug=true on nested template' );
+ equal( jsviews.render.tmplWithNestedResources({ a: "aVal" }), "aval aValbtrue% (double:aVal&aVal) (override outer double:AVAL|AVAL)", 'Access nested resources from template' );
+ equal( jsviews.render.useLower({ a: "aVal" }), "", 'Cannot access nested resources from a different template' );
+ equal( jsviews.render.tmplWithNestedResources({ a: "aVal" }, context), "aval aValbcontextualNot2true% (double:aVal&aVal) (override outer double:contextualUpperAVAL|contextualUpperAVAL)", 'Resources passed in with context override nested resources' );
+ equal( jsviews.templates.tmplWithNestedResources.templates.templateWithDebug.fn.toString().indexOf("debugger;"), 90, 'Can set debug=true on nested template' );
});

0 comments on commit ac145e0

Please sign in to comment.