lit-html-brackets v0.3.0
lit-html-brackets
Extension to lit-html that supports a syntax using brackets, similar to Angular's templates.
Overview
const template = ({ isAuthenticated, login, logout, options, refs }) => html`
<label>
<input #rememberMe=${refs} type="checkbox" [(checked::change)]=${bind(options, 'rememberMe')}>
Remember me?
</label>
<button #=${bind(refs, 'loginButton')}
class="login-cta" [class.login-cta--logged-on]=${isAuthenticated}
(click)="${isAuthenticated ? logout : login}" (keyup.enter)=${isAuthenticated ? logout : login}
>
${isAuthenticated ? 'Log out' : 'Log in'}
</button>
`;- Use
[]in attributes to get property binding- Use
[class.foo]to show/hide the classfoodepending on the truthiness of the value - Use
[style.foo]to bind the value to to thefoostyle property
- Use
- Use
()in attributes for event binding- Listeners for
keyup/keydownsupport binding to a single key or a key with modifiers, with slightly different semantics from Angular.
- Listeners for
- Use
[()]for two way binding. This requires use of thebindfunction. - Use
#to get references to the elements. This can be used with#prop=${object}whereobject.propwill be set to the element instance, or#name=${callback}wherecallback(elementRef, 'name')will be called. Thenamecan be empty. - The
bindfunction which can be used with the three types of bindings.[prop]=${bind(obj, propName)}: identical to[prop]=${obj[propName]}(event)=${bind(obj, propName)}: identical to(event)=${e => obj[propName] = e.detail}. This usesCustomEvent#detailand as such only works for custom events, not for browser events.[(prop)]=${bind(obj, propName)}: identical to[prop]=${obj[propName]}combined with(prop-changed)=${() => obj[propName] = elementRef.prop}whereelementRefis the element on which the property is bound.[(prop::some-event)]=${bind(obj, propName)}: identical to[prop]=${obj[propName]}combined with(some-event)=${() => obj[propName] = elementRef.prop}whereelementRefis the element on which the property is bound.#=${bind(obj, propName)}: identical to#propName=${obj}
- All other bindings are left as is, i.e. node bindings are not changed and attributes that don't use
[]or()are simply set as attributes. - The
[],()and[()]syntax only works in attributes with a${}value due to howlit-htmlinternally works.
Motivation
- lit-html is awesome but by default it lacks options to set properties or event binding.
The extension provided by lit-html to introduce a Polymer-like syntax for setting properties and event listeners (
property,attribute$andon-event) leads to confusing behaviour, which this extension's syntax ([property],attributeand(event)) doesn't:
This extension defaults to attributes, so if you don't write[]or()anywhere you are really just writing regular HTML, while the lit-html extension makes you set properties instead of attributes:/* The following template behaves differently depending on the render function used: * - The default `render` exposed by lit-html and the `render` function exposed by lit-html-brackets * will set attributes `a` to `"foo"` and `b` to `"bar"`. * - The `render` function exposed by lit-html's extension sets the `a` attribute to `"foo"` but it * sets the `b` property to `"bar"`. */ const template = html`<div a="foo" b=${'bar'}></div>`;
Differences with Angular template syntax
Events listeners should be passed instead of called, that is:
// lit-html-brackets syntax html`<div (click)=${onClick}></div>`vs
<!-- angular syntax --> <div (click)="onClick($event)"></div>Event listeners can be registered with negative modifiers
noshift,noalt,nocontrolandnometa. These will only fire the listener if the modifier is absent.- Event listeners are fired even if unspecified modifiers are present. Let's take the example of a listener registered
to
keyup.enter. In Angular 5 that listener wouldn't fire forshift+enterkey-ups. In lit-html-brackets that listener will fire. Usekeyup.noshift.enterto get a listener that doesn't fire when shift is pressed. - Event listeners in Angular can be bound to window/document events. This is arguably more useful when used with
Angular's
@HostBinding('window:scroll')annotation than inside a template<div (window:scroll)="...">. As such, lit-html-brackets doesn't support these global event listeners. - Two way binding has to be used with the
bindfunction, otherwise it results in one-way binding. This simply because we need the object and the property key to create two way binding. In Angular's templates, the object is known: the component instance. This is not the case for lit-html-brackets.