diff --git a/lib/mixins/element-mixin.html b/lib/mixins/element-mixin.html
index eb41b3df47..e74ed1f1ff 100644
--- a/lib/mixins/element-mixin.html
+++ b/lib/mixins/element-mixin.html
@@ -105,41 +105,41 @@
* the list returned from `_properties`.
* This list is used in `_initializeProperties` to set property defaults.
*
- * @param {PolymerElementConstructor} klass Element class
+ * @param {PolymerElementConstructor} constructor Element class
* @return {PolymerElementProperties} Flattened properties for this class
* that have default values
* @private
*/
- function propertyDefaults(klass) {
- if (!klass.hasOwnProperty(
- JSCompiler_renameProperty('__propertyDefaults', klass))) {
- klass.__propertyDefaults = null;
- let props = klass._properties;
+ function propertyDefaults(constructor) {
+ if (!constructor.hasOwnProperty(
+ JSCompiler_renameProperty('__propertyDefaults', constructor))) {
+ constructor.__propertyDefaults = null;
+ let props = constructor._properties;
for (let p in props) {
let info = props[p];
if ('value' in info) {
- klass.__propertyDefaults = klass.__propertyDefaults || {};
- klass.__propertyDefaults[p] = info;
+ constructor.__propertyDefaults = constructor.__propertyDefaults || {};
+ constructor.__propertyDefaults[p] = info;
}
}
}
- return klass.__propertyDefaults;
+ return constructor.__propertyDefaults;
}
/**
* Returns a memoized version of the the `observers` array.
- * @param {PolymerElementConstructor} klass Element class
+ * @param {PolymerElementConstructor} constructor Element class
* @return {Array} Array containing own observers for the given class
* @protected
*/
- function ownObservers(klass) {
- if (!klass.hasOwnProperty(
- JSCompiler_renameProperty('__ownObservers', klass))) {
- klass.__ownObservers =
- klass.hasOwnProperty(JSCompiler_renameProperty('observers', klass)) ?
- /** @type {PolymerElementConstructor} */ (klass).observers : null;
+ function ownObservers(constructor) {
+ if (!constructor.hasOwnProperty(
+ JSCompiler_renameProperty('__ownObservers', constructor))) {
+ constructor.__ownObservers =
+ constructor.hasOwnProperty(JSCompiler_renameProperty('observers', constructor)) ?
+ /** @type {PolymerElementConstructor} */ (constructor).observers : null;
}
- return klass.__ownObservers;
+ return constructor.__ownObservers;
}
/**
@@ -561,7 +561,7 @@
*/
attributeChangedCallback(name, old, value) {
if (old !== value) {
- let property = this._propertyNameForAttribute(name);
+ let property = this.constructor.propertyNameForAttribute(name);
if (!this._hasReadOnlyEffect(property)) {
super.attributeChangedCallback(name, old, value);
}
diff --git a/lib/mixins/properties-changed.html b/lib/mixins/properties-changed.html
index b9a49b6154..209cac5a25 100644
--- a/lib/mixins/properties-changed.html
+++ b/lib/mixins/properties-changed.html
@@ -68,6 +68,39 @@
}
}
+ /**
+ * Returns a property name that corresponds to the given attribute.
+ * By default, converts dash to camel case, e.g. `foo-bar` to `fooBar`.
+ * @param {string} attribute Attribute to convert
+ * @return {string} Property name corresponding to the given attribute.
+ *
+ * @protected
+ */
+ static propertyNameForAttribute(attribute) {
+ return attribute;
+ }
+
+ /**
+ * Returns an attribute name that corresponds to the given property.
+ * By default, converts camel to dash case, e.g. `fooBar` to `foo-bar`.
+ * @param {string} property Property to convert
+ * @return {string} Attribute name corresponding to the given property.
+ *
+ * @protected
+ */
+ static attributeNameForProperty(property) {
+ return property.toLowerCase();
+ }
+
+ /**
+ * Override point to provide a type to which to deserialize a value to
+ * a given property.
+ * @param {string} name Name of property
+ *
+ * @protected
+ */
+ static typeForProperty(name) { } //eslint-disable-line no-unused-vars
+
/**
* Creates a setter/getter pair for the named property with its own
* local storage. The getter returns the value in the local storage,
@@ -355,13 +388,13 @@
* @param {string} attribute Name of attribute to deserialize.
* @param {?string} value of the attribute.
* @param {*=} type type to deserialize to, defaults to the value
- * returned from `_typeForProperty`
+ * returned from `typeForProperty`
*/
_attributeToProperty(attribute, value, type) {
if (!this.__serializing) {
- const property = this._propertyNameForAttribute(attribute);
+ const property = this.constructor.propertyNameForAttribute(attribute);
this[property] = this._deserializeValue(value, type ||
- this._typeForProperty(property));
+ this.constructor.typeForProperty(property));
}
}
@@ -378,7 +411,7 @@
this.__serializing = true;
value = (arguments.length < 3) ? this[property] : value;
this._valueToNodeAttribute(/** @type {!HTMLElement} */(this), value,
- attribute || this._attributeNameForProperty(property));
+ attribute || this.constructor.attributeNameForProperty(property));
this.__serializing = false;
}
@@ -447,39 +480,6 @@
}
}
- /**
- * Returns a property name that corresponds to the given attribute.
- * By default, converts dash to camel case, e.g. `foo-bar` to `fooBar`.
- * @param {string} attribute Attribute to convert
- * @return {string} Property name corresponding to the given attribute.
- *
- * @protected
- */
- _propertyNameForAttribute(attribute) {
- return attribute;
- }
-
- /**
- * Returns an attribute name that corresponds to the given property.
- * By default, converts camel to dash case, e.g. `fooBar` to `foo-bar`.
- * @param {string} property Property to convert
- * @return {string} Attribute name corresponding to the given property.
- *
- * @protected
- */
- _attributeNameForProperty(property) {
- return property.toLowerCase();
- }
-
- /**
- * Override point to provide a type to which to deserialize a value to
- * a given property.
- * @param {string} name Name of property
- *
- * @protected
- */
- _typeForProperty(name) { } //eslint-disable-line no-unused-vars
-
}
return PropertiesChanged;
diff --git a/lib/mixins/properties-mixin.html b/lib/mixins/properties-mixin.html
index a20325926a..d56332e33a 100644
--- a/lib/mixins/properties-mixin.html
+++ b/lib/mixins/properties-mixin.html
@@ -67,11 +67,11 @@
* Returns the super class constructor for the given class, if it is an
* instance of the PropertiesClass.
*
- * @param {PropertiesClassConstructor} ctor PropertiesClass constructor
+ * @param {PropertiesClassConstructor} constructor PropertiesClass constructor
* @return {PropertiesClassConstructor} Super class constructor
*/
- function superForClass(ctor) {
- const proto = /** @type {PropertiesClassConstructor} */ (ctor).prototype;
+ function superForClass(constructor) {
+ const proto = /** @type {PropertiesClassConstructor} */ (constructor).prototype;
const superCtor = Object.getPrototypeOf(proto).constructor;
if (superCtor.prototype instanceof PropertiesClass) {
return superCtor;
@@ -83,31 +83,39 @@
* given class. Properties not in object format are converted to at
* least {type}.
*
- * @param {PropertiesClassConstructor} ctor PropertiesClass constructor
+ * @param {PropertiesClassConstructor} constructor PropertiesClass constructor
* @return {Object} Memoized properties object
*/
- function ownProperties(ctor) {
- if (!ctor.hasOwnProperty(
- JSCompiler_renameProperty('__ownProperties', ctor))) {
- const props = ctor.properties;
- ctor.__ownProperties = props ? normalizeProperties(props) : null;
+ function ownProperties(constructor) {
+ if (!constructor.hasOwnProperty(
+ JSCompiler_renameProperty('__ownProperties', constructor))) {
+ const props = constructor.properties;
+ constructor.__ownProperties = props ? normalizeProperties(props) : null;
}
- return ctor.__ownProperties;
+ return constructor.__ownProperties;
}
/**
- * Returns map of properties for attributes for the given class.
+ * Creates a map matching attribute names to properties by using
+ * `attributeNameForProperty` and the names in `properties`. This map is
+ * used in `propertyNameForAttribute` which locks property names and
+ * attribute names together using this data.
*
- * @param {PropertiesClassConstructor} ctor PropertiesClass constructor
- * @param {string} nane Name of attribute
- * @return {string} Name of property.
+ * @private
+ * @param {PropertiesClassConstructor} constructor PropertiesClass constructor
+ * @return {Object} Object mapping property names to attributes.
*/
- function propertyNameForAttributeMap(ctor) {
- if (!ctor.hasOwnProperty(
- JSCompiler_renameProperty('__propertyNameForAttributeMap', ctor))) {
- ctor.__propertyNameForAttributeMap = {};
+ function propertyNameForAttributeMap(constructor) {
+ if (!constructor.hasOwnProperty(JSCompiler_renameProperty(
+ '__propertyNameForAttributeMap', constructor))) {
+ const map = constructor.__propertyNameForAttributeMap = {};
+ const props = constructor._properties;
+ for (let prop in props) {
+ const attr = constructor.attributeNameForProperty(prop);
+ map[attr] = prop;
+ }
}
- return ctor.__propertyNameForAttributeMap;
+ return constructor.__propertyNameForAttributeMap;
}
/**
@@ -126,7 +134,7 @@
static get observedAttributes() {
const props = this._properties;
return props ? Object.keys(props).map(p => {
- return this.prototype._attributeNameForProperty(p);
+ return this.attributeNameForProperty(p);
}) : [];
}
@@ -180,16 +188,16 @@
}
/**
- * Overrides PropertiesChanged implementation to provide caching.
+ * Overrides PropertiesChanged implementation to discover the property
+ * associated with an attribute by revers mapping the property names in
+ * the `properties` object to the attribute name generated via
+ * `attributeNameForProperty`.
+ *
* @param {string} name Name of property
* @return {string} Name of attribute
*/
- _propertyNameForAttribute(name) {
- const cache = propertyNameForAttributeMap(this.constructor);
- if (!cache[name]) {
- cache[name] = super._propertyNameForAttribute(name);
- }
- return cache[name];
+ static propertyNameForAttribute(name) {
+ return propertyNameForAttributeMap(this)[name];
}
/**
@@ -200,8 +208,8 @@
*
* @protected
*/
- _typeForProperty(name) {
- const info = this.constructor._properties[name];
+ static typeForProperty(name) {
+ const info = this._properties[name];
return info && info.type;
}
diff --git a/lib/mixins/property-accessors.html b/lib/mixins/property-accessors.html
index d5532d7f9b..7cce3d699b 100644
--- a/lib/mixins/property-accessors.html
+++ b/lib/mixins/property-accessors.html
@@ -133,7 +133,7 @@
*
* @protected
*/
- _propertyNameForAttribute(attribute) {
+ static propertyNameForAttribute(attribute) {
return caseMap.dashToCamelCase(attribute);
}
@@ -145,7 +145,7 @@
*
* @protected
*/
- _attributeNameForProperty(property) {
+ static attributeNameForProperty(property) {
return caseMap.camelToDashCase(property);
}
diff --git a/lib/mixins/property-effects.html b/lib/mixins/property-effects.html
index 842114e7a5..324674c281 100644
--- a/lib/mixins/property-effects.html
+++ b/lib/mixins/property-effects.html
@@ -2064,7 +2064,7 @@
* @protected
*/
_createReflectedProperty(property) {
- let attr = this._attributeNameForProperty(property);
+ let attr = this.constructor.attributeNameForProperty(property);
if (attr[0] === '-') {
console.warn('Property ' + property + ' cannot be reflected to attribute ' +
attr + ' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.');
diff --git a/test/unit/polymer.properties-element.html b/test/unit/polymer.properties-element.html
index c0c21de587..b53c836148 100644
--- a/test/unit/polymer.properties-element.html
+++ b/test/unit/polymer.properties-element.html
@@ -26,7 +26,8 @@
return {
prop: String,
noStomp: String,
- id: String
+ id: String,
+ camelCase: String
};
}
@@ -196,7 +197,7 @@
-
+
@@ -262,7 +263,8 @@
test('attributes', function() {
const fixtureEl = fixture('my-element-attr');
assert.equal(fixtureEl.prop, 'attr');
- assert.equal(fixtureEl._callAttributeChangedCallback, 1);
+ assert.equal(fixtureEl.camelCase, 'camelCase');
+ assert.equal(fixtureEl._callAttributeChangedCallback, 2);
fixtureEl.removeAttribute('prop');
assert.equal(fixtureEl.prop, null);
});