Search code examples
javascriptpolymer

How do I update the DOM in polymer 1.x when data is changed?


I am new to Polymer and come from an Angular background where data is shared between components using services.

I have two views (form-view and display-view) that I would like to share the data that is "stored" in an object in another element called data-store.

Here is the plunker

form-view

<dom-module id="form-view">

      <template>

        <style>
          :host {
            display: inline-block;
          }
        </style>

        <h1>Form View</h1>
        <label for="form-name">Name</label>
        <input id="form-name" value="{{formData.name::change}}">
        <label for="form-city">City</label>
        <input id="form-city" value="{{formData.city::change}}">

        <data-store form-data="{{formData}}"></data-store>
      </template>



      <script>
      Polymer({
        is: 'form-view',
        ready: function() {
          console.log("FORM VIEW", this.formData);
        },
        properties: {
          formData: {
            type: Object
          }
        }
      });
      </script>

    </dom-module>

display-view

      <template>

        <style>
          :host {
            display: inline-block;
          }
        </style>
        <h1>Display View</h1>
        <h4>Name: {{formData.name}}</h4>
        <h4>City: {{formData.city}}</h4>
        <button id="getData">Get Data</button>
        <data-store form-data="{{formData}}"></data-store>
      </template>

      <script>
      Polymer({
        is: 'display-view',
        properties: {
          formData: {
            type: Object
          }
        },
        ready: function(){
          var that = this;
          console.log('display view', this.formData);

          this.$.getData.addEventListener('click', function(evt) {
            console.log("Form Data from store", that.formData);
            that.set('formData.name', that.formData.name);
            that.set('formData.city', that.formData.city);
          })
        }
      });
      </script>

    </dom-module>

data-store

    <dom-module id="data-store">

      <template>

        <style>
          :host {
            display: inline-block;
          }
        </style>

      </template>

      <script>
      Polymer({
        is: 'data-store',
        properties: {
          formData: {
            type: Object,
            notify: true,
            value: {
              name: 'Hello',
              city: 'World'
            }
         }
        },
        observers: ['_dataChanged(formData.*)'],
        _dataChanged: function(change) {
          console.log("DATA CHANGED", change);
          console.log("Form Data", this.formData);
        }

      });
      </script>

    </dom-module>

I basically want to the display-view to be updated whenever I change an input on the form-view.

If you open plunker you will see that the form-view and display-view both show the original values for name and city from the data-store.

How I'm binding them:

<data-store form-data="{{formData}}"></data-store>

When I change either one of the inputs, the observer in the data-store fires the '_dataChanged' function, but the change is not updated on the display-view.

However, if you click on the "get data" button on the display-view after making a change on the "form-view" you will see that the change shows up on the formData object (in the console.log) just not in the view. I even tried to use:

  this.set('formData.name', this.formData.name);

Even this won't update the value on the display-view.

Can someone help me understand why my data is not being updated and how I can update an input on one view and have it change on all other views that are bound to the same object?

Thanks!


Solution

  • Polymer implements the mediator pattern, where a host element manages data flow between itself and its local DOM nodes.

    When two elements are connected with a data binding, data changes can flow downward, from host to target, upward, from target to host, or both ways.

    When two elements in the local DOM are bound to the same property data appears to flow from one element to the other, but this flow is mediated by the host. A change made by one element propagates up to the host, then the host propagates the change down to the second element.

    So, in the above code you were trying to make data flow between three target or child elements i.e. between data-store, form-view and display-view. That is why the data is not rendering in display-view. It would have displayed if data-store have stored the property in localstorage and other elements used that storage to pull that property. That is one way to do what you are looking for.

    Another way is to pass the formData from host element i.e. from parent-view. You can simply do:

    <data-store form-data="{{formData}}"></data-store>
    <iron-pages selected="[[routeName]]" attr-for-selected="name">
      <form-view form-data="{{formData}}" name="form"></form-view>
      <display-view form-data="{{formData}}" name="display"></display-view>
    </iron-pages>
    

    Check in the plnkr: https://plnkr.co/edit/KLw8G04qVPVPmderLlzd?p=preview.