grohjy
grohjy

Reputation: 2149

How to observe property changes with LitElement

I don't know how to observe property changes in LitElement.

I've been trying these methods, but I couldn't get these to work:

  static get properties() {
    return {
      temp1:String,
      temp2: {
       type:String,
       observer: '_temp2Changed'
        }
  };

  temp1Changed(newValue, oldValue){
    console.log("temp1 changed")
  }
  _temp2Changed(newValue, oldValue){
    console.log("temp2 changed")
  }

Upvotes: 10

Views: 22662

Answers (6)

Nur Ilyas
Nur Ilyas

Reputation: 121

Implement the shouldUpdate() method. Source here.

Controls whether an update should proceed. Implement shouldUpdate to specify which property changes should cause updates. By default, this method always returns true.

function shouldUpdate(changedProperties: Map<any, any>) {
    changedProperties.forEach((oldValue, propName) => {
        const newValue = JSON.stringify(this[propName]);
        console.log(`${propName} changed. oldValue: ${oldValue} newValue: ${newValue}`);
    });
    return true;
}

Upvotes: 0

Marco Monz&#243;n
Marco Monz&#243;n

Reputation: 51

Properties 'hasChanged' option:

You can define this 'hasChanged' option in a property declaration within the static properties getter. Just like you do with 'type' option.

hasChanged should return true to update the element.

myProp: { hasChanged(newVal, oldVal) {
 // compare newVal and oldVal
 // return `true` if an update should proceed
}}

Chek it out here

Lifecycle method: updated

There is a lifecycle method named 'updated' that gets triggered every time any property changes.

This method receives as params the 'changedProperties' with their previous values so you can compare them to current values, then decide whatever you want to do with that.

Check out 'updated' documentation here.

Upvotes: 4

bcalisch
bcalisch

Reputation: 47

LitElement automatically re-renders every time the property changes, so long as the property is defined in your '_render' method.

This is noted in the LitElement readme:

React to changes: LitElement reacts to changes in properties and attributes by asynchronously rendering, ensuring changes are batched. This reduces overhead and maintains consistent state.

For example:

_render({firstName, lastName}) {
  return html`
    <div> Hello ${firstName} ${lastName} </div>
  `
};

Will re-render every time your firstName and lastName properties change. I hope this provides a little bit of clarity.

Upvotes: 1

6pi
6pi

Reputation: 417

I personally override the "requestUpdate" method to be aware of a change before rendering.

My use-case is to intercept a change of a "label" attribute to trigger asynchronous data request.

Snippet below (in TypeScript):

@customElement('my-element')
export default class MyElement extends LitElement {

    @property({type: String})
    label: string | null = null;

    @property({attribute: false})
    private isLoading: boolean = false;

    @property({attribute: false, noAccessor: true})
    private data: MyData | null = null;

    protected render() {/*some code*/}

    requestUpdate(name?: PropertyKey, oldValue?: unknown) {
        if(name && name == "label" && this.label !== oldValue) {
            this.isLoading = true;
            requestData(this.label, this._callbackData);
        }
        return super.requestUpdate(name, oldValue);
    }

    private _callbackData(data: MyData}) {
        this.data = data;
        this.isLoading = false;
    }

}

In this way, my element is rendered only twice: one with the new label and loading as true then one other when data are available.

Upvotes: 4

Michał Pietraszko
Michał Pietraszko

Reputation: 6199

Version 0.6.0+

First, you have to specify element properties. You can do it by creating a static getter which returns an object including their names as keys and their attribute related configuration.

The updated lifecycle method will be called when changed properties had caused a re-render. The first argument will return values before an update.

class MyComponent extends LitElement {
  static get properties() {
    return {
      foo: {}
    };
  }

  constructor() {
    super();
    this.foo = 0;
    setInterval(() => this.foo++, 1000);
  }

  updated(changedProperties) {
    console.log(changedProperties); // logs previous values
    console.log(this.foo); // logs current value
  }

  render() {
    return html`${this.foo}`;
  }
}

customElements.define("my-component", MyComponent);

Upvotes: 12

grohjy
grohjy

Reputation: 2149

I found a solution from Polymer's PWA Starter kits.

Add following to your element definition:

_propertiesChanged(props, changed, oldProps) {
    console.log("_propertiesChanged(props, changed, oldProps):");
    console.log(props);    
    console.log(changed);  
    console.log(oldProps); 
    if (changed && 'temp1' in changed) {
      console.log("if temp1:"+changed.temp1);
    }
    super._propertiesChanged(props, changed, oldProps); //This is needed here
}

console output:
_propertiesChanged(props, changed, oldProps):
{temp1: "newVal1", temp2: "val2"}
{temp1: "newVal1"}
{temp1: "val1"}
if temp1:newVal1

Upvotes: 0

Related Questions