Search code examples
propertiesweb-componentsetter

WebComponents property setter not trigger if property defined before


WebComponents property setter not trigger if property defined before. As follows:

<foo-bar id='ele1'></foo-bar>
<foo-bar id='ele2'></foo-bar>
<script>
  ele1.foo = 'hello';

  class FooBar extends HTMLElement {

    set foo(val) {
      console.log(`set ${this.id} to ${val}`);
      this._foo = val;
    }

    get foo() {
      return this._foo
    }
  }

  customElements.define('foo-bar', FooBar);

  setTimeout(() => {
    ele1.foo = 'world';
    ele2.foo = 'world';

    console.log(`ele1.foo is ${ele1.foo}`);
    console.log(`ele2.foo is ${ele2.foo}`);
  }, 1000);
</script>

The console output (which set ele1 to world is not output`):

set ele2 to world
ele1.foo is world
ele2.foo is world

So I have to observe the property by Object.defineProperty like this:

<foo-bar id='ele1'></foo-bar>
<foo-bar id='ele2'></foo-bar>
<script>
  ele1.foo = 'hello';

  class FooBar extends HTMLElement {
    constructor() {
      super();
      this._foo = this.foo;

      Object.defineProperty(this, 'foo', {
        get: () => this._foo,
        set: val => {
          console.log(`set ${this.id} to ${val}`);
          this._foo = val;
        }
      })
    }
  }

  customElements.define('foo-bar', FooBar);

  setTimeout(() => {
    ele1.foo = 'world';
    ele2.foo = 'world';

    console.log(`ele1.foo is ${ele1.foo}`);
    console.log(`ele2.foo is ${ele2.foo}`);
  }, 1000);
</script>


Solution

  • <foo-bar id='ele1'></foo-bar>
    <foo-bar id='ele2'></foo-bar>
    <script>
      ele1.foo = 'hello';
    
      class FooBar extends HTMLElement {
        constructor() {
          super();
          
          this._foo = this.foo;
          delete this.foo;
        }
    
        set foo(val) {
          console.log(`set ${this.id} to ${val}`);
          this._foo = val;
        }
    
        get foo() {
          return this._foo
        }
      }
    
      customElements.define('foo-bar', FooBar);
    
      setTimeout(() => {
        ele1.foo = 'world';
        ele2.foo = 'world';
    
        console.log(`ele1.foo is ${ele1.foo}`);
        console.log(`ele2.foo is ${ele2.foo}`);
      }, 1000);
    </script>