XTemplate.html 15.5 KB
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-XTemplate'>/**
</span> * 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=&quot;.&quot; 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:
 *
 *     &lt;tpl for=&quot;.&quot;&gt;...&lt;/tpl&gt;       // loop through array at root node
 *     &lt;tpl for=&quot;foo&quot;&gt;...&lt;/tpl&gt;     // loop through array at foo node
 *     &lt;tpl for=&quot;foo.bar&quot;&gt;...&lt;/tpl&gt; // loop through array at foo.bar node
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;.&quot;&gt;',       // process the data.kids node
 *             '&lt;p&gt;{#}. {name}&lt;/p&gt;',  // use current array index to autonumber
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *     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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Title: {title}&lt;/p&gt;',
 *         '&lt;p&gt;Company: {company}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',     // interrogate the kids property within the data
 *             '&lt;p&gt;{name}&lt;/p&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *     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(
 *         '&lt;p&gt;{name}\'s favorite beverages:&lt;/p&gt;',
 *         '&lt;tpl for=&quot;drinks&quot;&gt;',
 *             '&lt;div&gt; - {.}&lt;/div&gt;',
 *         '&lt;/tpl&gt;'
 *     );
 *     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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;tpl if=&quot;age &amp;gt; 1&quot;&gt;',
 *                 '&lt;p&gt;{name}&lt;/p&gt;',
 *                 '&lt;p&gt;Dad: {parent.name}&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *     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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;tpl if=&quot;age &amp;gt; 1&quot;&gt;',
 *                 '&lt;p&gt;{name}&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * More advanced conditionals are also supported:
 *
 *     var tpl = new Ext.XTemplate(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;p&gt;{name} is a ',
 *             '&lt;tpl if=&quot;age &amp;gt;= 13&quot;&gt;',
 *                 '&lt;p&gt;teenager&lt;/p&gt;',
 *             '&lt;tpl elseif=&quot;age &amp;gt;= 2&quot;&gt;',
 *                 '&lt;p&gt;kid&lt;/p&gt;',
 *             '&lt;tpl else&gt;',
 *                 '&lt;p&gt;baby&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *
 *     var tpl = new Ext.XTemplate(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;p&gt;{name} is a ',
 *             '&lt;tpl switch=&quot;name&quot;&gt;',
 *                 '&lt;tpl case=&quot;Aubrey&quot; case=&quot;Nikol&quot;&gt;',
 *                     '&lt;p&gt;girl&lt;/p&gt;',
 *                 '&lt;tpl default&gt;',
 *                     '&lt;p&gt;boy&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *
 * A `break` is implied between each case and default, however, multiple cases can be listed
 * in a single &amp;lt;tpl&amp;gt; tag.
 *
 * # Using double quotes
 *
 * Examples:
 *
 *     var tpl = new Ext.XTemplate(
 *         &quot;&lt;tpl if='age &amp;gt; 1 &amp;&amp; age &amp;lt; 10'&gt;Child&lt;/tpl&gt;&quot;,
 *         &quot;&lt;tpl if='age &amp;gt;= 10 &amp;&amp; age &amp;lt; 18'&gt;Teenager&lt;/tpl&gt;&quot;,
 *         &quot;&lt;tpl if='this.isGirl(name)'&gt;...&lt;/tpl&gt;&quot;,
 *         '&lt;tpl if=&quot;id == \'download\'&quot;&gt;...&lt;/tpl&gt;',
 *         &quot;&lt;tpl if='needsIcon'&gt;&lt;img src='{icon}' class='{iconCls}'/&gt;&lt;/tpl&gt;&quot;,
 *         &quot;&lt;tpl if='name == \&quot;Don\&quot;'&gt;Hello&lt;/tpl&gt;&quot;
 *     );
 *
 * # Basic math support
 *
 * The following basic math operators may be applied directly on numeric data values:
 *
 *     + - * /
 *
 * For example:
 *
 *     var tpl = new Ext.XTemplate(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;tpl if=&quot;age &amp;gt; 1&quot;&gt;',  // &lt;-- Note that the &gt; is encoded
 *                 '&lt;p&gt;{#}: {name}&lt;/p&gt;',  // &lt;-- Auto-number each item
 *                 '&lt;p&gt;In 5 Years: {age+5}&lt;/p&gt;',  // &lt;-- Basic math
 *                 '&lt;p&gt;Dad: {parent.name}&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *     );
 *     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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Company: {[values.company.toUpperCase() + &quot;, &quot; + values.title]}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;div class=&quot;{[xindex % 2 === 0 ? &quot;even&quot; : &quot;odd&quot;]}&quot;&gt;',
 *             '{name}',
 *             '&lt;/div&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *      );
 *
 * Any code contained in &quot;verbatim&quot; blocks (using &quot;{% ... %}&quot;) 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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Company: {[values.company.toUpperCase() + &quot;, &quot; + values.title]}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '{% if (xindex % 2 === 0) continue; %}',
 *             '{name}',
 *             '{% if (xindex &gt; 100) break; %}',
 *             '&lt;/div&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;'
 *      );
 *
 * # 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(
 *         '&lt;p&gt;Name: {name}&lt;/p&gt;',
 *         '&lt;p&gt;Kids: ',
 *         '&lt;tpl for=&quot;kids&quot;&gt;',
 *             '&lt;tpl if=&quot;this.isGirl(name)&quot;&gt;',
 *                 '&lt;p&gt;Girl: {name} - {age}&lt;/p&gt;',
 *             '&lt;tpl else&gt;',
 *                 '&lt;p&gt;Boy: {name} - {age}&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *             '&lt;tpl if=&quot;this.isBaby(age)&quot;&gt;',
 *                 '&lt;p&gt;{name} is a baby!&lt;/p&gt;',
 *             '&lt;/tpl&gt;',
 *         '&lt;/tpl&gt;&lt;/p&gt;',
 *         {
 *             // XTemplate configuration:
 *             disableFormats: true,
 *             // member functions:
 *             isGirl: function(name){
 *                return name == 'Aubrey' || name == 'Nikol';
 *             },
 *             isBaby: function(age){
 *                return age &lt; 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend: 'Ext.Template',

    requires: 'Ext.XTemplateCompiler',

<span id='Ext-XTemplate-property-emptyObj'>    /**
</span>     * @private
     */
    emptyObj: {},

<span id='Ext-XTemplate-cfg-compiled'>    /**
</span>     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     */

<span id='Ext-XTemplate-cfg-definitions'>    /**
</span>     * @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) {
            //&lt;debug&gt;
            Ext.log('Error: ' + e.message);
            //&lt;/debug&gt;
        }

        return out;
    },

<span id='Ext-XTemplate-method-compile'>    /**
</span>     * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
     * @return {Ext.XTemplate} this
     */
    compile: function() {
        return this;
    },

    statics: {
<span id='Ext-XTemplate-method-getTpl'>        /**
</span>         * 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
         * &quot;upgrade&quot; 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 &amp;&amp; !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;
        }
    }
});
</pre>
</body>
</html>