Menu

The Templating Program Data Block

Csaba Skrabák

Parameters of the in-browser templating engine appear in a data block script element in the document (or shadow DOM if it's a component).

    <script type="text/json" id="templating-program">[ /*here is the main program*/ ]</script>

The top of the structure (the main program) is an array of the following kinds of elements.

(1) For an HTML/templating element with quantifiers in the bracy template document: Instantiation Instruction object,

    { 
        "calculate": [
            [ "name1", ... ], /*for explanation, see wiki page "Calculate Language"*/
            ... /*one array for each placeholder in the tag (not counting in the attribute value)*/
        ]
        "template": "{template-id}", /*the ID of the <template> that needs to be instantiated, according to the result*/
        "assign": { "name": [ "name1", ... ], ... }, /* declares new names in the scope of the subprogram below */
        "sub": [ /*the same structure as the main program*/ ],

        /*count limits; omit both fields if t:times="0+"*/
        "min": 1, /*if t:times="1" or "1+"*/
        "max": 1 /*if t:times="1" or "1-"*/
    }

(2) for an HTML/templating element that has no quantifier but t:times attribute: Instantiation object with the "calculate" field omitted,
(3) for a text node (child or attribute value) in which one or more placeholders appear: Substitution Instruction object,

    { 
        "context": "elem-id", /*subject to id translation if in a plural template*/
        "xpath": "text()[1]", /*point to the text node; xpath is relative to the element with the id in "context" field*/
        "parts": [ /*concatenate the following parts in order to get the text content to substitute*/
            "prefix", /*elements that have a JSON type string are taken as a string literal*/
            [ "name1", ... ], /*array typed element: take the result of operations; see wiki page "Calculate Language"*/
            ... /*alternate between string and array elements; parts field can start with any of the two types, and can end with any*/
        ],
        "assign": { "name": [ "name1", ... ], ... }, /* declares new names in the scope of the subprogram below */
        "sub": [ /*the same structure as the main program*/ ]
        /*no need to define count limits here: if there are any, it appears in an Instantiation Instruction*/
    }

Calculate Programs

The calculate language appears as a JSON array in the above structure in these possible places:

  1. in the calculate field of a Instantiation object, all elements;
  2. inside the format field of a Substitution object, as an element at the second or later position (i.e. index > 0);
  3. inside the assign field of any Instruction object, as a value.

It is a transformed version of the series of operations in the [Placeholders].

See [Calculate Language].

Subprograms in the Templating Program Structure

In the sub field of the structure, the format of the top level array is repeated. The subprogram will have a narrower scope: if the calculate results in a collection of objects, the fields of that object can be accessed by name in this scope.

Examples

See [Examples (Templating Program)] page.

The XPath in a Substitution Instruction Object

The template engine in theory could evaluate any XPath, however the transplate script will simply select one from the following XPaths:

  • element that has a user-defined ID, which is unique: "."
    - in a template (if t:times="0+" or "1+"), ID needs to depend on (contain) the counter
    - problem: in order to substitute the ID that the developer defined to the ID attribute value, template engine needs to locate the element that has no final ID yet
    - solution: give the element in template content an auto-generated ID, only the real DOM will have the specified ID of such elements
  • if it is the only text node of an element with an ID: "text()"
  • if it is the first text node of an element with an ID: "text()[1]"
  • if it is an attribute value in an element with some ID: "@attr"
  • if it is a sibling text node next to an element with an ID: "following-sibling::text()[1]"
  • comment nodes before a text node with placeholder in it results in an XPath ending in [2] or higher
  • when the text node to be located is in a template, the context node may become the root node of the fragment, which cannot have an ID; context field of the Substitutiun object will be that of the template element, and the engine can use the fragment as the context node when looking up the text node
  • otherwise an auto-id is needed

Element node can be identified by id attribute. Attribute values can be replaced as a whole. Inner text nodes are trickier. In between sibling text nodes, elements may appear and even multiply due to templating.

Solution: set the ID of the preceding element, or, if none, the parent. It would be logical to keep the template itself behind all its own instantiated elements. It would have its ID so the following text node would be findable. At the same time, it keeps track where to add new instances of the template. Locators would be XPaths evaluated by documentFragment.evaluate calls.

ID Translation

If there is an element with an ID attribute in the template, the in-browser template engine needs to give those elements new unique IDs before it instantiates more than one instance. The in-browser template engine needs to do the substitutions using the original IDs since the progam references those. When the substituted value is Observable then it subscribes at the Observable such a callback that updates the text node located via the new ID.

In templates that are t:times="1-" or "1", the translation is identity, i.e. the IDs don't change.

Note: not only the transplate script needs an IdFactory instance but also the in-browser template engine. The class goes to the common module.
Note: the ID attributes should not accept an Observable value.

Formal Requirements on the HTML Document

The in-browser templating engine can expect a node to be filled with data to be found using one of the following JavaScript snippet. In the DOM:

document.evaluate(xpath, document.getElementById(id), null, 8).singleNodeValue.nodeValue = calculationResult

The in-browser templating engine can expect the templating program data block to be found using the following JavaScript snippet.

const prg = JSON.parse(document.getElementById("templating-program").text)

Related

Wiki: Calculate Language
Wiki: Examples (Templating Program)
Wiki: Home
Wiki: Placeholders