Skip to content

Cht syntax

...Max... edited this page Sep 22, 2013 · 90 revisions

CHT Language Syntax

The input language of the CHT engine has minimalistic syntax designed to preserve the usual structure and flow of the HTML. The entire input sequence (a .cht file) consists of template definitions. Each template definition may contain a mix of HTML and CHT elements with appropriate text content. The text content as well as the HTML attribute values may also contain substitution expressions used to embed computed values into the HTML output.

CHT tag and element syntax

<? [ Prefix.] TagName [ Argument ] [ KeywordArgument … ] ?>

The CHT tags are enclosed in <? and ?> brackets, reminiscent of the XML processing instructions. Simple elements have no content; structured elements may have content (HTML, CHT and/or text), optionally separated into sections:

<? [ Prefix.] TagName [ Argument ] [ KeywordArgument … ] ?>

Content
[
<? SectionName [ Argument ] ?>
Content
]

<? /TagName ?>

CHT elements may have one positional argument which is either a simple text string that does not contain spaces or characters =, <, > or a quoted string enclosed in either double " or single ' quotes. Some CHT elements may have keyword arguments that follow the standard HTML syntax, except that characters <, > and & are allowed within their values. Note that characters <, > and & are generally allowed in quoted arguments within CHT tags but not within HTML tags embedded in CHT, where standard rules apply. In most cases, the argument values are Q+ expressions.

Note that sections may have positional arguments as well, but they don’t have the equivalent of closing tags: a section ends where another section begins or where the enclosing structured element ends. Content between the opening tag and the first section syntactically belongs to a “default” or “unnamed” section; this may or may not be valid semantically depending on the nature of the structured element.

Aside from the tags used in template definitions (<?template?> and <?section?>) CHT provides a few built-in elements and allows the user to define both simple and structured elements of his own. At the moment, user-defined structured elements have one limitation: their sections cannot repeat (i.e. cannot be used more than once within the same element).

Qualified template names

As noted above, the user-defined elements may have either simple, or qualified names (i.e. prefixed with multiple dot-separated identifiers). Qualified names are used to refer to templates defined outside of the current file: the CHT loader maps prefix to location of the module that defines the template.

Context-dependent template lookup

While a qualified element name always refers to an externally defined template, a simple name may refer to one of three things, in this order of precendence:

  1. A CHT primitive;
  2. A template defined in the same namespace (i.e. the same file);
  3. A template defined in the same namespace as one of the structured user-defined elements enclosing the context where the simple name was used.
    The latter case provides a convenient shortcut for CHT template libraries which may provide multiple related user-defined elements (for example, <?table?>, <?row?> and <?cell?>). Using this shortcut, the code using these custom library elements can be written as:
<? myLibrary.table ?>
  <? row ?>
    <? cell ?> A <? /cell ?> <? cell ?> B <? /cell ?>
  <? /row ?>
  <? row ?>
    <? cell ?> C <? /cell ?> <? cell ?> D <? /cell ?>
  <? /row ?>
<? /table ?>

without having to specify the library prefix more than once.

Note that nested enclosing elements would be inspected for the purposes of the name lookup in the inside-out order and the first namespace to actually contain the specified template name will be used.

Attributes on user-defined elements

The user-defined CHT elements may also utilize a form of keyword arguments made to resemble the regular HTML attributes. Template definitions do not specify which attributes (or keyword arguments) the template accepts, instead, any and all attributes present in an element referring to a template are placed into the attributes slot of the scope. Thus, the following statement

<? myTemplate attr1="expr1" ... attrN="exprN" ?>

is equivalent to an activation of the same template within an explicit scope:

<? scope attributes="extend:{attr1:expr1,...,attrN=exprN}" ?>
  <? myTemplate ?>
<? /scope ?>

Note that the attributes slot is always marked with extend: tag, adding new attributes to the ones already defined in the current scope. To hide a specific attribute, set it to null. To pass an empty attribute dictionary to a template from a context where attributes may already be present in the scope, use:

<? scope attributes="(null)" ?>
  <? myTemplate ?>
<? /scope?>

Attribute values are evaluated using the current input at a point where the user-defined element appeared; they are not affected by the positional argument passed to the template.

Literal arguments

All built-in CHT elements except <?template?> as well as all user-defined elements may use an alternative way of specifying values for positional and keyword arguments. Prefixing the argument value with @ character marks it as literal argument: it will be processed as plain text with substitutions; the resulting text will form a string value passed into the element. The @ character should be added in front of the leading quote or in front of the argument value itself if it is not enclosed in quotes. This is especially useful when passing actual HTML attributes to a template, for example:

<? myDiv class=@"header" style=@"color:red" ?>

where myDiv is defined as

<? template myDiv ?>
  <div {{attributes:$@.attributes}}>...</div>
<? /template ?>

Substitution syntax

{{ Q+Expression }}

The substitution expressions use the Q+ syntax. Substitution expressions are processed within any text content and within HTML argument values but not within CHT tags, including argument values (which tend to be Q+ expressions themselves anyway). All substitution expressions are evaluated in singleton mode i.e. they are not generators (details here).

<? template ?>

Simple template definitions

<? template Name [ Attribute = Value … ] ?>

Content
<? /template ?>

References to simple user-defined elements, i.e. <? Name [ Argument ] ?> are replaced with expansions of their content based on the passed in argument value. If the argument is omitted, the template is evaluated against current input determined from the context where its reference was encountered.

Depending on the attribute settings, some templates are not expanded inline in the Javascript code produced by the jtlc compiler, but compiled into separate functions. That distinction does not affect the resulting HTML expansions produced by executing the evaluators.

Structured template definitions

<? template Name [ Attribute = Value … ] ?>

Content
<? section [ SectionName ] ?>
DefaultSectionContent
<? /section ?>
<? /template ?>

References to structured user-defined elements are replaced with expansions of the template-level content. Section content supplied at the point of reference is substituted in place of section definitions unless a particular section is omitted; DefaultSectionContent is used in its place then. Content between the opening tag of the template reference and the first section tag is substituted in place of the default/unnamed section (which cannot have default content). Arguments may be passed to the template itself (which applies to the template-level content as well as to the default/unnamed section, if present) and to any of the sections.

Structured templates are never compiled separately; instantiating them directly from Javascript code results in an expansion containing only default content provided by the template definition.

Template attributes

Name Acceptable values Applies to Description
compiled true Simple templates Designates the template as separately compiled.
macro true Templates expanded inline Specifies that the template arguments are evaluated at the point of use within the template. This allows passing generators into templates and may lead to multiple evaluations of the argument.
async true Simple templates Designates the template as asynchronously evaluated (implies compiled="true"). This is necessary only for recursive templates where the compiler may not yet know at the point of reference whether to generate the code for a potentially asynchronous call or for a straightforward function call.
marker HTML tag names Simple templates Designates the template as incrementally updated (canUpdateDom() will return true for its instances) and specifies the tag name of the invisible HTML element to be injected as the marker. Implies async="true" and therefore compiled="true".
transition Expression returning a function Any templates Associates a transition callback with the template. In most cases its value is a global name, but it can in fact contain anything that an argument of a Q+ expr: tag may contain. Note that this attribute cannot contain full Q+ queries: tags and pipeline sequences will not be processed. It is important to keep in mind that associating a transition with a template comes into effect only when this template is executed directly from the Javascript code or when it is updated incrementally. More details here.

<? embed ?>

<? embed [ Argument ] template=TemplExpr [ async="false" ] [ Attribute = Value … ] ?>

Evaluates a CHT template determined dynamically by the value of the TemplExpr (with Argument as its current input, if specified) and embeds its instance into that of the calling template. Both the Argument and the TemplExpr are relative to the current input of the <? embed ?> element itself. TemplExpr must synchronously resolve to a compiled CHT template.

Optional argument async="false" may be used as an optimization, to indicate that the template function returned by TemplExpr is guaranteed to evaluate synchronously. In this case, CHT compiler does not flag the context in which <? embed ?> appears as asynchronous which produces more efficient code in simple cases. On the other hand, an incrementally rendered template evaluated with this setting by mistake will only be rendered once and lose all subsequent asynchronous events.

Note that <? embed ?> does not introduce any <? when ?> blocks of its own and cannot work with an asynchronously evaluated TemplExpr. When the latter is desired, use the following pattern:

<? when “TemplExpr | wait | expr:[$,$0]” ?>
	<? embed “$[0]|argument” template=”$[1]” ?>
<? /when ?>

Do not use this pattern with getTemplate() method of the CHT loader as <? load ?> primitive is the simpler and better alternative.

Any extra attributes on <?embed?> are passed to the embedded template; their values are evaluated using the current input of the <? embed ?> element.

<? foreach ?>

<? foreach [ Generator ] ?>

Content
<? /foreach ?>

Expands to a concatenation of Content evaluated for every value from Generator. The Generator is a Q+ expression evaluated in an iterative mode; the <? foreach ?> element serves as a sink for the Generator.

<? group ?>

<? group [ Generator ] { key=KeyList | count=Count } ?>

Content
<? /group ?>

A counterpart to the group tag in JXL: expands to a concatenation of Content evaluated for every group formed by splitting the sequence generated by Generator. The splitting can be done either based on KeyList, which is a comma-separated list of Q+ expressions evaluated for every value in sequence or into groups of equal size based on Count.

The content of the <? group ?> element in CHT is logically equivalent to the body argument of the group in JXL: its current input is set to the current subsequence formed according to the grouping rules. To iterate over current subsequence, use a nested <? foreach ?> or a nested <? group ?>.

<? if ?>

<? if [ IfArgument ] ?>
IfContent

[ <? elseif ElseIfArgument ?>

ElseIfContent
… ]
[ <? else ?>
ElseContent
]
<? /if ?>

Expands to IfContent if IfArgument evaluates to a true-like value or to ElseIfContent if the corresponding ElseIfArgument does; otherwise expands to ElseContent. All arguments are Q+ expressions evaluated in a singleton context (i.e. they are not generators).

<? load ?>

This primitive is only available to the templates loaded by the CHT loader. It has two forms — the asynchronous one does not make any assumptions about either template’s behavior or its immediate availability to the loader:

<? load [ Argument ] template=TemplName [ Attribute = Value … ] ?>

PlaceholderContent
<? /load ?>

and the synchronous one requires that not only the template itself should evaluate synchronously but also that its body will be immediately available to the CHT loader. This means that the template being loaded should belong to a template module that has already been downloaded and parsed; it does not have to be already compiled. Make sure both conditions hold before using this optimization!

<? load [ Argument ] template=TemplName async="false" ?>

TemplName must synchronously evaluate to a fully qualified name of a template in the form accepted by the getTemplate() method of the CHT loader. After the loader resolves the name and provides the compiled template, the latter is evaluated with Argument as its current input, and its instance is then embedded into the calling template’s instance in the same way <? embed ?> does it.

The asynchronous form of <? load ?> will expand to PlaceholderContent if the template is not immediately available: PlaceholderContent serves the same purpose as the <? else ?> section of the <? when ?> block. The synchronous form will immediately fail in this case; there is no provision equivalent to <? except ?> section because this situation can only occur in case of the bug in the user’s code.

Any extra attributes on <?load?> are passed to the embedded template; their values are evaluated using the current input of the <? load ?> element.

Neither <? load ?> nor <? embed ?> could be used with structured templates.

<? module ?>

<? module [ Argument ] [ transition=" Expression " ] ?>

Content
<? /module ?>

This primitive creates an unnamed separately compiled template from the enclosed CHT fragment and calls it from the point where it is defined, with optional Argument passed in as input.

The primary use for this facility is for creation of library components that combine view and controller functionality. In many cases the controller needs to re-render the template in order to reflect changes in the model; a library template can instantiate a controller passing it a reference to module evaluator function accessible from Q+ queries as $self:

<? template activeElement ?>
  <? module ?>
     <div data-dojo-type="myLibrary.activeElement" 
          data-dojo-props="data:{{ref:$}},template:{{ref:$self}}">
        <? section ?><? /section ?>
     </div>
  <? /module ?>
<? /template ?>

The user of myLibrary.activeElement can thus embed any CHT content inside a div element with an associated controller that retains ability to re-generate this content with updated data at any time!

Please note that Argument is used as input for the module only when evaluating it as part of the enclosing CHT context. Calls to $self from the controller code bypass the Argument expression and specify the input for the enclosed code directly, making it possible to distinguish between these two types of calls within the module code.

<? scope ?>

<? scope [ Argument ] { Slot = Value … }?>

Content
<? /scope ?>

A counterpart to the scope tag in JXL: modifies current scope $@ by adding or overriding its slots. You can specify one or more Slot names in a single <?scope?> element, affecting the Content within. Use Q+ extend: tag to modify the existing slots instead of hiding them completely. As a convenience, you can change current input for the Content by specifying the optional positional Argument; this will not affect the slot values.

<? shuffle ?> and <? cut ?>

<? shuffle [ Argument ] ?>

{
Content
<? cut [ forward=Key ] [ back=Key ] ?>
Content

}
<? /shuffle ?>

The <?shuffle?> and <?cut?> elements are used to re-order or hide individual fragments of content generated by a template at run time, while the template is evaluated. This is a provision for customization of the layouts, that would otherwise be determined by the order of elements within CHT code, by the end user. These elements are processed as follows:

  1. The content of <?shuffle?> is evaluated; optional Argument could be used to modify current input for the content (it does affect the Key expressions in the enclosed <?cut?> elements);
  2. The <?cut?> elements encountered in the content separate it into fragments. Note that <?cut?> elements are not sections of <?shuffle?> and do not have to be present within the same template textually: they could come from nested user-defined elements or sections or even be generated dynamically with <?foreach?> or <?group?>. There is one limitation imposed by the current version of CHT: all <?cut?> elements must appear within the same CHT compilation unit as the enclosing <?shuffle?> element. That is, separately compiled templates cannot inject cuts into a shuffle context of their caller;
  3. Every <?cut?> element may carry up to two fragment keys: forward= key is associated with the fragment after the cut point and back= key is associated with the fragment before the cut point;
  4. After the entire content of <?shuffle?> has been processed, fragments that did not receive a key different from an undefined value, null, false or an empty string are removed;
  5. Finally, the remaining fragments are reordered according to their keys in ascending order. If an element has two keys assigned, the key from the cut point in front of the element (i.e. a forward= key) is used.

<? when ?>

<? when [ Promise ] ?>

FinalContent
[ <? else ?>
PlaceholderContent
]
[ <? except ?>
AlternativeContent
]
<? /when ?>

The basic building block of asynchronous templates, <? when ?> expands to FinalContent when its argument is not a promise or after it has resolved to a value. Otherwise it expands to PlaceholderContent (or to nothing if the <? else ?> part is omitted). The current input of FinalContent is the value of the argument after promise resolution; the current input of PlaceholderContent is the same as that of <? when ?> itself.

If the <? except ?> section is present, a failure to resolve the promise is not communicated to the application code (or to the embedding <? when ?> block if any). Instead, the block expands to AlternativeContent with the Error object passed into the promise’s failure handler as current input. Note that current implementation will not work correctly if the exception raised by the promise is not a subtype (based on instanceof) of Error!