<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: templating.xml 18147 2013-01-30 22:52:25Z wolfgang_m $ --><book><bookinfo><graphic fileref="logo.jpg"/><productname>eXist-db – Open Source Native XML Database</productname><title>HTML Templating Module</title><date>October 2012</date></bookinfo><chapter><title>HTML Templating Module</title><section><title>Introduction</title><para>The main goal of the HTML templating framework in eXist-db is a clean separation
                of concerns. Generating entire pages in XQuery is quick and dirty, but makes
                maintenance and code sharing difficult. Ideally people should be able to look at the
                HTML view of an application and modify its look and feel without knowing
                XQuery. The application logic - written in XQuery - should be kept
                separate. Likewise, the XQuery developer should only deal with the minimal
                amount of HTML which is generated dynamically.</para><para>The templating module also handles most of the HTTP processing an application
                requires. It does so using sophisticated features like automatic parameter injection
                and type conversion. The goal was to remove repeating code like:</para><synopsis language="xquery">let $query := request:get-parameter("query", ())
let $field := request:get-parameter("field", ())
...</synopsis><para>In fact you should not see many calls to the HTTP request or session module inside
                a templating function. It is all handled by parameter injection. </para><note><para>Working examples for the templating module can be found in the <ulink url="{demo}/examples/templating/templates.html">demo
                    application</ulink>.</para></note></section><section><title>Writing the HTML</title><para>The templating module is mainly based on conventions. Wherever possible it tries
                to make a best guess instead of requiring additional code or annotations to be
                written. This works as long as the conventions used are sufficiently clear.</para><para>The input for the templating framework is always a plain HTML file. The module
                scans the HTML view for elements with class (or data-template, see below) attributes
                following a simple convention and tries to translate them into XQuery function
                calls. By using class attributes, the HTML remains sufficiently clean and does not
                get messed up with application code. A web designer could take the HTML files and
                work on them without being bothered by the extra class names.</para><para>In the simplest case, a template call inside a class attribute is just the name of
                a function known in the XQuery context. To start with the usual "Hello world"
                example:</para><synopsis language="xquery">&lt;div class="demo:hello grey-box"&gt;Greet the user&lt;/div&gt;</synopsis><para>When the module encounters demo:hello, it will try to find a function named
                "demo:hello" in all the modules known to the XQuery. If the function's signature
                follows a certain convention (see below), it will be called and the div will either
                be replaced or enhanced by whatever the function returns.</para><para>Please note that the additional class "grey-box" does not interfere with the
                template call and is just ignored. "grey-box" might be a class used for styling, so
                we don't want to remove it. The templating framework will only take a closer look at
                class names which follow the <code>prefix:local-name</code> pattern.</para><para>It is also possible to pass static parameters to a template call. Those are
                encoded like URI parameters, e.g.: <code>demo:hello?language=de</code>. A
                static parameter will be passed to the XQuery function as a fallback value if it
                cannot be determined by looking at the HTTP context (see below).</para><section><title>HTML5 Method</title><para>Instead of "abusing" class attributes to encode template calls or other
                    application-specific information, HTML5 provides a standard method for adding
                    data to an element, using <option>data</option> attributes. This approach is
                    supported in newer versions of the templating framework (beginning with version
                    0.3.0 of the shared-resources package, which contains the source of the
                    templating).</para><para>The <option>data</option> attributes must follow a certain naming pattern. The
                    templating function to call has to be specified in an attribute:
                        <option>data-template</option>, while optional parameters would go into one
                    or more attributes of the form: <option>data-template-xxx</option>. For example,
                    you could change the pre-HTML5 style template call:</para><synopsis language="xml">&lt;div class="demo:hello?language=de"&gt;&lt;/div&gt;</synopsis><para>into</para><synopsis language="xml">&lt;div data-template="demo:hello" data-template-language="de"&gt;&lt;/div&gt;</synopsis><para>The templating framework supports both alternatives.</para></section></section><section><title>Templating Functions</title><para>A templating function is an ordinary XQuery function in a module which takes at
                least two parameters (of a certain type), though additional parameters are allowed.
                If a function does not follow this convention, it will simply be ignored by the
                templating framework. For example, our "Hello world!" function could be defined as
                follows:</para><programlisting language="xquery">
declare function demo:hello($node as node(), $model as map(*), $language as xs:string, $user as xs:string) as element(div) {
    &lt;div&gt;
    {
        switch($language)
            case "de" return
                "Hallo " || $user
            case "it" return
                "Ciao " || $user
            default return
                "Hello " || $user
    }
    &lt;/div&gt;
};
            </programlisting><para>The two required parameters here are <code>$node</code> and <code>$model</code>.
                $node contains the HTML node currently being processed: in this case, the div
                element. $model is an XQuery map with application data. It will be empty for now,
                but we'll see later why it is important.</para></section><section><title>Parameter Injection and Type Conversion</title><para>The additional parameters, <code>$language</code> and <code>$user</code>, will be
                injected automatically. The templating framework tries to make a best guess about how
                to fill those parameters with values. It checks the following 3 contexts for
                parameters with the same name (in the order below):</para><orderedlist><listitem><para>if the current <emphasis>HTTP request</emphasis> contains a (non-empty)
                        parameter with the same name as the parameter variable, it is used to set
                        the value of the variable</para></listitem><listitem><para>if the <emphasis>HTTP session</emphasis> contains an attribute with the
                        same name as the parameter variable, the variable's value will be set to
                        it</para></listitem><listitem><para>if the <emphasis>static parameters</emphasis> passed to the template call
                        contain a parameter matching the variable name, it will be used</para></listitem></orderedlist><para>If neither 1. nor 2. lead to a non-empty value, the function signature
                will thus be checked for an annotation <code>%templates:default("name", "value1",
                    ..., "valueN")</code>. The first parameter of the annotation should
                match the name of the parameter variable. All other parameters of the
                annotation are taken as values for the variable.</para><para>If "language" is passed as a parameter in the HTTP request, it will overwrite
                the static parameter we provided because the HTTP request is checked first. 
            </para><para>The templating framework will attempt automatic type conversion for all
                parameters. If the parameter has a declared type of xs:integer, it will try to cast
                a parameter it finds into an integer. If the type is node(), the parameter value
                will be parsed into XML. These conversions may fail and you may get an error if you
                are passing a parameter with the wrong type.</para></section><section><title>Additional Annotations</title><para>Our "Hello world" example above does not preserve the div from which
                it was called, but replaces it with a new one which lacks the "grey-box" class.
                This is the default behavior. To preserve the enclosing div, we should
                add the XQuery annotation <code>%templating:wrap</code> to the function
                signature.</para><para>A second annotation can be used to provide a default value for a parameter:
                    <code>%templating:default("parameter", "value1", "value2", ...)</code>. For
                example, we may want to set the $language parameter to "en" if the value cannot be
                determined
                otherwise:<programlisting language="xquery">declare 
    %templating:wrap %templating:default("language", "en")
function demo:hello($node as node(), $model as map(*), $language as xs:string, $user as xs:string) as xs:string {
    switch($language)
        case "de" return
            "Hallo " || $user
        case "it" return
            "Ciao " || $user
        default return
            "Hello " || $user
};</programlisting></para><para>As you can see, we could remove the wrapping div and just return a string now.
            </para></section><section><title>Using the Model to Keep Application Data</title><para>In a more complex application, a view will have many templating functions, which
                all access the same data. For example, take a typical search page: there might be
                one HTML element to display the number of hits, one to show the query, and another
                one for printing out the results. All those components need to access the search
                result. How do you do this in a templating framework?</para><para>This is where the <code>$model</code> parameter becomes important. It is passed to
                all template functions and they can add data to it, which will then be available to
                nested template calls. A search page could thus contain HTML like this:</para><programlisting language="xml">&lt;div class="demo:search"&gt;
    &lt;p&gt;Found &lt;span class="demo:hit-count"&gt;&lt;/span&gt; hits&lt;/p&gt;
    &lt;ul class="demo:result-list"&gt;&lt;/ul&gt;
&lt;/div&gt;</programlisting><para> The <code>demo:hit-count</code> and <code>demo:result-list</code> occur inside
                the div calling <code>demo:search</code>. They are thus nested template calls.
                    <code>demo:search</code> would probably perform the actual search operation
                based on the parameters passed by the user. But instead of directly printing the
                search result in HTML, it delegates this to the nested templates. demo:search might
                be implemented as: </para><programlisting language="xquery">declare 
    %templating:wrap
function demo:search($node as node(), $model as map(*), $query as xs:string) as map(*) {
    let $result :=
        for $hit in collection($config:app-root)//SCENE[ft:query(., $query)]
        order by ft:score($hit) descending
        return $hit
    return
        map { "result" := $result }
};</programlisting><para><code>demo:search</code> differs from the functions we have seen so far in that it
                returns an XQuery map and not HTML or an atomic type. If a templating function
                returns a map, the templating framework will proceed as follows:</para><orderedlist><listitem><para>add the returned map to the current <code>$model</code> map (adding it
                        to the map entries produced by any ancestor templates calling it)</para></listitem><listitem><para>resume processing the children of the current HTML node</para></listitem></orderedlist><para>The <code>demo:hit-count</code> and <code>demo:result-list</code> can thus access
                the query results in the <code>$model</code> map passed to them:</para><programlisting language="xquery">declare function demo:hit-count($node as node(), $model as map(*)) as xs:integer {
    count($model("result"))
};</programlisting><section><title>Manual Processing Control</title><para>Inside a templating function, you can also call <code>templates:process($nodes
                        as node()*, $model as map(*))</code> to let the templating module process
                    the given node sequence. You just need to make sure you are not running into an
                    endless loop by calling <code>templates:process</code> on the currently
                    processed node. A common pattern is to trigger <code>templates:process</code> on
                    the children of the current node:</para><programlisting language="xquery">templates:process($node/node(), $model)</programlisting><para>This is comparable to calling <sgmltag>xsl:apply-templates</sgmltag> in XSLT
                    and will have the same effect as returning a map (see the section above), but
                    with your templating function having full control.</para><para>For example, it is sometimes necessary to first process all the descendant
                    nodes of the current element, then apply some action to the processed tree. The
                    documentation app has a function, <code>config:expand-links</code>, which scans
                    the final document tree for links and expands them. The function is implemented
                    as follows:</para><programlisting language="xquery">declare %templates:wrap function config:expand-links($node as node(), $model as map(*), $base as xs:string?) {
    for $node in templates:process($node/node(), $model)
    return
        config:expand-links($node, $base)
};</programlisting></section></section><section><title>Set-Up</title><para>The templating module is entirely implemented in XQuery. It provides a single
                public function, <code>templates:apply</code>. A complete main module which calls
                the templating framework to process an HTML file passed in the HTTP request body could look as
                follows:</para><programlisting language="xquery">(:~
 : This is the main XQuery which will (by default) be called by controller.xql
 : to process any URI ending with ".html". It receives the HTML from
 : the controller and passes it to the templating framework.
 :)
xquery version "3.0";

import module namespace templates="http://exist-db.org/xquery/templates" at "templates.xql";

(: 
 : The following modules provide functions which will be called by the 
 : templating framework.
 :)
import module namespace app="http://my.domain/myapp" at "app.xql";

declare option exist:serialize "method=html5 media-type=text/html";

(:
 : We have to provide a lookup function to templates:apply to help it
 : find functions in the imported application modules. The templates
 : module cannot see the application modules, but the inline function
 : below does see them.
 :)
let $lookup := function($functionName as xs:string, $arity as xs:int) {
    try {
        function-lookup(xs:QName($functionName), $arity)
    } catch * {
        ()
    }
}
(:
 : The HTML is passed in the request from the controller.
 : Run it through the templating framework and return the result.
 :)
let $content := request:get-data()
return
    templates:apply($content, $lookup, ())</programlisting><para>This module would be called from the URL rewriting controller. For example, we
                could add a rule to <filename>controller.xql</filename> to pass any .html resource
                to the above main query (saved to <filename>modules/view.xql</filename>):</para><programlisting language="xquery">(: Pass all requests to HTML files through view.xql, which handles HTML templating :)
if (ends-with($exist:resource, ".html")) then
    &lt;dispatch xmlns="http://exist.sourceforge.net/NS/exist"&gt;
        &lt;view&gt;
			&lt;forward url="{$exist:controller}/modules/view.xql"&gt;
                &lt;set-attribute name="$exist:prefix" value="{$exist:prefix}"/&gt;
                &lt;set-attribute name="$exist:controller" value="{$exist:controller}"/&gt;
            &lt;/forward&gt;
        &lt;/view&gt;
        &lt;error-handler&gt;
            &lt;forward url="{$exist:controller}/error-page.html" method="get"/&gt;
            &lt;forward url="{$exist:controller}/modules/view.xql"/&gt;
        &lt;/error-handler&gt;
    &lt;/dispatch&gt;</programlisting><para>The only part of the main module code which might look a bit unusual is the inline
                lookup function: the templating module uses dynamic function calls to execute
                template functions in application modules. But unfortunately, XQuery modules can
                only "see" functions in their own context. There is thus no way for the templating
                module to determine what functions are defined in application modules which are
                outside its context. We thus need to "help" it by providing a callback function to
                resolve function references. The lookup function is defined in the main context and
                can thus access all the modules imported into the main module.</para><para>Normally you can just copy and paste the main module code as given above. To adopt
                it to your own application, just import your application modules and you're
                done.</para></section><section><title>Integration with eXide</title><para>The "New Application" templates in eXide already include the HTML templating
                module and configure the URL rewriting to call it for any path ending in
                    <filename>.html</filename>. Using eXide is thus the easiest way to get started
                with the templating framework. Please consult the <ulink url="development-starter.xml">Getting Started with Web Application
                    Development</ulink> guide to read more.</para></section><section><title>Where to Find the Module?</title><para>If you generate your application with eXide, a copy of the HTML templating module
                will be included, so you can customize it. If you rather want to make sure you have
                the latest version of the templating module: the <ulink url="https://github.com/eXist-db/shared-resources">shared-resources</ulink>
                application also exports the module.  This will always be the latest version. You
                could thus define a dependency on the shared-resources app (see the <ulink url="repo.xml">packaging documentation</ulink>). In this case you can just
                import the module by its namespace URI, but without specifying a location.</para><synopsis language="xquery">import module namespace templates="http://exist-db.org/xquery/templates";</synopsis><para>The documentation and demo applications all read the templating module from
                shared-resources.</para></section><section><title>Pre-defined Template Commands</title><para>The templating module defines a number of general-purpose templating functions,
                which are described below for reference. <code>templates:surround</code> is probably
                the most powerful one and used by almost all HTML views.</para><section><title>templates:include</title><synopsis language="xquery">templates:include?path=path-to-xml-resource</synopsis><para>Includes the content of the resource given by path into the current element.
                    The path is always interpreted relative to the current application directory or
                    collection.</para></section><section><title>templates:each</title><synopsis language="xquery">templates:each?from=map-key&amp;amp;to=map-key</synopsis><para>Retrieve the sequence identified by the map key <option>from</option> from the
                        <code>$model</code> map. If it exists, iterate over the items in the
                    sequence and process any nested content once. During each iteration, the current
                    item is added to the <code>$model</code> map using the key
                    <option>to</option>.</para></section><section><title>templates:if-parameter-set</title><synopsis language="xquery">templates:if-parameter-set?param=request-parameter</synopsis><para>Conditionally includes its content only if the given request parameter is set
                    and is not empty.</para></section><section><title>templates:if-parameter-unset</title><synopsis language="xquery">templates:if-parameter-unset?param=request-parameter</synopsis><para>Conditionally includes its content only if the given request parameter is not
                    set or is empty.</para></section><section><title>templates:surround</title><synopsis language="xquery">templates:surround?with=xml-resource&amp;at=id&amp;using=id</synopsis><para>Surrounds its content with the contents of the XML resource specified in
                    "with". The "at" parameter determines where the content is inserted into the
                    surrounding XML. It should match an existing HTML id in the template.</para><para>The "using" parameter is optional and specifies the id of an element in the
                    "with" resource. The current content will be surrounded by this element. If the
                    parameter is missing, the entire document given in "with" will be used.</para><para>The surround template instruction is used by all pages of the <ulink url="{demo}/index.html">Demo</ulink>
                    application. The header, basic page structure and menus are the same for all
                    pages. Each page thus only contains a simple div with a template
                    instruction:</para><synopsis language="xquery">templates:surround?with=templates/page.html&amp;at=content</synopsis><para>The instruction takes the content of the current element and injects it into
                    the template page.</para></section><section><title>templates:form-control</title><synopsis language="xquery">templates:form-control</synopsis><para>Use on &lt;input&gt; and &lt;select&gt; elements: checks the HTTP request for
                    a parameter matching the name of the form control and fills it into the value of
                    an input or selects the corresponding option of a select.</para></section><section><title>templates:load-source</title><synopsis language="xquery">templates:load-source</synopsis><para>Normally used with an &lt;a&gt; element: opens the document referenced in the
                    href attribute in eXide.</para></section></section></chapter></book>