iron-input
iron-input copied to clipboard
unpleasant side-effect of bindValue default value = '' (#130)
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:
<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