iron-input icon indicating copy to clipboard operation
iron-input copied to clipboard

unpleasant side-effect of bindValue default value = '' (#130)

Open christophe-g opened this issue 6 years ago • 0 comments

Description

While upgrading my app to Polymer 2.0, I encountered a pretty nasty side effect of #130 (bindValue having a default value = ''), where persistent data is being wiped in situation like :

<firebase-document path="/myPath" data= "{{data}}"></firebase-document>
<paper-input label="label" value="{{data.WILL_BE_DELETED}}"></paper-input>

Expected outcome

data.WILL_BE_DELETED is saved/synched between persistent layer (firebase) and paper-input.

Actual outcome

Persisted data is being wiped out because iron-input instantiate a default '' value, which is being notified to the persistent layer.

Fix

Prevent iron-input to notify upstream when the element is being attached (_initSlottedInput) and bindValue = "" (default value).

Tests are passing.

If this seems acceptable, I will propose a PR.


      _initSlottedInput: function() {
        this._isInitiatingSlottedInput = true;
        this._inputElement = this.getEffectiveChildren()[0];

        if (this.inputElement && this.inputElement.value) {
          this.bindValue = this.inputElement.value;
        }

        this._isInitiatingSlottedInput = false;
        this.fire('iron-input-ready');
      },
      
      /**
       * @suppress {checkTypes}
       */
      _bindValueChanged: function(bindValue, inputElement) {
        // The observer could have run before attached() when we have actually
        // initialized this property.
        if (!inputElement) {
          return;
        }

        if (bindValue === undefined) {
          inputElement.value = null;
        } else if (bindValue !== inputElement.value) {
          this.inputElement.value = bindValue;
        }

        if (this.autoValidate) {
          this.validate();
        }

        // Note(cg): prevent notifying the change when initiating slotted input and bindValue is default. 
        if(this._isInitiatingSlottedInput && bindValue === '') {
          return;
        }
        // manually notify because we don't want to notify until after setting value
        this.fire('bind-value-changed', {value: bindValue});
      },

Steps to reproduce

Example bellow will produce: image

<html>
<head>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">

  <title>iron-input demo</title>

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
  <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">

  <link rel="import" href="../../paper-input/paper-input.html">
   <!-- Order of imports matters. The validator needs to be imported before the
    iron-input needs it. -->
  <link rel="import" href="../iron-input.html">
  
  <custom-style>
    <style is="custom-style" include="demo-pages-shared-styles"></style>
  </custom-style>
</head>
  
  <dom-module id="app-test">
    <template>
      <style>
        :host {
          display: block;
        }
        
      </style>
      <ul>
      <template is="dom-repeat" items="[[logs]]">
          <li >name: [[item]]</li>
      </template>
      </ul>
      <div >name: [[data.name]]</div>
    </template>
    <script>
      Polymer({
        is: 'app-test',
        
        properties: {
          
          data:  {
            type: Object,
            notify: true, 
            value: {}
          },

          logs: {
            type: Array, 
            value: []
          }
        },
  
        observers: ['_observeData(data, data.*)'],
        
        _observeData() {
          this.push('logs', JSON.stringify(this.data));
          if(this.data && this.data.name === '') {
            this.push('logs', 'EMPTY !!!');
          }
        }
      });
    </script>
  </dom-module>
</script>

<body>
  <div class="vertical-section-container centered">
    <h4>Basic inputs</h4>
    <demo-snippet class="">
      <template>
         <dom-bind id="app">
          <template>
            <app-test data="{{data}}"></app-test>
            <paper-input placeholder="name" label="my name" value="{{data.name}}"></paper-input>
          </template>
        </dom-bind>
      </template>
    </demo-snippet>
    <script>
    window.addEventListener('WebComponentsReady', function() {

      const app = document.querySelector('#app');
      app.data = {name:'cg'}

    });
    </script>
  </div>
</body>
</html>

Browsers Affected

  • [x] Chrome
  • [x] Firefox

christophe-g avatar Nov 29 '18 15:11 christophe-g