infuse.host

Infuse your HTML with dynamic content.

Introduction

Infuse.host allows you to infuse HTML templates with dynamic content. The resulting infused HTML fragments can then be added to host elements. This is done by writing template literals or expressions in your HTML templates. It also allows you to:

Installation

You can install the infuse.host NPM package in your project by running the following command.

Webpack

In order to keep things simple, all the code examples shown below have templates that are parsed in the browser (using the parseTemplate function). However, parsing templates in the browser is a practice that is discouraged in a production environment. You can avoid parsing templates in the browser by parsing them as part of a build or back-end process.

If you're using webpack, you can use infuse-loader to parse templates in HTML files and generate ES modules. These modules can then be imported into other modules. You can install the infuse-loader NPM package in your project by running the following command.

The site https://todo.infuse.host/ is an example of a web application built using infuse.host, in which all templates are parsed as part of a webpack build process using infuse-loader. The source code can be found at https://github.com/serg-io/todo.infuse.host.

Examples

In the following "Hello World" example, the <template> element is parsed, cloned, and infused and the resulting fragment (<h1>Hello World</h1>) is added to <header> (the host element).

Loading example...

The same result can be achieved using a custom element. The Infuse.Host class can be extended to define custom elements. In this example the text is obtained from the custom element (host.title is used in the template instead of data.title).

Loading example...

In the previous example the infused fragment is appended to the <custom-header> using regular DOM. However, a Shadow DOM can be used by setting the element's shadowRootMode to either 'open' or 'closed'. In the following example the <button> element is extended to define a customized built-in element that uses a shadow DOM.

Loading example...

The following is a more elaborate example that simulates a shopping cart.

Loading example...

Template Literals

You can write template literals in your HTML code (inside elements and attribute values), the same way you would write them in regular Javascript code. They start and end with back-tick characters, they can contain strings and expressions, and they can be tagged. However, in order to be able to tag template literals, you must tell infuse.host what are the tag functions that you use within your HTML templates. You do this by setting the tags configuration option.

A common use case for tagged template literals is internationalization. For instance, if you want to tag your template literals with a tag function called i18n you must use the configs ES module to set the tags configuration option:

The parser uses the "tags" configuration option to identify the tag functions that you use in your HTML templates. For instance, if you use the tags object shown above, i18n`submit` will be infused with (replaced with) the string "Submit".

Loading example...

Expressions

An expression starts with ${ and ends with } and contains Javascript code, which is evaluated during the infuse process, and its result is used to infuse the corresponding part of an element. Expressions can be used inside template literals, inside back-tick characters (just like in regular Javascript code), or directly in your HTML code, without back-tick characters.

The following variables are available within expressions:

Expressions and template literals can be used in combination with static strings. For instance:

Custom Constants

Custom constants are variables available within the expressions and event handlers of an element. These constants are defined using attributes that follow this format: const-variable-name="${ expression }", where variable-name is the name of the variable, expression is the Javascript code that gets evaluated (right before the element is infused for the first time), and the resulting value is assigned to the variable.

For instance, lets say that host.getCarInventory() returns an array of objects, each object represents a car with a color attribute. The following paragraph displays how many blue cars there are in the inventory:

Note: In HTML, attributes names are case-insensitive (they're lower-cased automatically). To define a variable with uppercase letters in its name, you must use dashes as shown in the previous example.

Note: the event variable is not available in expressions to declare custom constants.

Parts

There are four parts of an element that can be infused:

Attributes

In the following example, the class attribute will be infused with "is-valid" (in addition to "form-control") if host.isEmailValid evaluates to a truthy value:

Boolean Attributes

Boolean attributes have names that end with a question mark and they're added to the element if their expressions result in a truthy value.

If the result of an expression is true, the attribute is added to the element using an empty string as its value. If the result is a truthy value, other than true, it will be used as the value of the attribute. Otherwise, if the expression results in a falsy value, the attribute is removed from the element.

In the following example, the attribute disabled is added to the <button> element if host.isFormInvalid is true:

When an expressions results in a string that is not empty, the string will be used as the value of the attribute. For instance, if host.getBtnClass() returns the string "btn btn-warning", it will be used as the value of the class attribute. However, if it returns a falsy value, the class attribute will not be added to the button at all.

Properties

Properties of elements can be infused by adding an attribute in the HTML code that starts with a dot. The expression given in the value will be evaluated and the result will be assigned to the specified property of the element. In the following example, the expression evaluates to a Date instance which is assigned to the property valueAsDate of the input element:

Note: In HTML, attributes names are case-insensitive (they're lower-cased automatically). To infuse a property with uppercase letters in its name use dashes as shown in the example above.

Text Child Nodes

The text child nodes of an element can be infused by adding an expression or a template literal:

Tagged template literals can be used for things like internationalization:

Event Handlers

You can add event handlers to elements, just like you would in regular HTML code, by using on-event attributes.

When an element is parsed, event handler attributes are converted into event listener functions (terminology). When an element is infused for the first time, these event listeners are added to the element using the addEventListener method.

Event handlers have access to the same variables available within expressions. For instance, if you want to call a method on the host element when a form is submitted, you would add an onsubmit event handler attribute.

Loading example...

Watches

Watches re-infuse an element when an event occurs on itself or another element.

For instance, when the following button is infused for the first time the disabled boolean attribute will not be added because the event variable is undefined.

A watch can be added to infuse the button again whenever it is clicked. When a watch infuses an element, the value of event is the event that triggered the infusion.

In the following example, the watch will listen for the click event on the button itself (this). When the event occurs, the element will be re-infused, this time the event will be the click event that triggered the watch, which will cause the disabled attribute to be added to the button.

Watches can also be used to watch events on other elements. For instance, the button can be infused when a submit event occurs on the host element:

If the host element in the previous example contains multiple forms, the button will be disabled whenever any of those forms are submitted. This is because events bubble up the DOM and eventually reach the host.

Watches can be limited to infuse an element only when the specified type of event is triggered by an element that matches a given selector. For instance, the following button will be disabled when a submit event reaches the host element and the event was triggered by a form with "login-form" as its ID:

Watches are written in this format: watch-variable="event-map", where variable is the name of the variable (element) to watch. This is usually host or this, but it can be any variable available within the element's expressions as long as it's an Element or implements the addEventListener method.

The event-map can be written in any of the following formats:

Iterating Templates

Iterating templates use the for and each attributes to indicate that the template must be cloned and infused multiple times. The value of the for attribute is a string with up to 3 comma-separated variable names to use in each iteration. The value of the each attribute must be an expression. Evaluating the expression must result in a collection (array, Map, Set, or any value that has a forEach method). The collection is used to clone the template multiple times, once for each value in the collection.

Using the for and each attributes to clone and infuse a template multiple times is analogous to using the forEach method to iterate over multiple values in regular Javascript. For instance, in Javascript you would write a forEach loop like this:

A similar iteration can be implemented to clone and infuse a template multiple times by adding the for and each attributes to the <template> element:

Lets say that host.getBooksArray() returns an array of objects, each object represents a book and contains the attributes isbn, title, and author. The following template can be used to generate a <table> with a list of books.

Loading example...

Note how the iterating template is inside a parent template. When a template is cloned and infused, nested templates are also cloned and infused.

Custom Elements

The Infuse.Host class can be extended to define a class for a custom element.

Defining a template property is the only requirement when extending Infuse.Host. The purpose of the template property is to provide the template element that will be cloned and infused whenever the custom element is used.

By default, the infused fragment will be appended to the custom element's Light DOM. However, a Shadow DOM can be used by setting the shadowRootMode property to either 'open' or 'close' (which are the two modes that the attachShadow method accepts).

Both, the template and shadowRootMode properties, can be defined as getters or regular functions.

Lets say you want to define a custom element for a login form, that uses a Shadow DOM, and that you're using webpack and infuse-loader to parse and import your templates. The following code would define a custom element called <login-form>.

When the <login-form> custom element is used, Infuse.Host uses the template property to obtain the template that needs to be cloned and infused. And, since shadowRootMode is set to 'open', the resulting fragment is added to the custom element's Shadow DOM. When the element is removed from the DOM, memory allocated for the element, and any of its descendants, is cleared automatically, there's no need to call the clear function.

Customized Built-in Elements

If you want to extend one of the browser's built-in elements you can use the CustomHost function to define a customized built-in element.

For instance, lets say that your application contains a shopping cart and you want to summarize the items in the cart using a list (an <ul> element) where each element in the list is an item in the cart. You can define a customized built-in element that extends the HTMLLIElement class (the <li> element):

Once defined, you can create them manually using the constructor:

or using document.createElement:

and add them to the DOM manually.

You can also use them in HTML templates:

Cleanup

When a template is cloned and the cloned fragment is infused, memory is allocated to create variables, event listeners, and watches. Infuse.host keeps track of allocated memory. When an element is removed from the DOM and is no longer needed, memory associated with that element must be cleared. This memory can be cleared using the clear function. The clear function searches for infused elements and clears memory associated with the elements it finds.

In the following example a <form> element is removed from the DOM and the clear function is called using the form as the only argument. The clear function will search for infused elements inside the form and clear memory that was allocated to infuse them. If the form itself is an infused element, memory associated with the form will also be cleared.

Note: Custom elements defined using the Infuse.Host class or the Infuse.CustomHost function call the clear method when the custom element is removed from the DOM (when the disconnectedCallback method is called). Therefore, the clear function doesn't need to be called for custom elements defined using Infuse.Host or Infuse.CustomHost.

Command Line Interface

The infuse-cli package provides a command line interface (CLI) to convert HTML templates (files) to ES Modules which can be used with infuse.host. To install it execute the following command:

Once installed you can use the infuse command in your NPM scripts or by using it with NPM's npx command. For instance, the following command will parse templates in the src/template.html file and will write the generated ES Module to dist/template.js.

Documentation and additional examples can be found in the infuse-cli package web page. You can also use the CLI to access the documentation:

Configuration Options

The config ES module allows you to change configuration options.

In the following example, the setConfigs function is used to change the configuration options eventHandlerExp and eventName:

The following is a list of all the configuration options:

License

MIT.