/** * A template class that supports advanced functionality like: * * - Autofilling arrays using templates and sub-templates * - Conditional processing with basic comparison operators * - Basic math function support * - Execute arbitrary inline code with special built-in template variables * - Custom member functions * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created * * XTemplate provides the templating mechanism built into {@link Ext.view.View}. * * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples * demonstrate all of the supported features. * * # Sample Data * * This is the data object used for reference in each code example: * * var data = { * name: 'Don Griffin', * title: 'Senior Technomage', * company: 'Sencha Inc.', * drinks: ['Coffee', 'Water', 'More Coffee'], * kids: [ * { name: 'Aubrey', age: 17 }, * { name: 'Joshua', age: 13 }, * { name: 'Cale', age: 10 }, * { name: 'Nikol', age: 5 }, * { name: 'Solomon', age: 0 } * ] * }; * * # Auto filling of arrays * * The **tpl** tag and the **for** operator are used to process the provided data object: * * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl * tag for each item in the array. * - If for="." is specified, the data object provided is examined. * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0). * * Examples: * * ... // loop through array at root node * ... // loop through array at foo node * ... // loop through array at foo.bar node * * Using the sample data above: * * var tpl = new Ext.XTemplate( * '

Kids: ', * '', // process the data.kids node * '

{#}. {name}

', // use current array index to autonumber * '

' * ); * tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object * * An example illustrating how the **for** property can be leveraged to access specified members of the provided data * object to populate the template: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Title: {title}

', * '

Company: {company}

', * '

Kids: ', * '', // interrogate the kids property within the data * '

{name}

', * '

' * ); * tpl.overwrite(panel.body, data); // pass the root node of the data object * * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a * loop. This variable will represent the value of the array at the current index: * * var tpl = new Ext.XTemplate( * '

{name}\'s favorite beverages:

', * '', * '
- {.}
', * '
' * ); * tpl.overwrite(panel.body, data); * * When processing a sub-template, for example while looping through a child array, you can access the parent object's * members via the **parent** object: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '', * '

{name}

', * '

Dad: {parent.name}

', * '', * '

' * ); * tpl.overwrite(panel.body, data); * * # Conditional processing with basic comparison operators * * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render * specific parts of the template. * * Using the sample data above: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '', * '

{name}

', * '', * '

' * ); * tpl.overwrite(panel.body, data); * * More advanced conditionals are also supported: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '

{name} is a ', * '', * '

teenager

', * '', * '

kid

', * '', * '

baby

', * '
', * '

' * ); * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '

{name} is a ', * '', * '', * '

girl

', * '', * '

boy

', * '
', * '

' * ); * * A `break` is implied between each case and default, however, multiple cases can be listed * in a single <tpl> tag. * * # Using double quotes * * Examples: * * var tpl = new Ext.XTemplate( * "Child", * "Teenager", * "...", * '...', * "", * "Hello" * ); * * # Basic math support * * The following basic math operators may be applied directly on numeric data values: * * + - * / * * For example: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '', // <-- Note that the > is encoded * '

{#}: {name}

', // <-- Auto-number each item * '

In 5 Years: {age+5}

', // <-- Basic math * '

Dad: {parent.name}

', * '', * '

' * ); * tpl.overwrite(panel.body, data); * * # Execute arbitrary inline code with special built-in template variables * * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template. * The expression is evaluated and the result is included in the generated result. There are * some special variables available in that code: * * - **out**: The output array into which the template is being appended (using `push` to later * `join`). * - **values**: The values in the current scope. If you are using scope changing sub-templates, * you can change what values is. * - **parent**: The scope (values) of the ancestor template. * - **xindex**: If you are in a looping template, the index of the loop you are in (1-based). * - **xcount**: If you are in a looping template, the total length of the array you are looping. * * This example demonstrates basic row striping using an inline code block and the xindex variable: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Company: {[values.company.toUpperCase() + ", " + values.title]}

', * '

Kids: ', * '', * '

', * '{name}', * '
', * '

' * ); * * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in * the generated code for the template. These blocks are not included in the output. This * can be used for simple things like break/continue in a loop, or control structures or * method calls (when they don't produce output). The `this` references the template instance. * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Company: {[values.company.toUpperCase() + ", " + values.title]}

', * '

Kids: ', * '', * '{% if (xindex % 2 === 0) continue; %}', * '{name}', * '{% if (xindex > 100) break; %}', * '', * '

' * ); * * # Template member functions * * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for * more complex processing: * * var tpl = new Ext.XTemplate( * '

Name: {name}

', * '

Kids: ', * '', * '', * '

Girl: {name} - {age}

', * '', * '

Boy: {name} - {age}

', * '
', * '', * '

{name} is a baby!

', * '
', * '

', * { * // XTemplate configuration: * disableFormats: true, * // member functions: * isGirl: function(name){ * return name == 'Aubrey' || name == 'Nikol'; * }, * isBaby: function(age){ * return age < 1; * } * } * ); * tpl.overwrite(panel.body, data); */ Ext.define('Ext.XTemplate', { extend: 'Ext.Template', requires: 'Ext.XTemplateCompiler', /** * @private */ emptyObj: {}, /** * @cfg {Boolean} compiled * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the * first call to {@link #apply} or {@link #applyOut}. */ /** * @cfg {String/Array} definitions * Optional. A statement, or array of statements which set up `var`s which may then * be accessed within the scope of the generated function. */ apply: function(values, parent) { return this.applyOut(values, [], parent).join(''); }, applyOut: function(values, out, parent) { var me = this, compiler; if (!me.fn) { compiler = new Ext.XTemplateCompiler({ useFormat: me.disableFormats !== true, definitions: me.definitions }); me.fn = compiler.compile(me.html); } try { me.fn.call(me, out, values, parent || me.emptyObj, 1, 1); } catch (e) { // Ext.log('Error: ' + e.message); // } return out; }, /** * Does nothing. XTemplates are compiled automatically, so this function simply returns this. * @return {Ext.XTemplate} this */ compile: function() { return this; }, statics: { /** * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class). * Many times, templates are configured high in the class hierarchy and are to be * shared by all classes that derive from that base. To further complicate matters, * these templates are seldom actual instances but are rather configurations. For * example: * * Ext.define('MyApp.Class', { * someTpl: [ * 'tpl text here' * ] * }); * * The goal being to share that template definition with all instances and even * instances of derived classes, until `someTpl` is overridden. This method will * "upgrade" these configurations to be real `XTemplate` instances *in place* (to * avoid creating one instance per object). * * @param {Object} instance The object from which to get the `XTemplate` (must be * an instance of an {@link Ext#define}'d class). * @param {String} name The name of the property by which to get the `XTemplate`. * @return {Ext.XTemplate} The `XTemplate` instance or null if not found. * @protected */ getTpl: function (instance, name) { var tpl = instance[name], // go for it! 99% of the time we will get it! proto; if (tpl && !tpl.isTemplate) { // tpl is just a configuration (not an instance) // create the template instance from the configuration: tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl); // and replace the reference with the new instance: if (instance.hasOwnProperty(name)) { // the tpl is on the instance instance[name] = tpl; } else { // must be somewhere in the prototype chain for (proto = instance.self.prototype; proto; proto = proto.superclass) { if (proto.hasOwnProperty(name)) { proto[name] = tpl; break; } } } } // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl // is ready to return return tpl || null; } } });