diff --git a/lib/mixins/properties-changed.html b/lib/mixins/properties-changed.html
index 80b595c4ad..c7bce993ba 100644
--- a/lib/mixins/properties-changed.html
+++ b/lib/mixins/properties-changed.html
@@ -117,6 +117,7 @@
this.__dataPending = null;
this.__dataOld = null;
this.__dataInstanceProps = null;
+ this.__serializing = false;
this._initializeProperties();
}
@@ -356,9 +357,45 @@
* returned from `_typeForProperty`
*/
_attributeToProperty(attribute, value, type) {
- const property = this._propertyForAttribute(attribute);
- this[property] = this._deserializeValue(value, type ||
- this._typeForProperty(property));
+ if (!this.__serializing) {
+ const property = this._propertyForAttribute(attribute);
+ this[property] = this._deserializeValue(value, type ||
+ this._typeForProperty(property));
+ }
+ }
+
+ /**
+ * Serializes a property to its associated attribute.
+ *
+ * @suppress {invalidCasts} Closure can't figure out `this` is an element.
+ *
+ * @param {string} property Property name to reflect.
+ * @param {string=} attribute Attribute name to reflect to.
+ * @param {*=} value Property value to refect.
+ */
+ _propertyToAttribute(property, attribute, value) {
+ this.__serializing = true;
+ value = (arguments.length < 3) ? this[property] : value;
+ this._valueToNodeAttribute(/** @type {!HTMLElement} */(this), value,
+ attribute || this._attributeForProperty(property));
+ this.__serializing = false;
+ }
+
+ /**
+ * Sets a typed value to an HTML attribute on a node.
+ *
+ * If the value is `undefined`, the attribute will be removed.
+ *
+ * @param {Element} node Element to set attribute to.
+ * @param {*} value Value to serialize.
+ * @param {string} attribute Attribute name to serialize to.
+ */
+ _valueToNodeAttribute(node, value, attribute) {
+ if (!value && value !== '' && value !== 0) {
+ node.removeAttribute(attribute);
+ } else {
+ node.setAttribute(attribute, value);
+ }
}
/**
diff --git a/lib/mixins/properties-mixin.html b/lib/mixins/properties-mixin.html
index 06c6359402..298ac04e57 100644
--- a/lib/mixins/properties-mixin.html
+++ b/lib/mixins/properties-mixin.html
@@ -42,6 +42,10 @@
*/
const base = Polymer.PropertiesChanged(superClass);
+ function BooleanAttribute(value) {
+ return value !== null;
+ }
+
/**
* @polymer
* @mixinClass
@@ -51,21 +55,33 @@
*/
class Properties extends base {
+ static get BooleanAttribute() {
+ return BooleanAttribute;
+ }
+
/**
* Implements standard custom elements getter to observes the attributes
* listed in `properties`.
*/
static get observedAttributes() {
- const props = this.properties;
+ this._ensurePropertyInfo();
+ const props = this.prototype.__propertyInfo;
return props ? Object.keys(props).map(p => {
return this.prototype._attributeForProperty(p);
}) : [];
}
+ _propertyForAttribute(name) {
+ return this.__attributeInfo[name] || name;
+ }
+
+ _attributeForProperty(name) {
+ const info = this.__propertyInfo[name];
+ return info && info.attribute || name.toLowerCase();
+ }
+
static _ensureFinalized(name) {
- const proto = this.prototype;
- if (!proto.hasOwnProperty('__finalized')) {
- proto.__finalized = true;
+ if (!this.prototype.hasOwnProperty('__finalized')) {
this.finalize(name);
}
}
@@ -77,13 +93,25 @@
* @param {string} name Name of the element
*/
static finalize(name) { // eslint-disable-line no-unused-vars
- const props = this.properties;
+ this.prototype.__finalized = true;
+ this._ensurePropertyInfo();
+ const props = this.prototype.__propertyInfo;
if (props) {
- this.prototype.__propertyInfo = props;
this.createProperties(Object.keys(props));
}
}
+ static _ensurePropertyInfo() {
+ let proto = this.prototype;
+ if (!proto.hasOwnProperty('__propertyInfo')) {
+ const props = this.prototype.__propertyInfo = this.properties;
+ const attrInfo = this.prototype.__attributeInfo = {};
+ for (let prop in props) {
+ attrInfo[proto._attributeForProperty(prop)] = prop;
+ }
+ }
+ }
+
/**
* Overrides implementation in PropertiesChanged to immediately process
* any pending changes to properties and ensure that
@@ -116,8 +144,8 @@
* @protected
*/
_typeForProperty(name) {
- const props = this.__propertyInfo;
- return props && props[name];
+ const info = this.__propertyInfo[name];
+ return info.type || info;
}
/**
diff --git a/lib/mixins/property-accessors.html b/lib/mixins/property-accessors.html
index 366fa05d82..6ce056d38f 100644
--- a/lib/mixins/property-accessors.html
+++ b/lib/mixins/property-accessors.html
@@ -127,8 +127,6 @@
constructor() {
super();
- /** @type {boolean} */
- this.__serializing;
/** @type {number} */
this.__dataCounter;
}
@@ -209,44 +207,9 @@
}
/**
- * Deserializes an attribute to its associated property.
- *
- * This method calls the `_deserializeValue` method to convert the string to
- * a typed value.
- *
- * @param {string} attribute Name of attribute to deserialize.
- * @param {?string} value of the attribute.
- * @param {*=} type type to deserialize to.
- */
- _attributeToProperty(attribute, value, type) {
- // Don't deserialize back to property if currently reflecting
- if (!this.__serializing) {
- super._attributeToProperty(attribute, value, type);
- }
- }
-
- /**
- * Serializes a property to its associated attribute.
- *
- * @suppress {invalidCasts} Closure can't figure out `this` is an element.
- *
- * @param {string} property Property name to reflect.
- * @param {string=} attribute Attribute name to reflect.
- * @param {*=} value Property value to refect.
- */
- _propertyToAttribute(property, attribute, value) {
- this.__serializing = true;
- value = (arguments.length < 3) ? this[property] : value;
- this._valueToNodeAttribute(/** @type {!HTMLElement} */(this), value,
- attribute || this._attributeForProperty(property));
- this.__serializing = false;
- }
-
- /**
- * Sets a typed value to an HTML attribute on a node.
- *
- * This method calls the `_serializeValue` method to convert the typed
- * value to a string. If the `_serializeValue` method returns `undefined`,
+ * Overrides PropertiesChanged implementation to calls the
+ * `_serializeValue` method to convert the typed value to a string.
+ * If the `_serializeValue` method returns `undefined`,
* the attribute will be removed (this is the default for boolean
* type `false`).
*
@@ -255,12 +218,7 @@
* @param {string} attribute Attribute name to serialize to.
*/
_valueToNodeAttribute(node, value, attribute) {
- let str = this._serializeValue(value);
- if (str === undefined) {
- node.removeAttribute(attribute);
- } else {
- node.setAttribute(attribute, str);
- }
+ super._valueToNodeAttribute(node, this._serializeValue(value), attribute);
}
/**