简体   繁体   中英

Data binding on JavaScript HTMLElement property in Polymer

Making an SPA using Polymer, and I need my custom components to all use a common custom component which represents my backend API and is responsible of GET-ting/POST-ing data from/to the API. It also serves as a "cache" and holds the data to display. This way, all the components that have access to this single element will share the same data.

So what I want to do is this... :

<my-api
  users="{{users}}"
  products="{{products}}">
</my-api>

...but programmatically, as <my-api> is not declared in all of my components but once in the top one and then passed down through the hierachy by JavaScript:

Polymer({
  is: 'my-component',
  properties: {
    api: {
      observer: '_onApiChanged',
      type: HTMLElement
    },
    products: {
       type: Array
    },
    users: {
       type: Array
    }
  },
  _onApiChanged: function(newVal, oldVal) {
    if (oldVal)
      oldVal.removeEventListener('users-changed', this._onDataChanged);

    // Listen for data changes
    newVal.addEventListener('users-changed', this._onDataChanged);

    // Forward API object to children
    this.$.child1.api = newVal;
    this.$.child2.api = newVal;
    ...
  },
  _onDataChanged: function() {
    this.users = this.api.users; // DOESN'T WORK as 'this' === <my-api>
    this.products = this.api.products; // Plus I'd have to repeat for every array
  }
});

Does Polymer offers a built-in way to do this ? Can I create a double curly braces binding programmatically ?

I would likely architect this slightly differently: passing down the products / users arrays declaratively taking advantage of Polymer's binding system. Or you could write your my-api element in such a way that they all share state and the first declared one is the primary while future declared ones are replicas. This would let you declare them wherever you need them and bind to the values via Polymer's normal ways.

But to answer your question, there's currently no way to easily programmatically setup the same kind of binding without using private Polymer APIs.

To avoid repeating as much and for the binding issue you were having you could use Polymer's built-in listen and unlisten methods:

Polymer({
  is: 'my-component',
  properties: {
    api: {
      observer: '_onApiChanged',
      type: HTMLElement
    },
    products: {
       type: Array
    },
    users: {
       type: Array
    }
  },
  _onApiChanged: function(newVal, oldVal) {
    var apiProperties = ['users', 'products'];

    if (oldVal) {
      apiProperties.forEach(function(prop) {
        this.unlisten(oldVal, prop + '-changed', '_onDataChanged');
      });
    }

    // Listen for data changes
    apiProperties.forEach(function(prop) {
      this.listen(newVal, prop + '-changed', '_onDataChanged');
    });

    // Forward API object to children
    this.$.child1.api = newVal;
    this.$.child2.api = newVal;
    ...
  },
  _onDataChanged: function() {
    this.users = this.api.users; // `this` should be the element now
    this.products = this.api.products;
  }
});

Given how this is a common pattern you're doing, you could probably get a lot of benefit out of extracting some of these things into a Behavior that abstracts away the binding/unbinding and API element forwarding.

Another optimization you may could make work would be to to look at the event passed to _onDataChanged to see if you can infer which value changed and update your corresponding property. This could prevent you needing to add a line for every property.

I ended up using an other solution. Instead of manually passing the top <my-api> element down the hierarchy any element that needs access to this shared data declares its own <my-api> .

Then in the <my-api> element's declaration I made that all instances use the same arrays references. So whenever I update one they all get updated, and I don't have to pass anything down the HTML hierarchy, which makes things a LOT simpler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM