grips is a simple-logic templating engine written in JavaScript. It's designed to work either/both in the browser or on the server, with the same code-base and the same template files.
grips will "compile" requested templates into JavaScript functions, which takes JSON data as input and returns the rendered string output. The compilation of templates can either be on-demand, or they can be pre-compiled in a build process and used later.
The design philosophy behind grips is not to be remarkable for what it can do, but to be **remarkable for what it cannot do**. That is to say, grips is as restrained in functionality as is necessary to accomplish all reasonable templating tasks.
If you find yourself needing to do something in templating that you cannot do with the features that grips provides, there's a good chance you're doing something you shouldn't be doing in templating. The goal is a minimal but capable set of logic for templating which in its limitations enforces (and encourages) responsible separation-of-concerns.
## Examples
The examples/ directory has several sample template files. Take a look at "tmpl.master.html" and "tmpl.index.html" in particular for good general real-world looking examples. "test.html" is more esoteric and shows off more complexities of the syntax nuances.
## Templating syntax
Template tags (as defined below) have a short/terse form and a long/friendlier form. You can mix and match the forms as you see fit. Examples may use the short form (for conciseness), but there's no reason to infer a preference either way. Some people prefer less templating cruft in their templates, others prefer more readable and less cryptic templates. To each his/her own. Just a matter of taste.
**NOTE:** For simplicity sake, when the engine prints a debug error message where it references a template tag, it always uses the short form for the tag, regardless of what it encountered in the original template.
### Define a named template section (aka, "partial")
Long-form:
```
{$define "#xxx" }
...
...
{$}
```
Short-form:
```
{$: "#xxx" }
...
...
{$}
```
### Define a named template section (partial), with local variable assignment(s)
`$` is the data object (context) passed into the partial.
```
{$: "#xxx" |
x = $.val_1 |
y = $.val_2
}
...
...
{$}
```
#### Conditional Assignments
```
{$: "#bar" |
baz = $.baz ? "yes" : ""
}
baz: {$= baz $}
{$}
```
The `else` condition of a conditional can be omitted and defaults to `""`.
```
{$: "#bar" |
baz = $.baz ? "yes"
}
baz: {$= baz $}
{$}
```
### Let-local Assignments
The Let tag provides a way to make a local variable assignment that only exists for the block it creates, which contains the assignment to a more limited scope than the Define tag and Loop tag assignments.
Long-form:
```
{$let foo = "foo" | bar = "bar" }
...
{$}
```
Short-form:
```
{$# foo = "foo" | bar = "bar" }
...
{$}
```
### Insert/print data property or variable
Long-form:
```
{$insert $.val_1 $} {$insert myval $}
{$print $.val_1 $} {$print myval $}
```
Short-form:
```
{$= $.val_1 $} {$= myval $}
```
### Include template partial, by static literal
Long-form:
```
{$partial "#yyy" $} {$insert @"#yyy" $}
```
Short-form:
```
{$= @"#yyy" $}
```
Template partials can be referenced either by only the (relative) `#partial-ID`, or by a full canonical `collection-ID#partial-ID` reference. If no `collection-ID` is specified, the current containing collection will be assumed.
**NOTE:** The short-form (and `{$insert ... $}`) requires the `@` symbol to distinguish a partial include from a data insertion. The long-form `{$partial ... $}` makes it more explicit in this case.
### Include template partial, by variable
Long-form:
```
{$partial $.val_1 $} {$partial myval $}
```
Short-form:
```
{$= @$.val_1 $} {$= @myval $}
```
### Manually specify data context for template partial include
```
{$= @"#yyy" | $.user $}
```
### Escaping (html, string, url) at the block-level
You can wrap a block-level escape tag around an arbitrary set of template content (inside a partial, of course), and it will cause the output of that section to be escaped according to which escaping rule(s) you specify.
There are 3 types of escaping rules you can choose from: `h` for html encoding, `s` for string (JavaScript, etc) escaping, and `u` for URL encoding/escaping. You can specify more than one rule together, but in most cases you'll probably just use one. If you specify no rule, the default is `s` (string).
The long-form of the rule(s) flag is `escape` or `escape h` or `escape hus`, etc. The short-form of the rule(s) flag is `~` or `~h` or `~hus`, etc.
Long-form:
```
{$: "#yyy" }
Here is {$escape}string "escaped" content (by default){$}
And here is {$escape h}html encoded content{$}
Then, http://some.com/?a={$escape u}http://other.com{$}
Finally, multiple {$escape hu}rules "can" be combined.{$}
{$}
```
Short-form:
```
{$: "#yyy" }
Here is {$~}string "escaped" content (by default){$}
And here is {$~h}html encoded content{$}
Then, http://some.com/?a={$~u}http://other.com{$}
Finally, multiple {$~hu}rules "can" be combined.{$}
{$}
```
### Escaping (html, string, url) at the tag-level
You can apply escaping/encoding rules to a `{$insert .. $}` or `{$partial .. $}` tag directly, without needing to wrap it in a block-level escaping tag. You have the same 3 rule choices as for block-level escaping rules (see above).
The form of the rule(s) flag is `~` or `~h` or `~hus`, etc, regardless of whether the tag is long-form or short-form. **NOTE:** the "long-form" of the rule(s) flag (`escape ..`) itself is not supported for tag-level escaping.
Used with long-form tags:
```
{$: "#yyy" }
Here's a tag-level (string) escaping:
Here's more tag-level (html) escaping with a partial include: {$partial~h "#zzz" $}
{$}
```
Used with short-form tags:
```
{$: "#yyy" }
Here's a tag-level (string) escaping:
Here's more tag-level (html) escaping with a partial include: {$=~h @"#zzz" $}
{$}
```
### Loop on data variable (array or plain key/value object)
`_` is the current iteration binding, and it includes:
* `index` (numeric: zero-based positional index)
* `key` (string: for object iteration, the property name; for array iteration, the `index`)
* `value` (actual item value)
* `first`
* `last`
* `odd`
* `even`
Long-form:
```
{$loop $.val_1 }
...
key: {$insert _.key $} value: {$insert _.value $}
...
{$}
```
Short-form:
```
{$* $.val_1 }
...
key: {$= _.key $} value: {$= _.value $}
...
{$}
```
### Loop on data variable, with loop iteration local variable assignment(s)
```
{$* $.val_1 |
rowtype = _.odd ? "#oddrow" : "#evenrow" |
someprop = _.value.someProp ? "#hassomeprop"
}
...
{$= @rowtype $}
...
{$= @someprop $}
...
{$}
```
### Range Literals
```
{$* [2..7] }
counting: {$= _.value $}
{$}
```
```
{$* [4..-3] }
counting: {$= _.value $}
{$}
```
### Set Literals
```
{$* ["Jan", "Feb", "Mar"] }
month: {$= _.value $}
{$}
```
### Precomputing hash literals
Hash literals can be pre-computed against a defined set or range of values. In the below examples, the value of `$.myradio` will be compared to all values in the range/set (0,1,2 or "low","medium","high"). The results of the comparison and conditional assignment are stored in a local variable hash, keyed by the comparison values. For instance, in the below example, one of the three pre-computation comparisons/assignments the syntax implies is: `checked[1] = ($.myradio === 1) ? "checked" : ""` (same for values 0 and 2).
```
{$: "#bar" |
checked[0..2] = $.myradio ? "checked"
}
{$}
```
Using a loop the above can be even more terse:
```
{$: "#bar" |
checked[0..2] = $.myradio ? "checked"
}
{$* [0..2] }
{$}
{$}
```
Here's a "trick" for even more terseness, by iterating over the pre-computed comparison hash:
```
{$: "#bar" |
options[0..2] = $.myradio ? "checked"
}
{$* options }
{$}
{$}
```
Pre-computation with a set-literal:
```
{$: "#bar" |
checked["low","medium","high"] = $.myradio ? "checked"
}
{$}
```
### "Extend" (inherit from) another template collection
Long-form:
```
{$extend "collection-ID" $}
```
Short-form:
```
{$+ "collection-ID" $}
```
Template collections are an arbitrary grouping of one or more template partials. Usually a template collection corresponds to a file. A template collection can "extend" another template collection, in a similar way you'd be used to having one class extend another class.
A template collection that extends another collection means that it "inherits" the template partials from the collection it extends. You can reference those template partials in template-includes, even if they don't exist in the current template collection. You can also override a template partial that was inherited, simply by defining it in the current collection.
If you reference a partial for inclusion, the engine will start at the appropriate collection level and look for the partial there, and if not found, will walk up the extension chain, if any, looking for a matching partial.
For example, collection "foo":
```
{$: "#baz" } baz {$}
{$: "#bam" } bam {$}
```
And collection "bar":
```
{$+ "foo" $}
{$: "#baz" }
Foobar {$= @"foo#baz" $} {$= @"#bam" $}
{$}
```
In this example, `bar` extends `foo`, and `#baz` and `#bam` are inherited from `foo` into `bar`. `#baz` is redefined in `bar`, but the original inherited version can be referenced by giving the full `foo#baz` template reference. Finally, since `#bam` was inherited, it can be referenced even without the full template reference.
### Raw unparsed section
Long-form:
```
{$raw
this stuff {$= won't be parsed $}, just passed through raw
%$}
```
Short-form:
```
{$%
this stuff {$= won't be parsed $}, just passed through raw
%$}
```
### Template comment block
Long-form:
```
{$comment comments get removed
in parsing /$}
```
Short-form:
```
{$/ comments get removed
in parsing /$}
```
## Debug vs. Non-Debug
grips can either be used in debug mode or non-debug mode, controlled by which version of the library file/module you include. The debug version of the library has friendly error handling, and also produces compiled templates with friendly error handling, whereas the non-debug library (and templates compiled by it) will simply throw a generic "Unknown error" for any errors encountered.
It is recommended that during development you use the debug version of the library, as it will greatly assist in understanding grips template syntax and behavior. But once you deploy grips and/or compiled templates to production, use the non-debug version of the library, because both the library and the compiled templates will be significantly smaller with debug bits stripped.
For browser usage (either basic or AMD-style), choose the appropriate file with or without the "-debug" in the filename. For node.js module usage, you select which version of the library you want to use directly on the included module.
```js
var grips_nondebug = require("grips").grips,
grips_debug = require("grips").debug;
```
## Full Compiler vs. Runtime
For browser usage (either basic of AMD-style), you can choose to include the full compiler, or just the runtime bits. The runtime is all that's required if you only plan to render pre-compiled templates. If you need to actually compile templates in the browser (usually pretty rare), include the full lib. Otherwise, you should include the **much smaller** runtime only.
The most typical production usage pattern would be to have a build process (see the `grips` CLI tool section below) that precompiles your templates, and includes them all together in a single script file (ex: "template-bundle.js"). In that scenario, you'd most likely want to **prepend** the runtime library file to the **beginning** of that template bundle file, so you'd only need to load that one combined file in the browser.
## Installing, Deploying
If you plan to use grips only in a browser, simply download the appropriate file(s) from the "deploy" directory of this repository, and use them in your page using a standard `