Templating Values
A Markup template is simply a JavaScript template literal, which means you can inject any value anywhere in the template.
const type = "button";
const label = "click me";
html`<button type="${type}">${label}</button>`;
// renders <button type="button">click me</button>
You can place values anywhere before, after and inside tags as well as inside attribute value quotes. However, the template will ignore any value placed on the tag body.
const type = "type='button'";
// will not render the type attribute
html`<button ${type}>click me</button>`;
// renders <button>click me</button>
This is because Markup parses the HTML string into nodes and will not allow dynamic tag or attribute names.
const tag = "button";
html`<${tag}>click me</${tag}>`;
// renders <button>click me</button>
If you need dynamic elements, you can create one using element utility and add it to the template.
Optional quotes
If you want to inject a single value as attribute, you may choose to leave out the quotes altogether.
const type = "button";
const label = "click me";
html`<button type=${type} disabled=${disabled} class="btn ${cls}">${label}</button>`;
The above example set the type
and
disabled
attributes without using the quotes
because it used the template literal curly braces
${...}
. This is find as long as the attribute is
single value. For multi or mixed value like the
class
in above example, you need the quotes.
If in doubt or just for consistency’s sake, you can always go for keeping the quotes which matches the normal HTML syntax. Just know that this is an option.
Different values handling
Markup templates handle certain values in a unique way so lets explore all possible value you might use.
Primitive JavaScript values
Any primitive value will be kept as is in it string format. Primitives is the simplest values you can use and have no special handling by the template.
true => "true"
12 => "12"
Array
Array
has special handling by the template. When
you provide an array to the template to render, as long as it is
not inside an attribute value quotes, it will have each item
rendered separately on the DOM.
const arr = [1, 2, 3, 2]
html`${arr}`;
// renders: 1232
How each item in the array gets rendered depend on its type.
This simple capability makes it pretty easy to render list of anything, including smaller template elements.
const arr = [1, 2, 3, 2].map(n => html`<strong>item ${n}</strong>`)
html`${arr}`
Markup will update each "sub-template" according to the data in the array changes. Depending on the data change, Markup will re-order, add or remove nodes matching the data in the array.
Non-Primitive JavaScript values
With the exception of Array
, every JavaScript
object will be changed to its string equivalent. If the object
contains a toString
method the result of that will
be displayed otherwise it falls back to default JavaScript
handling:
{} => [object Object]
Map => [object Map]
Set => [object Set]
Nesting templates
Naturally, you can also place other
HTMLTemplate
instances inside your template which
results in nested templates. It will be handled as expected and
rendered just fine.
const button = html`<button type="button">click me</button>`;
let count = 0;
const countUp = html`count: ${count} ${button}`;
// renders:
// count: 0 <button type="button">click me</button>
One important thing to know is that even if you nest template, each one is tracked separately.
This means that if there is a change, only the template dependent on the change will get update which makes composing complex views really performant. This will make more sense once you start working with state and dynamic values.
DOM Elements
Any DOM element placed in the template will be rendered normally.
const button = document.createElement('button');
button.textContent = "click me"
let count = 0;
const countUp = html`count: ${count} ${button}`;
// renders:
// count: 0 <button>click me</button>
HTML string
If you just want to render raw HTML string in the DOM so people can see the tags, simply add them.
const htmlCode = '<span>HTML span tag</span>'
html`${htmlCode}`
The above html code will not get parsed and will display as is as text.
The only way to render HTML is to use the html
to
create HTMLTemplates
. Everything else defaults to
being just string.
If you want to render an HTML string inside the template you can
use
html
in a different way. Simply call it and provide
an array of string values.
const htmlCode = '<span>HTML span tag</span>'
html`${html([htmlCode])}`
The above example will parse the HTML string into DOM Nodes.
Nil values
Any nil value (null or undefined) will be rendered as string. This means that you should change them to empty string if you dont want to render any nil value
html`${null}` // renders "null"
html`${undefined}` // renders "undefined"
Functions
Functions in the template also receive special treatment. Basically, there are two reasons to add functions to the template.
- Event handlers: To listen to events on tags.
- Dynamic values: To react to value changes anywhere in the DOM.
Please explore those two topics to understand more.
Web Components
Template values are handled differently when used with web component attributes.
If you use Web Components tag in your template the Markup will automatically detect it and try to pass the values directly to the component as is in case the component itself has a property or setter for that value.
Let's take for example the following TodoItem
web
component:
class TodoItem extends HTMLElement {
static observedAttributes = ["todo"];
todo: Todo = null; // will be updated by the template
}
customElements.define('todo-item', TodoItem)
When you try to pass data as props to a web component, the data will be passes as is directly to the property of the component and the attribute value will be JSON stringified.
const todo = {name: "go to gym"};
html`<todo-item todo="${todo}"/>`
// renders:
// <todo-item todo="{"name":"go to gym"}"></todo-item>
This is what makes this library perfect to work with web components as it enhances the developer experience and makes native web component much more appealing. learn more about working with Web Components