-
Notifications
You must be signed in to change notification settings - Fork 11
Keywords
All Keywords are enclosed by {{
Keyword}}
and all Tag Keywords (single statement keywords) start with {{#Keyword
. If you write a block keywords (keywords spanning over multiple other keywords) you always have to close it with another statement that starts with {{/Keyword
or the corresponding alias.
Note: Most keywords within Blocks are indented for the sake of readability. If you care about indentions that you can ether use the
-|
and|-
suffix/prefix to get rid of all leading and tailing whitespaces before and after an keyword (see chapter "Whitespace control") or use the optionTrimTailing = true
orTrimLeading = true
(see chapter "Set Option")
Note: All keywords are case-insensitive but I recommend writing keywords upper-cased to easily distingue them from other parts of your template
Some keywords take an expression. Expressions can be composed of an string and path or an number.
With the use of dots you can navigate down the tree of objects and print whatever is at that location.
Example
{{root.sub.sub.sub.sub.sub}}
by default, everything is HTML encoded that means if you print a string that is containing HTML chars like <
, >
or &
they will be replaced. To change this behavior use the ParserOptions.DisableContentEscaping
flag.
To emit or omit parts of your template you can check if they are matching a DefinitionOfFalse
.
The DefaultDefinitionOfFalse
is currently as folloring:
- value is not null
-
If value is
bool
Then value is notfalse
-
If value is
double
Then value is not0
-
If value is
int
Then value is not0
-
If value is
string
Then value is notString.Empty
-
If value is
IEnumerable
Then value is not Empty
The DefaultDefinitionOfFalse
is stored in a static variable of the ContextObject
but can be overwritten by setting the DefinitionOfFalse
.
To apply this to your template you can ether use the Scopeing keyword (see below) or use the #if
keyword.
Example
{{#IF root.subA.subB}}
{{root.subA.subB}}
{{/IF}}
if you want to omit something that does not match the DefinitionOfFalse
you can use the ^IF
keyword.
Example
{{^IF root.subA.subB}}
{{root.subA.subB}}
{{/IF}}
You can add an else block to any #If and ^If expression just by appending it the the next element
Example
{{#IF root.subA.subB}}
{{root.subA.subB}}
{{/IF}}
There can be any CONTENT but no other expression
{{#ELSE}}
{{root.subA.subC}}
{{/ELSE}}
An else must be a direct neighbour to an if with the exception for content so that this is would be invalid:
Invalid Example
{{#IF root.subA.subB}}
{{root.subA.subB}}
{{/IF}}
There can be any CONTENT but no other expression
{{root.Test}} <--This is not allowed!
{{#ELSE}}
{{root.subA.subC}}
{{/ELSE}}
You can also combine the If&Else together and just write {{#ifelse}}
Example
{{#IF root.subA.subB}}
{{root.subA.subB}}
{{#IFELSE}}
{{root.subA.subC}}
{{/ELSE}}
The #switch
& #case
keyword allows to create one or more branches that will be executed when a certain value or Expression equals one specific value
An #switch
must always enclose all #case
and #default
keywords, if you use the #case
or #default
keyword outside an #switch
, it will print only its children. All #case
blocks will be checked from top to bottom and the first matching block will be executed.
Example
{{#SWITCH data.data}}
{{#CASE 'Test'}}
Value is Test
{{/CASE}}
{{#CASE 'Untest'}}
Value is Untest
{{/CASE}}
{{#DEFAULT}}
Value is neither Test nor Untest
{{/DEFAULT}}
{{/SWITCH}}
If no #case
blocks expression is matching the input from its enclosing #switch
, the #default
block is executed and if not #default
block is present nothing will be printed.
A #switch
keyword does usually not scope its children to the checked value but you can enable this by adding the #scope
keyword
Example
{{#SWITCH data.data #SCOPE}}
{{#CASE 'Test'}}
Value is {{.}}
{{/CASE}}
{{#CASE 'Untest'}}
Value is {{.}}
{{/CASE}}
{{#DEFAULT}}
Value is neither Test nor Untest but {{.}}
{{/DEFAULT}}
{{/SWITCH}}
To change the scope of your current data you can use the #
keyword. If you scope to a path in your data, it has the same effect as the #if
keyword but also allows you to shorten certain very long paths.
Example
{{#root.subA.subB}}
{{.}} <-This emits "yourself"
{{/root.subA.subB}}
while in a scope you can navigate up 1 or more levels with ../
Example
{{#root.subA.subB}}
{{../subB}}
{{/root.subA.subB}}
or go directly back to the root with ~
Example
{{#root.subA.subB}}
{{~root.subA}}
{{/root.subA.subB}}
apart from that the #
keyword does also fit the need for an "if exists" statement. Any value that does not fit the DefinitionOfFalse
will be rendered within a scope.
If you want to render a scope if the DefinitionOfFalse does not match you can use the ^
keyword.
Example
{{^root.subA.subB}} <--this will only scope if the path `root.subA.subB` does _not_ match the DefinitionOfFalse
{{~root.subA}}
{{/root.subA.subB}}
Scopes can be aliased so that you only need to write the path once if you want to access the data.
Example
{{#root.subA.subB AS sub}} <--this will scope to root.subA.subB if it matches the DefinitionOfFalse and put the value of it into "sub"
{{sub.Data}} <--this and the next statement are the same
{{Data}}
{{/sub}} <--when using an alias it also can be used to close the block
you can iterate collections with {{#each path}}
or enumerate objects with {{#each path.?}}
Example Each
{{#EACH root.collection}}
{{prop.a.b}}
{{/EACH}}
Example Every
{{#EACH root.object.?}}
{{Key}} <-Emits the name/key of the property or Key of IDictionary<string,object> respectively
{{Value}} <-Emits the value of the property or item in a Dictionary
{{/EACH}}
Both Each and Every can be aliased.
Example Each
{{#EACH root.collection AS item}} <-- this creates a new variable that contains the element of root.collection
{{item.prop.a.b}}
{{/EACH}}
There are both Do
, While
and Repeat
loops in Morestachio. All accept an expression and Do
& While
will iterate their children as long as the expression does return true
where Repeat
will iterate a fix number of times.
Example Do
{{#DO $index.SmallerAs(5)}}
{{$index}},
{{/DO}}
This example will output the numbers from 1-4 and then stops.
Example Do
{{#WHILE $index.SmallerAs(5)}}
{{$index}},
{{/WHILE}}
This example will output the numbers from 1-5 and then stops.
Example Repeat
{{#REPEAT 10}} <-- this will print {{$index}} exactly 10 times
{{$index}},
{{/REPEAT}}
this example will output the number from 0-9.
When using one of the keywords for enumerating a list, you have also access to special variables related to that loop.
All special variables are prefixed with $
to indicate an generated value:
Name | Description |
---|---|
$first |
Returns a boolean value that indicates whether this is the first step in the loop |
$last |
Returns a boolean value that indicates whether this is the last step in the loop |
$middel |
Returns a boolean value that indicates whether this is nether the first step nor the last step in the loop |
$index |
Returns a the current index step in the loop |
$odd |
Returns a boolean value that indicates that $index is and odd value |
$even |
Returns a boolean value that indicates that $index is and even value |
Note that nevertheless you might have access to
$last
within an#while
and#do
loop but as the template cannot predict when the condition will possibly end this might never be true
You can declare an alias for both #
and #each
keywords. By using the alias you can both close the tag with the alias and use the alias as first element in any expression to access the value like this:
Example Alias
{{#root.data AS rootInData}} {{rootInData.Property}} {{/rootInData}}
{{#each root.data AS rootInData}} {{rootInData.Property}} {{/rootInData}}
By suffixing the Path with "AS Name" you declare the Alias.
If you use an alias that name is also existing in the current object and you want do access the current object instead of the alias you can simply use the "." as first part of you expression.
Partials are reusable templates within your template. You must declare a Partial before you can use it.
Example Partial
{{#DECLARE NAME}}
Any Template {{code}} that will be executed in another scope
{{/DECLARE}}
Within an partial you also have access to the $recursion
variable that indicates the current depth of all nested partials.
You have two options in how to invoke a partial. #import
and #include
.
#include
should be seen as obsolete but will stay functional for now. You should opt to use the newer #import
keyword.
The difference between #import
and #include
is that while #include
does only accept a static name for its partial whoever it does validate whenever that partial does exist at parse-time. #import
on the other hand does allow you to set the name dynamically by using an expression but does validate whenever a partial exists only at render-time.
Example Partial Include vs Import
{{#INCLUDE PartialName}} <-- PartialName is evaluated at parse-time and cannot be changed from within the template
{{#IMPORT 'PartialName'}} <-- the string 'PartialName' is evaluated at render-time
{{#IMPORT Path.To.PartialName.Or.Variable}} <-- also valid as evaluated at render-time and can be dynamicly changed by using an variable for example
It is also possible to set the context the partial should be rendered in by utilizing the #with
inline keyword
Example Partial Include vs Import
{{#IMPORT 'PartialName' #with context.data}} <-- the string 'PartialName' is evaluated at render-time
{{#IMPORT Path.To.PartialName.Or.Variable #with context.data}} <-- also valid as evaluated at render-time and can be dynamicly changed by using an variable for example
{{#IMPORT Path.To.PartialName.Or.Variable #with GetContextForPartialFormatter($name)}} <-- you also have access to the $name variable and could get dynmaic context for your partial
You can also declare templates that imports them self.
Example Hierarchical Partial
{{#DECLARE NAME}}
Any Template {{code}} that will be executed in another scope and includes
itself
{{#IMPORT $name}}
{{/DECLARE}}
Note the usage of the special keyword $name
that is available within any partial to access the string name of the currently executing partial.
You should always define a condition that stops the execution sometimes as the default level (can be modified) of maximum depth is 255.
Example Hierarchical Partial with Condition
{{#DECLARE NAME}}
Any Template {{code}} that will be executed in another scope and includes
itself and stops when a object is not present
{{#IF child}}
{{#IMPORT $name #with child}}
{{/IF}}
{{/DECLARE}}
Please see the dedicated Variables page: https://github.com/JPVenson/morestachio/wiki/Variables
There are 3 Text operations that can modify static content
-
#NL
(NewLine) Adds a linebreak at the position of the tag -
#TNL
(TrimNewLine) Trims one following linebreak in the next content regardless of following Tags -
#TNLS
(TrimNewLines) Trims all following linebreak in the next content regardless of following Tags
You can also use the {{-|
prefix and the |-}}
suffix to removes any whitespace and newlines ether before and/or after a keyword like:
This is a <
{{-| name}}> text
or
This is a <{{name |-}}
> text
or both
This is a <
{{-| name |-}}
> text:
will all output the same text without whitespaces.
The keyword #SET OPTION name = Expression
allows you to set ParseTime options. This kinds of tokens are not emited to the list of Tokens.
List of possible Options:
Name | Description | Example |
---|---|---|
TokenPrefix | Changes the start of an Token from '{{' to an custom string | #SET OPTION TokenPrefix = "<&" |
TokenSuffix | Changes the end of an Token from '}}' to an custom string | #SET OPTION TokenPrefix = "&>" |
TrimLeading | WIP Removes all leading whitespace and newlines from every keyword following | #SET OPTION TrimLeading = true |
TrimTailing | WIP Removes all tailing whitespace and newlines from every keyword following | #SET OPTION TrimTailing = true |