Search code examples
data-bindingpolymerweb-componentpolymer-1.02-way-object-databinding

Data binding between two Polymer elements using polymer 1.0


Question

In the below example, how do I bind the obj.name variable of the <input> field in <test-element2> to <test-element> ?


Background:

Below is my code. I have two polymer elements. test-element has the data binded to obj.name. test-element2 has an input field which is observed by the function objChanged. Whatever value I change in the input field it changes and prints in test-element2 but the change isn't reflected in test-element. Can any body help get the value reflected to test-element1? I have a solution using this.fire("object-change") for when the text changes but I am looking for a solution without using event listeners.

One more thing is that I need to create an element from the script and it cannot be hardcoded in the HTML DOM.


Code:

<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Demo</title>

    <script src="../../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
    <link rel="import" href="../../bower_components/polymer/polymer.html"/>
</head>

<body>
    <dom-module id="test-element">
        <template>
            <div>Hello <span>{{obj.name}}</span></div>
        </template>

        <script>
            TestElement = Polymer({
                is: "test-element",

                properties: {
                    "obj": {
                        type: Object,
                        notify: true
                    }
                },

                observers: [
                    "objChanged(obj.name)"
                ],
                "objChanged": function() {
                    var that = this;
                    console.log("First element in 1",that.obj);
                }
            });
        </script>
    </dom-module>


    <dom-module id="test-element2">
        <template>
            <input value="{{obj.name::input}}"/>
        </template>

        <script>
            Polymer({
                is: "test-element2",

                properties: {
                    "obj": {
                        type: Object,
                        notify: true,
                        value: {
                            "name": "Charlie"
                        }
                    }
                },

                observers: [
                    "objChanged(obj.name)"
                ],

                ready: function() {
                    var element = new TestElement();
                    element.set("obj", this.obj);
                    this.appendChild(element);
                },

                "objChanged": function() {
                    console.log("changed in test-element2:", this.obj);
                }
            });
        </script>
    </dom-module>


    <test-element2></test-element2>
 </body>
</html>

Solution

  • If you include <test-element> in the <template> of test-element2 you can avoid using event listeners or observers. In this way test-element2 handles the data binding between the input and <test-element> for you.

    Below is a live working example that maintains the obj property as you have set it up in your elements.

    <script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents.min.js"></script>
    <link rel="import" href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
    <dom-module id="test-element">
      <template>
        <div>Hello <span>[[obj.name]]</span>
        </div>
      </template>
      <script>
        TestElement = Polymer({
          is: "test-element",
    
          properties: {
            "obj": {
              type: Object,
              notify: true
            }
          }
        });
      </script>
    </dom-module>
    <dom-module id="test-element2">
      <template>
        <input value="{{obj.name::input}}" />
        <test-element obj="[[obj]]"></test-element>
      </template>
    
      <script>
        Polymer({
          is: "test-element2",
    
          properties: {
            "obj": {
              type: Object,
              notify: true,
              value: {
                "name": "Charlie"
              }
            }
          }
        });
      </script>
    </dom-module>
    <test-element2></test-element2>

    Currently, imperative data-binding is not supported in Polymer 1.0 outside of <template is="dom-bind">.

    I would recommend setting up observers like the example below or adjusting your requirements to include <test-element> in test-element2.

    button {
      display: block;
    }
    <script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents.min.js"></script>
    <link rel="import" href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
    <dom-module id="test-element">
      <template>
        <div>Hello <span>[[obj.name]]</span>
        </div>
      </template>
      <script>
        TestElement = Polymer({
          is: "test-element",
    
          properties: {
            obj: {
              type: Object,
              notify: true
            }
          }
        });
      </script>
    </dom-module>
    <dom-module id="test-element2">
      <template>
        <input value="{{obj.name::input}}" />
      </template>
    
      <script>
        Polymer({
          is: "test-element2",
    
          properties: {
            obj: {
              type: Object,
              notify: true,
              value: {
                "name": "Charlie"
              }
            }
          },
          observers: ["objNameChanged(obj.name)"],
          objNameChanged: function(newValue) {
            Polymer.dom(document).querySelectorAll("test-element").forEach(function(element) {
              element.notifyPath("obj.name", newValue);
            });
    
            Polymer.dom(this.root).querySelectorAll("test-element").forEach(function(element) {
              element.notifyPath("obj.name", newValue);
            });
          }
        });
      </script>
    </dom-module>
    <test-element2></test-element2>
    <button>Add test-element to <em>test-element2</em>
    </button>
    <button>Add test-element to <em>body</em>
    </button>
    <script>
      var testElement2 = document.querySelector("test-element2");
    
      var createTestElement = function(insertPoint) {
        var testElement = new TestElement();
        testElement.notifyPath("obj.name", testElement2.obj.name);
    
        insertPoint.appendChild(testElement);
      };
    
      document.querySelector("button:nth-of-type(2)").addEventListener("click", function() {
        createTestElement(Polymer.dom(document).querySelector("body"));
      });
    
      document.querySelector("button").addEventListener("click", function() {
        createTestElement(Polymer.dom(testElement2.root));
      });
    </script>