Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / docs / source / XTemplate.html
1 <html>\r
2 <head>\r
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
4   <title>The source code</title>\r
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
7 </head>\r
8 <body  onload="prettyPrint();">\r
9     <pre class="prettyprint lang-js"><div id="cls-Ext.XTemplate"></div>/**
10  * @class Ext.XTemplate
11  * @extends Ext.Template
12  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
13  * <li>Autofilling arrays using templates and sub-templates</li>
14  * <li>Conditional processing with basic comparison operators</li>
15  * <li>Basic math function support</li>
16  * <li>Execute arbitrary inline code with special built-in template variables</li>
17  * <li>Custom member functions</li>
18  * <li>Many special tags and built-in operators that aren't defined as part of
19  * the API, but are supported in the templates that can be created</li>
20  * </ul></div></p>
21  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
22  * <li>{@link Ext.DataView}</li>
23  * <li>{@link Ext.ListView}</li>
24  * <li>{@link Ext.form.ComboBox}</li>
25  * <li>{@link Ext.grid.TemplateColumn}</li>
26  * <li>{@link Ext.grid.GroupingView}</li>
27  * <li>{@link Ext.menu.Item}</li>
28  * <li>{@link Ext.layout.MenuLayout}</li>
29  * <li>{@link Ext.ColorPalette}</li>
30  * </ul></div></p>
31  * 
32  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
33  *   
34  * @constructor
35  * The {@link Ext.Template#Template Ext.Template constructor} describes
36  * the acceptable parameters to pass to the constructor. The following
37  * examples demonstrate all of the supported features.</p>
38  * 
39  * <div class="mdetail-params"><ul>
40  * 
41  * <li><b><u>Sample Data</u></b> 
42  * <div class="sub-desc">
43  * <p>This is the data object used for reference in each code example:</p>
44  * <pre><code>
45 var data = {
46     name: 'Jack Slocum',
47     title: 'Lead Developer',
48     company: 'Ext JS, LLC',
49     email: 'jack@extjs.com',
50     address: '4 Red Bulls Drive',
51     city: 'Cleveland',
52     state: 'Ohio',
53     zip: '44102',
54     drinks: ['Red Bull', 'Coffee', 'Water'],
55     kids: [{
56         name: 'Sara Grace',
57         age:3
58     },{
59         name: 'Zachary',
60         age:2
61     },{
62         name: 'John James',
63         age:0
64     }]
65 };
66  * </code></pre>
67  * </div>
68  * </li>
69  * 
70  * 
71  * <li><b><u>Auto filling of arrays</u></b> 
72  * <div class="sub-desc">
73  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
74  * to process the provided data object:
75  * <ul>
76  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
77  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
78  * array.</li>
79  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
80  * <li>While processing an array, the special variable <tt>{#}</tt>
81  * will provide the current array index + 1 (starts at 1, not 0).</li>
82  * </ul>
83  * </p>
84  * <pre><code>
85 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
86 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
87 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
88  * </code></pre>
89  * Using the sample data above:
90  * <pre><code>
91 var tpl = new Ext.XTemplate(
92     '&lt;p>Kids: ',
93     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
94         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
95     '&lt;/tpl>&lt;/p>'
96 );
97 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
98  * </code></pre>
99  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
100  * to access specified members of the provided data object to populate the template:</p>
101  * <pre><code>
102 var tpl = new Ext.XTemplate(
103     '&lt;p>Name: {name}&lt;/p>',
104     '&lt;p>Title: {title}&lt;/p>',
105     '&lt;p>Company: {company}&lt;/p>',
106     '&lt;p>Kids: ',
107     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
108         '&lt;p>{name}&lt;/p>',
109     '&lt;/tpl>&lt;/p>'
110 );
111 tpl.overwrite(panel.body, data);  // pass the root node of the data object
112  * </code></pre>
113  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
114  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
115  * will represent the value of the array at the current index:</p>
116  * <pre><code>
117 var tpl = new Ext.XTemplate(
118     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
119     '&lt;tpl for="drinks">',
120        '&lt;div> - {.}&lt;/div>',
121     '&lt;/tpl>'
122 );
123 tpl.overwrite(panel.body, data);
124  * </code></pre>
125  * <p>When processing a sub-template, for example while looping through a child array,
126  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
127  * <pre><code>
128 var tpl = new Ext.XTemplate(
129     '&lt;p>Name: {name}&lt;/p>',
130     '&lt;p>Kids: ',
131     '&lt;tpl for="kids">',
132         '&lt;tpl if="age > 1">',
133             '&lt;p>{name}&lt;/p>',
134             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
135         '&lt;/tpl>',
136     '&lt;/tpl>&lt;/p>'
137 );
138 tpl.overwrite(panel.body, data);
139  * </code></pre>
140  * </div>
141  * </li>
142  * 
143  * 
144  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
145  * <div class="sub-desc">
146  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
147  * to provide conditional checks for deciding whether or not to render specific
148  * parts of the template. Notes:<div class="sub-desc"><ul>
149  * <li>Double quotes must be encoded if used within the conditional</li>
150  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
151  * <tt>if</tt> statements should be used.</li>
152  * </ul></div>
153  * <pre><code>
154 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
155 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
156 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
157 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
158 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
159 // no good:
160 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
161 // encode &#34; if it is part of the condition, e.g.
162 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
163  * </code></pre>
164  * Using the sample data above:
165  * <pre><code>
166 var tpl = new Ext.XTemplate(
167     '&lt;p>Name: {name}&lt;/p>',
168     '&lt;p>Kids: ',
169     '&lt;tpl for="kids">',
170         '&lt;tpl if="age > 1">',
171             '&lt;p>{name}&lt;/p>',
172         '&lt;/tpl>',
173     '&lt;/tpl>&lt;/p>'
174 );
175 tpl.overwrite(panel.body, data);
176  * </code></pre>
177  * </div>
178  * </li>
179  * 
180  * 
181  * <li><b><u>Basic math support</u></b> 
182  * <div class="sub-desc">
183  * <p>The following basic math operators may be applied directly on numeric
184  * data values:</p><pre>
185  * + - * /
186  * </pre>
187  * For example:
188  * <pre><code>
189 var tpl = new Ext.XTemplate(
190     '&lt;p>Name: {name}&lt;/p>',
191     '&lt;p>Kids: ',
192     '&lt;tpl for="kids">',
193         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
194             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
195             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
196             '&lt;p>Dad: {parent.name}&lt;/p>',
197         '&lt;/tpl>',
198     '&lt;/tpl>&lt;/p>'
199 );
200 tpl.overwrite(panel.body, data);
201 </code></pre>
202  * </div>
203  * </li>
204  *
205  * 
206  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
207  * <div class="sub-desc">
208  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
209  * in the scope of the template. There are some special variables available in that code:
210  * <ul>
211  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
212  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
213  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
214  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
215  * loop you are in (1-based).</li>
216  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
217  * of the array you are looping.</li>
218  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
219  * </ul>
220  * This example demonstrates basic row striping using an inline code block and the
221  * <tt>xindex</tt> variable:</p>
222  * <pre><code>
223 var tpl = new Ext.XTemplate(
224     '&lt;p>Name: {name}&lt;/p>',
225     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
226     '&lt;p>Kids: ',
227     '&lt;tpl for="kids">',
228        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
229         '{name}',
230         '&lt;/div>',
231     '&lt;/tpl>&lt;/p>'
232 );
233 tpl.overwrite(panel.body, data);
234  * </code></pre>
235  * </div>
236  * </li>
237  * 
238  * <li><b><u>Template member functions</u></b> 
239  * <div class="sub-desc">
240  * <p>One or more member functions can be specified in a configuration
241  * object passed into the XTemplate constructor for more complex processing:</p>
242  * <pre><code>
243 var tpl = new Ext.XTemplate(
244     '&lt;p>Name: {name}&lt;/p>',
245     '&lt;p>Kids: ',
246     '&lt;tpl for="kids">',
247         '&lt;tpl if="this.isGirl(name)">',
248             '&lt;p>Girl: {name} - {age}&lt;/p>',
249         '&lt;/tpl>',
250         // use opposite if statement to simulate 'else' processing:
251         '&lt;tpl if="this.isGirl(name) == false">',
252             '&lt;p>Boy: {name} - {age}&lt;/p>',
253         '&lt;/tpl>',
254         '&lt;tpl if="this.isBaby(age)">',
255             '&lt;p>{name} is a baby!&lt;/p>',
256         '&lt;/tpl>',
257     '&lt;/tpl>&lt;/p>',
258     {
259         // XTemplate configuration:
260         compiled: true,
261         disableFormats: true,
262         // member functions:
263         isGirl: function(name){
264             return name == 'Sara Grace';
265         },
266         isBaby: function(age){
267             return age < 1;
268         }
269     }
270 );
271 tpl.overwrite(panel.body, data);
272  * </code></pre>
273  * </div>
274  * </li>
275  * 
276  * </ul></div>
277  * 
278  * @param {Mixed} config
279  */
280 Ext.XTemplate = function(){
281     Ext.XTemplate.superclass.constructor.apply(this, arguments);
282
283     var me = this,
284         s = me.html,
285         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
286         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
287         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
288         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
289         m,
290         id = 0,
291         tpls = [],
292         VALUES = 'values',
293         PARENT = 'parent',
294         XINDEX = 'xindex',
295         XCOUNT = 'xcount',
296         RETURN = 'return ',
297         WITHVALUES = 'with(values){ ';
298
299     s = ['<tpl>', s, '</tpl>'].join('');
300
301     while((m = s.match(re))){
302         var m2 = m[0].match(nameRe),
303                         m3 = m[0].match(ifRe),
304                 m4 = m[0].match(execRe),
305                 exp = null,
306                 fn = null,
307                 exec = null,
308                 name = m2 && m2[1] ? m2[1] : '';
309
310        if (m3) {
311            exp = m3 && m3[1] ? m3[1] : null;
312            if(exp){
313                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
314            }
315        }
316        if (m4) {
317            exp = m4 && m4[1] ? m4[1] : null;
318            if(exp){
319                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
320            }
321        }
322        if(name){
323            switch(name){
324                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
325                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
326                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
327            }
328        }
329        tpls.push({
330             id: id,
331             target: name,
332             exec: exec,
333             test: fn,
334             body: m[1]||''
335         });
336        s = s.replace(m[0], '{xtpl'+ id + '}');
337        ++id;
338     }
339         Ext.each(tpls, function(t) {
340         me.compileTpl(t);
341     });
342     me.master = tpls[tpls.length-1];
343     me.tpls = tpls;
344 };
345 Ext.extend(Ext.XTemplate, Ext.Template, {
346     // private
347     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
348     // private
349     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
350
351     // private
352     applySubTemplate : function(id, values, parent, xindex, xcount){
353         var me = this,
354                 len,
355                 t = me.tpls[id],
356                 vs,
357                 buf = [];
358         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
359             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
360             return '';
361         }
362         vs = t.target ? t.target.call(me, values, parent) : values;
363         len = vs.length;
364         parent = t.target ? values : parent;
365         if(t.target && Ext.isArray(vs)){
366                 Ext.each(vs, function(v, i) {
367                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
368             });
369             return buf.join('');
370         }
371         return t.compiled.call(me, vs, parent, xindex, xcount);
372     },
373
374     // private
375     compileTpl : function(tpl){
376         var fm = Ext.util.Format,
377                 useF = this.disableFormats !== true,
378             sep = Ext.isGecko ? "+" : ",",
379             body;
380
381         function fn(m, name, format, args, math){
382             if(name.substr(0, 4) == 'xtpl'){
383                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
384             }
385             var v;
386             if(name === '.'){
387                 v = 'values';
388             }else if(name === '#'){
389                 v = 'xindex';
390             }else if(name.indexOf('.') != -1){
391                 v = name;
392             }else{
393                 v = "values['" + name + "']";
394             }
395             if(math){
396                 v = '(' + v + math + ')';
397             }
398             if (format && useF) {
399                 args = args ? ',' + args : "";
400                 if(format.substr(0, 5) != "this."){
401                     format = "fm." + format + '(';
402                 }else{
403                     format = 'this.call("'+ format.substr(5) + '", ';
404                     args = ", values";
405                 }
406             } else {
407                 args= ''; format = "("+v+" === undefined ? '' : ";
408             }
409             return "'"+ sep + format + v + args + ")"+sep+"'";
410         }
411
412         function codeFn(m, code){
413             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
414             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
415         }
416
417         // branched to use + in gecko and [].join() in others
418         if(Ext.isGecko){
419             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
420                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
421                     "';};";
422         }else{
423             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
424             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
425             body.push("'].join('');};");
426             body = body.join('');
427         }
428         eval(body);
429         return this;
430     },
431
432     <div id="method-Ext.XTemplate-applyTemplate"></div>/**
433      * Returns an HTML fragment of this template with the specified values applied.
434      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
435      * @return {String} The HTML fragment
436      */
437     applyTemplate : function(values){
438         return this.master.compiled.call(this, values, {}, 1, 1);
439     },
440
441     <div id="method-Ext.XTemplate-compile"></div>/**
442      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
443      * @return {Function} The compiled function
444      */
445     compile : function(){return this;}
446
447     <div id="prop-Ext.XTemplate-re"></div>/**
448      * @property re
449      * @hide
450      */
451     <div id="prop-Ext.XTemplate-disableFormats"></div>/**
452      * @property disableFormats
453      * @hide
454      */
455     <div id="method-Ext.XTemplate-set"></div>/**
456      * @method set
457      * @hide
458      */
459
460 });
461 <div id="method-Ext.XTemplate-apply"></div>/**
462  * Alias for {@link #applyTemplate}
463  * Returns an HTML fragment of this template with the specified values applied.
464  * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
465  * @return {String} The HTML fragment
466  * @member Ext.XTemplate
467  * @method apply
468  */
469 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
470
471 <div id="method-Ext.XTemplate-XTemplate.from"></div>/**
472  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
473  * @param {String/HTMLElement} el A DOM element or its id
474  * @return {Ext.Template} The created template
475  * @static
476  */
477 Ext.XTemplate.from = function(el){
478     el = Ext.getDom(el);
479     return new Ext.XTemplate(el.value || el.innerHTML);
480 };</pre>    \r
481 </body>\r
482 </html>