Skip to content

Commit

Permalink
Better attribute suppport
Browse files Browse the repository at this point in the history
* Moves _propertyToAttribute to PropertiesChanged
* PropertiesMixin supports `attribute` in the `properties` object to define an attribute name.
  • Loading branch information
Steven Orvell committed Oct 6, 2017
1 parent 5ae21a0 commit c91b9d1
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 57 deletions.
43 changes: 40 additions & 3 deletions lib/mixins/properties-changed.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
this.__dataPending = null;
this.__dataOld = null;
this.__dataInstanceProps = null;
this.__serializing = false;
this._initializeProperties();
}

Expand Down Expand Up @@ -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);
}
}

/**
Expand Down
44 changes: 36 additions & 8 deletions lib/mixins/properties-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
*/
const base = Polymer.PropertiesChanged(superClass);

function BooleanAttribute(value) {
return value !== null;
}

/**
* @polymer
* @mixinClass
Expand All @@ -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);
}
}
Expand All @@ -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
Expand Down Expand Up @@ -116,8 +144,8 @@
* @protected
*/
_typeForProperty(name) {
const props = this.__propertyInfo;
return props && props[name];
const info = this.__propertyInfo[name];
return info.type || info;
}

/**
Expand Down
50 changes: 4 additions & 46 deletions lib/mixins/property-accessors.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@

constructor() {
super();
/** @type {boolean} */
this.__serializing;
/** @type {number} */
this.__dataCounter;
}
Expand Down Expand Up @@ -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`).
*
Expand All @@ -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);
}

/**
Expand Down

0 comments on commit c91b9d1

Please sign in to comment.