Lit-html: What would be the best way to add event handlers in lit-html

Created on 7 Nov 2017  路  13Comments  路  Source: Polymer/lit-html

Coming from Polymer, they used on-event-name="eventHandlerName" which automatically calls the eventHandlerName method in that component

As for React, they used onEventName={eventHandlerName} which uses the function eventHandlerName.

What would be the best way to add event handlers, and if it is not yet implemented, would there be a proposed way to do it. I'm still studying how this lit-html works and would like to contribute if it is possible to do the same thing, detecting a call to create event handlers by using the on- prefix but putting the function instead of the name...

Most helpful comment

you can just use lib/lit-extended it supports for example
<button on-click=${(e) => { console.log(e); }}>click me</button>

All 13 comments

you can just use lib/lit-extended it supports for example
<button on-click=${(e) => { console.log(e); }}>click me</button>

you can just use lib/lit-extended

Yup. Notice there's no special "-" to capital rule, it's simple on-eventName sets the element[eventName] = listener.

@jridgewell So... If I understand right, I can use both variants on-click, onclick (I tested: both working correctly).

@daKmoR So cool!!! Will try it out!

Is there a plan to merge this into the top-level lit-html.js?

@nikhilk no, because in HTML you can only set attributes, and setting properties requires some syntax or semantics that's definitely non-standard. lit-html core is unopinionated, so that's why lit-extended exists.

Closing this issue as WAI.

Fair enough if that is the driver of whats in core vs. extended, though it seems things like onclick are quite attribute-like, even if that is a loose interpretation of what an attribute is.

More interestingly, I am curious what is the recommended way of picking up extended, so I can follow the right approach. My understanding is something like:

import * as lit from 'lit-html';

would pick up just the core functionality in lit-html.js. Is there a good way to pick up the extended version without directly referring to the built 'lit-extended.js' file?

I echo nikhilk's question.

To use lit-extended you have to import lit-extended:

import {html, render} from '../lit-html/lib/lit-extended.js';

There's no way to use lit-extended without importing lit-extended. Not sure what you would need that for.

FYI, here is how to import lit-extended instead of the standard lit-html:

import {html, render} from 'lit-html/lib/lit-extended'

Please correct me if I'm wrong, but as far as I understand lit-html extended (which supports events on the template) is not able to do some magical caching to variables within tagged template literals, so recommendation with example for antipatterns will be like this

_onInput() {
  // do stuff with `this`
}

// BAD: function is created on each render causing removal/readding the listener
_render() {
  return html`<input on-input="${e => this._onInput(e)}">`;
}

// BAD: same as previous, still function is recreated on each render
// remember: this._onInput.bind(this) !== this._onInput.bind(this)
// every call to .bind(this) creates a new instance
_render() {
  return html`<input on-input="${this._onInput.bind(this)}">`;
}


// GOOD: function was binded once in constructor, now same reference is reused
// remember: you don't need to create a method with a different name, just use the same
// this._methodName = this._methodName.bind(this);
constructor() {
  super();
  this._onInput = this._onInput.bind(this);
}
_render() {
  return html`<input on-input="${this._onInput}">`;
}

@bashmish It is not really necessary though you can do it if you want. Since lit-html is not a Virtual DOM, on-* listeners are not real properties but a source for addEventListener method. And they are optimized to avoid reassigning listeners as you can see in this code.

So I can say that using inline callbacks in lit-html doesn't cost more than using them in array methods like map, filter, etc.

@Lodin thanks for pointing that out, my bad

so the recommendation would be quite opposite then, depending on particular case you can choose from these two

_render() {
  return html`<input on-input="${e => this._onInput(e)}">`;
}

_render() {
  return html`<input on-input="${this._onInput.bind(this)}">`;
}

UPDATE:
The more I think about this, the more example see in our code, the more I understand that the guideline should be fist of all to understand how EventPart works within lit-html. All 3 ways of doing that are valid, but may be sometimes if you have too many event handlers in one template and same handler is reused multiple times, it is better to remove that overhead and cache the function in the constructor... while in other case it would be just a premature optimization. Anyway, thanks for letting me know that EventPart is different and quite smart.

Was this page helpful?
0 / 5 - 0 ratings