0

I'm training with LitElement and lit-html. I'm trying to make complex templates with functions and event listener. I have a module for the template module and another for one component where I use the templates. I'm having problem with a template for a buttom which I pass a function as parameter and when I clicked on the buttom call the function. This works: it makes the call but the reference for this is lost. I thought a possible cause maybe the arrow function so I rewrote the function like this:

let timerElementOperation = function(operationTimer, operation,that){
    operationTimer.bind(that);
    return html` <button @click=${function(){operationTimer()}}>${operation}</button> `;
}

But the problem is still there. What's happening?

//timer-element.js
class TimerElement extends LitElement{
    ...
    static get properties(){
        return {
            running:  { type: Boolean, reflect: true}
        };
    }
    render(){
        let partialTemplate;
        if( this.isPausable(this.running, this.finished) && this.time > 0 ){
            partialTemplate = Template.timerElementOperation(this.pause, 'pause');
        } else if(!this.running && this.time > 0){
            partialTemplate = Template.timerElementOperation(this.resume,'resume');
        }
    pause(){
        this.running = false; // this is lost.
    }

}

//timer-templates.js
import {html}   from '@polymer/lit-element';
let timerElementOperation = (operationTimer, operation) => {
    return html` <button @click=${()  => operationTimer()}>${operation}</button> `;
}

export timerElementOperation;
Maarten
  • 4,643
  • 7
  • 37
  • 51
Ismael Rodriguez
  • 329
  • 3
  • 10
  • where/how are `this.pause` and `this.resume` defined? – Thomas Nov 08 '18 at 13:45
  • `this.pause` and `this.resume` are defined insid the class TimerElement. You can see the method pause down to render method. – Ismael Rodriguez Nov 08 '18 at 14:52
  • 1
    I guess you should bind `this.pause` before pass it. Try `Template.timerElementOperation(this.pause.bind(this), 'pause')`. – User 28 Nov 09 '18 at 06:36
  • 1
    And what is `Template`? How you can call `Template.timerElementOperation`? Thanks. – User 28 Nov 09 '18 at 06:37
  • 1
    Thanks! It's work with `Templale.timerElementOperation(this.pause.bind(this),'pause')`. I was setting the bind when the context had already changed. Template is is a import from the file timer-template.js. `import * as Template from '../templates/timer-template.js';` – Ismael Rodriguez Nov 09 '18 at 10:57
  • Could you please add the entire example? I need more context to answer your question. You can generally solve issues like this without explicitly rebinding methods to `this`. – nicojs Mar 20 '19 at 05:45

1 Answers1

0

How should the template in Template.timerElementOperation know what this should be? It's an isolated function.

There's two ways about this - the first is to explicitly bind this: replace this.pause with this.pause.bind(this). This will work, but honestly makes for fairly idiosyncratic code.

The common practice is to use the fact that any @event binding in Lit will have this bound for you to the class it's in, so:

class TimerElement 
    extends LitElement{
    ...

    renderTimerElementOperation(operation){
        return html`<button @click=${this[operation]}>${operation}</button>`;
    }

    render(){
        let partialTemplate;
        if( this.isPausable(this.running, this.finished) && this.time > 0 ){
            partialTemplate = this.timerElementOperation('pause');
        } else if(!this.running && this.time > 0){
            partialTemplate = this.timerElementOperation('resume');
        }

        return html`... ${partialTemplate} ...`
    }
}

This is a lot simpler, and you generally want to keep the templates with or in the associated elements, rather than imported. If you want something re-usuable create another LitElement to reuse that, rather than importing functions that render templates in isolation.

Keith
  • 150,284
  • 78
  • 298
  • 434