0

How to bind a dynamic path in Polymer?

For instance: Lets say our component has 2 properties:

  1. list: an array of objects.
  2. map : a javascript object which map sub-objects.

Each item in the list has a property key which is the key to get the value from the map property. I would like to "dynamic" bind a path like map[item.key]. The only way to do something like this is to make a function, but it will not be triggered on changes of properties and sub-properties of map. =/

In the following snippet, you can see, by clicking on the button, it will dynamicly place an object in the map.key2 property, using the Polymer.Element.set method. But this doesn't trigger any changes because Polymer doesn't bind a path. It only execute the display function once. So this Stackoverflow answer doesn't help (even though it's the same question).

<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="employee-list.html">
<link rel="import"  href="https://polygit.org/components/polymer/polymer-element.html">
<link rel="import"  href="https://polygit.org/components/paper-button/paper-button.html">
<link rel="import"  href="https://polygit.org/components/polymer/lib/elements/dom-repeat.html">


<dom-module id="my-element">

 <template>
  <ul>
   <template is="dom-repeat" items="[[list]]">
    <!--Bind something like this-->
    <li> [[ _getAt(item.key) ]] </li>
   </template>
  </ul>

  <!--This button will add the 2nd object-->
  <paper-button on-tap="_onButtonClicked" raised>Add key 2</paper-button>
 </template>

 <script>
  class MyElement extends Polymer.Element {
   static get is(){
    return "my-element";
   }

   static get properties(){
    return {
     list : {
      type : Array,
      value : function () {
       return [
        {
         key : "key1",
         // ...
        },
        {
         key : "key2",
         // ...
        },
        // ...
       ]
      }
     },
     map : {
      type : Object,
      value : function () {
       return {
        key1 : {
         message : "Hello",
         // ...
        },
        // ...
       }
      }
     }
    };
   }

   _onButtonClicked(){
    // Add the 2nd object
    this.set("map.key2", {
     message : "World",
    });
        
     console.log("key 2 added");
   }
      
      _getAt(key){
        if (this.map[key])
          return this.map[key].message;
      }
  }

  customElements.define(MyElement.is, MyElement);
 </script>
</dom-module>

<my-element></my-element>

The Polymer documentation says that it's possible to build a path in a array. But I didn't find a way to bind an array of string as a path.

Yairopro
  • 9,084
  • 6
  • 44
  • 51

1 Answers1

0

"The only way to do something like this is to make a function, but it will not be triggered on changes of properties and sub-properties of map"

You can make this work by passing the objects/properties that are changing. Basic example

this will NOT update:

<template is="dom-repeat" items="[[_getItems()]]"></template>

this WILL update:

<template is="dom-repeat" items="[[_getItems(list)]]"></template>

Now the dom-repeat will fire again once the property 'list' changes. Let's say you have 2 properties, and you want to re-run dom-repeat when one of them changes:

<template is="dom-repeat" items="[[_getItems(list, somethingElse)]]"></template>

You might also want to take a look at https://www.polymer-project.org/1.0/docs/devguide/model-data#override-dirty-check

EDIT:

Where are you updating the property LIST? dom-repeat wont run again until that property is changed

DOUBLE EDIT:

try this (polygit is currently having server issues):

<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="employee-list.html">
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<link rel="import" href="https://polygit.org/components/paper-button/paper-button.html">
<link rel="import" href="https://polygit.org/components/polymer/lib/elements/dom-repeat.html">


<dom-module id="my-element">

    <template>
  <ul>
   <template is="dom-repeat" items="[[list]]">
    <!--Bind something like this-->
    <li> [[ _getAt(item.key, map, list) ]] </li>
   </template>
     </ul>

    <!--This button will add the 2nd object-->
    <paper-button on-tap="_onButtonClicked" raised>Add key 2</paper-button>
    </template>

    <script>
        class MyElement extends Polymer.Element {
            static get is() {
                return "my-element";
            }

            static get properties() {
                return {
                    list: {
                        type: Array,
                        value: function() {
                            return [{
                                    key: "key1",
                                    // ...
                                },
                                {
                                    key: "key2",
                                    // ...
                                },
                                // ...
                            ]
                        }
                    },
                    map: {
                        type: Object,
                        value: function() {
                            return {
                                key1: {
                                    message: "Hello",
                                    // ...
                                },
                                // ...
                            }
                        }
                    }
                };
            }

            _onButtonClicked() {
                var foo = this.list;

                this.set("map.key2", {
                    message: "World",
                });
               this.set("list", {});
               this.set("list", foo);
                console.log("key 2 added");
            }

            _getAt(key) {
                if (this.map[key])
                    return this.map[key].message;
            }
        }

        customElements.define(MyElement.is, MyElement);
    </script>
</dom-module>

<my-element></my-element>
Siebren K.
  • 41
  • 8
  • You're are forcing the entire list to refresh. Thats realy not optimized. If that was my puspose, I would just set the `mutable-data` property on the array, and trigger the `notifyPath` method on the list. – Yairopro Oct 31 '17 at 00:13
  • Furthermore, I know this trick to pass objects as parameters to the method, becaise they are considered as dependency. But this doesn't help if the `map.key2.message` property changes. Neither for binding upward (using `{{path}}`) because bindong functions are only downward. – Yairopro Oct 31 '17 at 00:17