Template Components were added in version 2.1.0.
Template Components¶
Template components are web components based on HTML templates. They use a template syntax similar to that of Angular. Expressions used inside of the template string are mapped onto properties of the corresponding component.
const template = `<div title={{ this.title }}>Hello {{ this.name }}</div>`;
class MyComponent extends TemplateComponent.fromString(template) {
constructor() {
super();
this.title = '';
this.name = '';
}
}
customElements.define('my-component', MyComponent);
Using the above example template will result in a web component which defines
two properties title
and name
. Setting them will automatically update the
DOM in the next rendering frame.
Inside of strings template expressions can be used inside of Node textContent,
attributes and using special syntax as properties, style properties and DOM
event handlers.
Properties referenced in template expressions can be bound to using
awml-option
tag with option type=bind
. As an example for the above component
this can be done using
<my-component>
<awml-option type=bind sync name=title src='.../Title'></awml-option>
<awml-option type=bind sync name=name src='.../Name'></awml-option>
</my-component>
Attributes¶
Template expressions can be used to control attribute values.
const template1 = `<div title={{ this.title }}></div>`;
const template2 = `<div title="Title: {{ this.title }}"></div>`;
Container Contents¶
Templates expressions can be used to control node content which allows either strings or arbitrary DOM nodes to be placed into the DOM.
const template = `<div>{{ this.content }}</div>`;
How the content is placed into the DOM depdends on the data type of the data.
Values of type
'string'
,'number'
or'boolean'
are rendered into the DOM as strings inside of a TextNode.The values
null
andundefined
result in no content, instead a CommentNode is placed into the DOM as a placeholder.Values of type
Node
are placed directly into the DOM. Naturally, using the same Node as content for two different template expressions does not work. Template Components do not guard against this usage and it will result in subtle issues.
Properties¶
Template expressions can be used to control node properties with a special syntax using square brackets.
const template1 = `<input type=text [value]={{ this.value }} />`;
Style properties¶
Template expressions can be used to control style properties using a special syntax using square brackets.
const template1 = `<div [style.color]={{ this.color }}></div>`;
Event handlers¶
Template expressions can be used to attach event handlers to DOM nodes.
const template = `<button (click)={{ this.onClick }}>click me</button>`;
class MyComponent extends TemplateComponent.fromString(template) {
constructor() {
super();
this.onClick = (ev) => {
// handle click event.
};
}
}
customElements.define('my-component', MyComponent);
Node References¶
DOM nodes inside of templates can be assigned names which make them available as properties on the resulting web components.
const template = `<div #hello>Hello</div>`;
This can be useful to access specific DOM nodes inside of the resulting component. It can also be useful when trying to create bindings to child components.
const template = `<aux-fader #gain></aux-fader>`;
customElements.define('my-channel', TemplateComponent.fromString(template));
Then inside the DOM options of the fader can be bound to using awml-option
.
<my-channel>
<awml-option type=bind name='gain.value' src='...'></awml-option>
</my-channel>
Optional Nodes¶
DOM nodes inside of templates can be added or removed based on the value of a template expression. This can be used to implement optional parts of an interface.
const template `
<aux-fader></aux-fader>
<aux-toggle %if={{ this.hasMute }} label=mute></aux-toggle>
`;
Binding aliases¶
Options of components inside of templates can be assigned aliases which make them available as options on the resulting web components.
const template = `<aux-fader $gain=value></aux-fader>`;
customElements.define('my-channel', TemplateComponent.fromString(template));
Then inside the DOM options of the fader can be bound to using awml-option
.
<my-channel>
<awml-option type=bind name='gain' src='...'></awml-option>
</my-channel>
Bind directives¶
Bind directives can be used to install bindings on components inside of a template. They are internally based on a Bindings object and work similar to BindComponent. The main advantage to using a BindComponent is that they require no additional component.
const template = `<aux-fader %bind={{ this.faderBindings }}></aux-fader>`;
class MyComponent extends TemplateComponent.fromString(template) {
constructor() {
super();
this.faderBindings = [
{
src: '...',
},
];
}
}
customElements.define('my-component', MyComponent);
See the documentation of IBindingDescription for a specification of the possible parameters. All bindings will calculate their prefix starting from the node which they are installed on.
Properties of template expressions¶
Template components automatically extract properties from template expressions.
Those properties are then defined as properties on the resulting template
component. Specifically, for a property named VAR
, the actual value is
stored in the property _VAR
and initialized to null
; a getter is defined
which returns the value and a setter is defined which updates the value and
notifies all template expressions depending on this property to be updated in
the next rendering frame.
In addition for each property a getter is defined called VAR$
which returns
a dynamic value which represents the property. This can, for example, be used to
bind properties to e.g. widget options.
Properties are detected inside of template expressions as matches of the regular
expression /this\.([\w0-9$_]+)/
. The resulting list of properties can be
controlled with parameters to TemplateComponent.create()
.