asteroid/docs/TEMPLATING_SYSTEM.org

7.3 KiB

Templating System

Introduction

The radiance ecosystem includes an HTML templating system that is built on top of Clip and lquery for advanced costumization.

While clip enables conditional logic on rendering parts in an HTML document, re-using the same tag mechanics, lquery enables dynamically setting of element properties with some lisp integrations.

Dominating these tools enables some smarter approaches on page content building with conditional rendering and code reuse.

Clip mechanics

Conditionals

Clip uses HTML tags for its logic and has 3 simple conditional directives: if, when and unless, that work exactly as their lisp counterparts, having the test attribute as the validator for truthness.

The if check is accomplished by the c:if tag, and requires a c:then tag for the body of its branch. It also has c:else and c:elseif to enable multiple branching:

<c:if test='framesetp'>
  <c:then>
    <!-- HTML test true branch -->
  </c:then>
  <c:else>
    <!-- HTML test false branch -->
  </c:else>
</c:if>

The above example tests for the truthness of the variable framesetp and executes the associate branch based on that. The test argument can, naturally, receive any kind of lisp syntax.

The c:when and c:unless directives, like their lisp counterparts, only have a branch that is executed when the check of test argument value is true or false accordingly.

Note: Clip also has c:case and c:cond conditionals that should work like they lisp counterparts but I didn't need them until now so I'll not try to explain them.

Data usage

Using a variable content in the template engine is accomplished by the lquery tag attribute, which will be explained later. But as this is a tag attribute, it requires an HTML tag to be attached. To enable the possibility of inserting something anywhere on the HTML document, Clip supplies the c:splice tag, a virtual HTML node that is removed after rendering, leaving only the content inserted by lquery.

The follow example inserts a textual value of the content variable directly into the HTML page:

<c:splice lquery='(text content)></c:splice>

However, a common use case is inserting external HTML rendered content in a different HTML document, for example, to reuse a navbar HTML partial in every page:

<c:splice lquery='(html navbar)></c:splice>

Those two usages are so frequently that Clip supplies shorhand aliases for both as the c:s and c:h tags:

<!-- Insert a textual value -->
<c:s>content</c:s>

<!-- Insert an HTML partial to render -->
<c:h>navbar</c:h>

A great usage of this feature is our new navbar reusable HTML partial, that can be loaded from a speficic lisp function on any page:

<c:h>(asteroid::load-template "partial/navbar")</c:h>

Note: As far as I understand, the templating engine works on a different package, so refering to the asteroid package is always required inside a template to use defined functions.

Clipboard environments

Clip uses the concept of environment as the data that is available to be used in the templating system.

The c:let directive lets define a new clipboard environment directly on the template. This will supersede the default clipboard, so any useful data needs to also be added to the clipboard for easy access:

<c:let new-var='"hello"' forward='var'>
  <!-- HTML content -->
</c:let>

The above example defines a local new-var with the value "hello" and a local forward with the parent var variable content.

Alternatively, if forwarding a variable to the new clipboard environment is not desired, the special (** :var) syntax can be used to access the parent clipboard.

The c:using directive creates a new clipboard environment with the contents of an existing variable on the parent clipboard.

<c:using value='obj'>
  <!-- HTML content using the 'obj.attribute1' value-->
  <c:splice>attribute1</c:splice>
</c:let>

The example creates a new clipboard environment inside the c:using attribute that has direct access to all properties of some structured variable obj. This tag transparently destructures any kind of composed data structure like plists, alists and classes.

Like the simplicity of structural access of the c:using tag, the clip function enables the same easy of access in any template context. For example, the same splice of the previous example can be obtained with:

<c:s>(clip obj :attribute1)</c:s>

This approach is, clearly, more useful when a single value is wanted, while the previous when several values on the structured variable will be used.

Iterators

Clip also has the c:iterate directive that iterates a sequence, and its body is rendered having the clipboard environment matching each element of that iteration. As I am yet to use this directive, I will leave its documentation to a later moment.

lquery mechanics

The lquery library enables changing any property of an HTML node, being its text or html content, css styles, element attributes (attr) and assigned css classes.

Its usage is very simple: an lquery attribute is added to an HTML document, and its value is a s-expression with an accepted funtion and the value for it to work on.

The following topics present some basic accepted lquery functions in use on our templates.

HTML node content

There are two basic lquery functions that change the content of the assigned HTML node:

  • html renders HTML elements as child
  • text sets a text content to the element

The lquery s-expression accepts variable or lisp calls for any of the referred functions. For example, the following example sets the content of the script tag to the result of the (asteroid::get-auth-state-js-var) function:

<script lquery='(text (asteroid::get-auth-state-js-var))'></script>

Note: the template render process works on a different package, so call to application defined functions needs to include the package name.

HTML node styling

lquery also grants easy styling customization of an HTML node. HTML styling can be accomplished in two ways:

  • css, which sets the HTML style attribute of the element. It's called as a plist with all the properties to set.
  • css classes that can be added (add-class) or removed (remove-class)

The following example adds the display and marging style properties to the document when the variable framesetp is true (margin is overwriten in this case):

<div style="margin: 15px 0;" lquery='(css :display (when framesetp "none") :margin (when framesetp "0"))'>

HTML attributes

The attr function of the lquery s-expression allows customization of any valid HTML node attribute. The following example sets two attributes of an anchor tag:

  • href, which uses the eval function to evaluate some lisp call
  • target which only has a value when the variable framesetp is true
<a lquery='(attr :href (eval (format nil "/asteroid/~a" status-href)) :target (when framesetp "_self"))'>