Processors

Definition processors are a feature of Modulo that allows for component parts and other definitions to support special attributes that trigger certain behavior. They are commonly used for configuring, loading, or tranforming definitions.

Definition processors are conventionally prefixed with a dash (that is, -). This distinguishes them from regular attributes. Some processors can only be used with a certain type of component parts or definitions. For example, examine the -store= processor for State:

<State -store="userdata" username="crash0veride" ></State>

Others are supported by all component parts. For example, examine the -src= processor for loading content from external files.

<Template -src="my-template.html"></Template> <Style -src="fancy-look.css"></Style>

Behavior

Definition processors have three important rules of behavior:

  1. As the name implies, definition processors are run while Modulo first loads definitions, meaning all the Processors will all complete before your web components register their tag names. Definition processors are applied before any other behavior. That is to say, every processor finishes its tasks before any components are mounted on the screen.

  2. Processors can be asyncronous, load data, process data, and can even do "slow" operations compared to other aspects of Modulo. CParts can load asyncronously, or even block loading if they choose to do so. This is what allows Modulo to self-configure and users to "invent" their own CParts: <Configuration -src="..."> will block loading any future -src= attributes until it's finished with all it's Processors; however, <Template -src="..."> is written to assumed to not need any particular ordering, and will be loaded asynchrously.

  3. Definition processors are not run in built or packed scripts. That is, the definitions stored in the compiled JS file are "pre-processed", and thus the processor only will run during development or when building. If accumulating many files and definition processors is slowing down your growing site, the good news is that after building, no one will notice, since Processors are (typically) only run during development. See Building Apps with Modulo Part 3 for more on pre-building.

Built-in Processors

Why no dash for <Component name="...">? Note that for Component, you use name= (without the -), and use a valid JavaScript identifier as the Web Component name. However, that behavior is specific to the Component definition, and is not available on Component Parts or other definitions. Definition types (e.g. core definitions like Library or a component parts like State) can each specify their own, unique behavior for processors.

-name

  • Supported by: All Definitions

The -name directive allows you to override the default name that Modulo will generate with something else. This allows you to have multiple Component Parts of the same type without conflict.

For a simple example, see below:

<Template -name="footer_template">

Example: -name for multiple Component Parts

Note that Template has special behavior around -name. If you specify a name, it will not by default still render, since it will assume you are going to include it somewhere else.

<Template> {# This becomes the "primary template" (named "template") #} <h3>Welcome to my blog</h3> <p>Things I like: {{ likes_data }}</p> <p>Things I dislike: {{ dislikes_data }}</p> {% include footer_template %} </Template> <Template -name="footer_template"> {# This becomes a non-primary template (named "footer_template") #} <p>(C) 2095 Tux the Penguin</p> </Template> <StaticData -name="likes_data"> [ "Fish", "radishes", "hackable software" ] </StaticData> <StaticData -name="dislikes_data"> [ "Wet towels" ] </StaticData>

-src

  • Supported by: All Definitions
<Template -src="./MyComponent/MyComponent.html" ></Template> <Script -src="https://unpkg.com/snarkdown"> modulo.register("templateFilter", snarkdown); </Script> <Style -src="./MyComponent/MyComponent.css" ></Style>

Note that the -src= combines the retrieved content with whatever is embedded between the opening and closing tag. It does not replace the embedded content, but instead places the retrieved content before content BEFORE the embedded content. This ordering is important, since it allows the internal content to "extend" the content that is being brought in.

-src for extension

The -src attribute is designed to allow for many types of "inheritence" or a sort of primitive extension, depending on the CPart. For example, Components can "inherit" CParts of base components (e.g., the Template and Script could be the same across two Components, but they only differ in Style), or Style can "inherit" another style file, but get to append some modifications to the end. (e.g., loading from a third party, then override certain properties). Similarly, Library or Modulo can load entire Modulo libraries, and then apply Configuration scripts at the end to modify configuration settings within that other libraries' execution context.

-filter-content

  • Supported by: Template (Alpha 71), StaticData, Style (Alpha 72+)

Allows you to apply template filters at load time.

Example 1: Trim excess code

You can use trim to remove excess characters from a remotely loaded file. For example, here we load content from an external document file, and then use |trim to clean up the doctype information, text we don't want in the Template:

<Template -src="https://some-site.com/some-old-document.xhtml" -filter-content="trim:'<xml version=1.0><html>,</html>'" ></Template>

In this case, the trim filter will remove the XML doctype and an HTML opening tag, along withe the HTML closing tag, if they are found at the beginning and end of the document retrieved from: https://some-site.com/some-old-document.xhtml. Processors all happen during initial loading of components and definitions, meaning the trimmed text will be ignored and omitted from the JS builds.

Example 2: Style template with trim

By defining a style Template, we can template style for inclusion. By using trim we can use a placeholder of x { .. } to make editing this CSS easier to read. Note that this is, in fact, a Template CPart, however, we will use a style initial tag to make it easier to read and syntax highlight.

See below for a concrete example of how to use this technique for making a quick button designer component:

<Template> <label><input [state.bind] name="bg" type="color" /> Color</label> <label><input [state.bind] name="size" type="range" min="5" max="20" /> Size</label> <button style="{% include button_style %}">Sample Button</button> <pre>button { {% include button_style %} }</pre> </Template> <style Template -filter-content="trim:'x {,}'" -name="button_style"> x { background-color: {{ state.bg }}; font-size: {{ state.size }}px; } </style> <State bg="#a0caa0" size:=10 ></State>

Example 3: Tagswap for <table>

Modulo uses HTML syntax for most everything. This, unfortunately, means that <table> tags can behave differently when it comes to how content is processed and loaded, causing content to "escape" when you are still just assembling the initial Template's DOM. Similarly, the closing script tag can have special meanings as well. To get around limitations like this, we can use the |tagswap to convert innocuous placeholder tags into other ones:

<Template -filter-content="tagswap:'t=table r=tr d=td'"> <t> <r> <d>Col 1</d> <d>Col 2</d> <d>Col 3</d> </r> <r> <d>A</d> <d>B</d> <d>C</d> </r> <r> <d>1</d> <d>2</d> <d>3</d> </r> <r> <d>X</d> <d>Y</d> <d>Z</d> </r> </t> </Template> <Style> table { border: 2px solid Tomato; } td { border: 1px dotted Tomato; padding: 2px; } </Style>

This will be applied at load time, so there are ultimately no performance penalties, as the final JavaScript file will have the tag transpositions "baked in" (and with no trace of the original template, either).

Gotcha: Applied at runtime

Remember that it is applied "pre-baked" to the content itself during initial content load, and NOT at Template run time, meaning it will affect (and potentially disrupt) the tempting language itself as well (e.g. using -filter-content="upper" will cause {{ state.stuff }} to become {{ STATE.STUFF }}, which will not work since capitalization matters in this case).