Search code examples
javascriptpolymerpolymer-1.0observers

How do I observe sub-property changes in Polymer?


For this object:

obj = {
        a:'a value',
        b:'b value'
      }

I need to call a function whenever a's or b's value is changed. I saw Observe changes for an object in Polymer JS, but that solution is not working for me.

I tried unsuccessfully to add an observer like this:

 observers: [
    'abc(obj.*)' //abc is function which need to be called
 ]

and:

 observe: {
    'obj.a': 'abc'     // here I'm observing a's attribute
 },

Plunker: http://plnkr.co/edit/BvX25wJHJh7i2aeyVBks?p=preview


Solution

  • In your Plunker, the observer is not called after the changes to obj.a in your ready() callback because the subproperty was directly assigned, which does not automatically fire a change-event for observers (or data bindings) to see. Here's the corrected plunker.

    Polymer docs describe how to observe subproperty changes:

    In order for Polymer to properly detect the sub-property change, the sub-property must be updated in one of the following two ways:

    There's also a third way: by calling notifyPath.

    After making changes to this.obj.a, you can notify observers and update bindings by calling this.notifyPath('obj.a', this.obj.a):

    this.obj.a = 100;
    this.obj.a++;
    this.notifyPath('obj.a', this.obj.a);
    

    HTMLImports.whenReady(() => {
      Polymer({
        is: 'x-foo',
        properties: {
          obj: {
            type: Object,
            value: () => ({a: 1, b: 2})
          }
        },
        observers: [
          '_objChanged(obj.a, obj.b)'
        ],
        _objChanged: function(a, b) {
          console.log('a', a, 'b', b);
        },
        _doNotifyPath: function() {
          this.obj.a++;
          this.obj.b++;
          this.notifyPath('obj.a', this.obj.a);
          this.notifyPath('obj.b', this.obj.b);
        }
      });
    });
    <head>
      <base href="https://polygit.org/polymer+1.11.3/components/">
      <script src="webcomponentsjs/webcomponents-lite.js"></script>
      <link rel="import" href="polymer/polymer.html">
    </head>
    <body>
      <x-foo></x-foo>
    
      <dom-module id="x-foo">
        <template>
          <div>obj.a = [[obj.a]]</div>
          <div>obj.b = [[obj.b]]</div>
          <button on-tap="_doNotifyPath">Notify Path</button>
        </template>
      </dom-module>
    </body>

    Alternatively, you could combine the setting and notification with this.set('obj.a', 'new value'):

    this.set('obj.a', this.obj.a + 1);
    

    HTMLImports.whenReady(() => {
      Polymer({
        is: 'x-foo',
        properties: {
          obj: {
            type: Object,
            value: () => ({a: 1, b: 2})
          }
        },
        observers: [
          '_objChanged(obj.a, obj.b)'
        ],
        _objChanged: function(a, b) {
          console.log('a', a, 'b', b);
        },
        _doSet: function() {
          this.set('obj.a', this.obj.a + 1);
          this.set('obj.b', this.obj.b + 1);
        }
      });
    });
    <head>
      <base href="https://polygit.org/polymer+1.11.3/components/">
      <script src="webcomponentsjs/webcomponents-lite.js"></script>
      <link rel="import" href="polymer/polymer.html">
    </head>
    <body>
      <x-foo></x-foo>
    
      <dom-module id="x-foo">
        <template>
          <div>obj.a = [[obj.a]]</div>
          <div>obj.b = [[obj.b]]</div>
          <button on-tap="_doSet">Set</button>
        </template>
      </dom-module>
    </body>

    codepen