Mostache - Mo Mustache
What and Why
Mostache has a modest goal: unobtrusively add a few key features to standard mustache templates while changing the original mustache library code as little as possible (~10 meaningful splices).
I love mustache, but I felt it could be slightly easier to feed it JSON without re-inventing the wheel or adding dozens of kilobytes of parsing code and while making sure that Mustache performance (currently great with version 0.7+) doesn't suffer from the modification. As a result, virtually any valid mustache template can be use with mostache, so upgrading is easy. Once you switch from mustache to mostache, you pickup the following features:
New Features
-
{INDEX} mini-tag
Note the single-brace. Returns the current index when iterating an Array. ex:
{{#persons}}<li> #{INDEX}. {{firstName}} {{lastName}} </li> {{/persons}}
-
{SEP} mini-section
Note the single-brace. Returns the enclosed block for every value except for the last. ex:
{SEP} <br /> {/SEP}
-
native method detection
Normally mustache passes any functions in the data to a special helper function, which doesn't work with native methods like "".toUpperCase(). Mostache detects natives methods in data and runs them against the data itself. it was always awkward to try to mash in methods to JSON data, but now at least the handy primitive native methods work without fuss. It's harder to explain than use. ex: {{name.toUpperCase}} now produces the data's name property's value in UPPERCASE, instead of the useless "[object Object]". Regular functions in your data still act as expected, it's only the existing hidden primitive methods in your data that run smarter. Mostly, these are for strings, but {{number.toFixed}} will produce an integer from a number, and {{array.sort}} will sort an array as alphabetical case-sensitive text.
-
{|filters}
The biggest improvement: calling helpers from the template instead of the data. Placed after a normal mustache path, filters pass the value to a filter for processing, formatting, or selection. You can use many filters on the same value by separating each one with a pipe ("|"), and the result will be passed left to right through each of the filters. The function is reached by it's gloabl JS path, so any object or function can be used to process, without registering to mustache ahead of time. Also note that if no value if found by the path/text to the left of the first "|", the path itself as a string (or nothing) will be used instead. That allows a particularly neat way of using JS mid-template: {{location|eval}}, or just injecting function returns: {{|Date}}. Finally, a special method indicator prefix (".") in front of the method name forces a method of the value to be executed, for example "".toUpperCase() as {{name|.toUpperCase}}.
Examples
Hover on item for Tooptip of Source Code
Filtering via Globals
- filter used to escape output: {{contacts.1.city|escape}}
- filters used to escape then base64 output: {{contacts.1.city|escape|btoa}}
- filter used to show random number without data: {{|Math.random}}
- filter used to show current Date without data: {{|Date}}
- filter used to show JSON of data: {{numbers|JSON.stringify}}
- filter used to show formatted JSON of data:
{{numbers|JSON.stringify(null,"\t")}}
- .. used to show JSON of current item: {{#contacts.2}} {{..|JSON.stringify}} {{/contacts.2}}
- filters used to limit a function output (0|1): {{{|Math.random|Math.round}}}
- filters used to limit a function output (using .method shortcut): {{{|Math.random|.toFixed(3)}}}
Filtering via user-land functions
- filter used to show min value of data (using expansion methods): min of 1-10= {{numbers|FN.min}}
- filter used to show max value of data (using expansion methods): max of 1-10= {{numbers|FN.max}}
-
these examples use the FN object, defined below as
var FN={ max: Function.apply.bind(Math.max, 0), min: Function.apply.bind(Math.min, 0), time: function(s){return new Date(s).toLocaleTimeString();}, date: function(s){return new Date(s).toLocaleDateString();} }
Filtering via self methods
- filter used to UPPERCASE a value string (using self shortcut): {{name|.toUpperCase}}
- filter used to bold a hard-coded string (using self shortcut): {{some text|.bold}}
- filter used to clone and reverse array (using self shortcuts): {{numbers|.slice|.reverse}}
- filter used to UPPERCASE a value string (using native method behaviour): {{name.toUpperCase}}
Using eval() for fun and profit
- filters used to limit a expression via eval: {{{Math.random()*100|eval|Math.floor}}}
- filter used to make a date pretty: {{new Date()|eval|.toLocaleString}}
- filter used to overload template with live value via eval: location = {{location|eval}}
- filter used to overload template with expression via eval: 123 * 456 = {{ 123 * 456 |eval}}
New mini-tag features
- separators used to build a CSV 1-10: {{#numbers}} {{.}}{SEP}, {/SEP}{{/numbers}}
- INDEX used to spy on a an [a,b,c] array: {{#abc}} {INDEX}={{.}} {{/abc}}
Less-Known Mustache Tricks
- dot used to show 1-10 (mustache native): {{#numbers}} {{.}} {{/numbers}}
- length used to show item count of 1-10 array (mustache native): numbers has {{numbers.length}} items.
- dots used to reach numerical index of Array (mustache native): contact 2's name is "{{contacts.2.first}}"
- parent scope from a sub-object (mustache native): {{#contacts.2}} {{name}} {{/contacts.2}}
- parent scope from a conflicting sub-object property (mustache native): {{#contacts.2}} {{contacts.1.first}} - {{first}} {{abc}} {{/contacts.2}}
- white-space preservation (mustache native):
Data
The example data used in the above demos:
Download
The file is hosted in dan's JS archive. view it here, or download it here.