Skip to content

Commit

Permalink
Merge pull request #4510 from Polymer/2.0-binding-api-refactor
Browse files Browse the repository at this point in the history
2.0 Improvements to binding API
  • Loading branch information
Steve Orvell authored Apr 13, 2017
2 parents afa843c + fa67457 commit 9997a04
Show file tree
Hide file tree
Showing 12 changed files with 1,504 additions and 546 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ addons:
sources:
- google-chrome
packages:
- google-chrome-stable
- google-chrome-beta
cache:
directories:
- node_modules
before_script:
- mkdir -p ~/bin
- ln -s /usr/bin/google-chrome-beta ~/bin/google-chrome
- export PATH=$HOME/bin:$PATH
- npm install -g bower gulp-cli@1
- bower install
- gulp lint
Expand Down
2 changes: 1 addition & 1 deletion lib/elements/dom-bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@
observer.observe(this, {childList: true});
return;
}
this._bindTemplate(template);
this.root = this._stampTemplate(template);
this.$ = this.root.$;
this.__children = [];
for (let n=this.root.firstChild; n; n=n.nextSibling) {
this.__children[this.__children.length] = n;
Expand Down
20 changes: 18 additions & 2 deletions lib/mixins/element-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@
if (window.ShadyCSS) {
window.ShadyCSS.prepareTemplate(template, is, ext);
}
proto._bindTemplate(template, propertiesForClass(proto.constructor));
proto._bindTemplate(template);
}

function flushPropertiesStub() {}
Expand Down Expand Up @@ -576,7 +576,9 @@
let value = typeof info.value == 'function' ?
info.value.call(this) :
info.value;
if (this._hasPropertyEffect(p)) {
// Set via `_setProperty` if there is an accessor, to enable
// initializing readOnly property defaults
if (this._hasAccessor(p)) {
this._setProperty(p, value)
} else {
this[p] = value;
Expand Down Expand Up @@ -622,6 +624,7 @@
hostStack.beginHosting(this);
this.root = this._stampTemplate(this._template);
hostStack.endHosting(this);
this.$ = this.root.$;
}
super.ready();
}
Expand Down Expand Up @@ -745,6 +748,19 @@
return Polymer.ResolveUrl.resolveUrl(url, base);
}

/**
* Overrides `PropertyAccessors` to add map of dynamic functions on
* template info, for consumption by `PropertyEffects` template binding
* code. This map determines which method templates should have accessors
* created for them.
*
* @override
*/
static _parseTemplateContent(template, templateInfo, nodeInfo) {
templateInfo.dynamicFns = templateInfo.dynamicFns || propertiesForClass(this);
return super._parseTemplateContent(template, templateInfo, nodeInfo);
}

}

return PolymerElement;
Expand Down
114 changes: 97 additions & 17 deletions lib/mixins/property-accessors.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@
* the standard `static get observedAttributes()`, implement `_propertiesChanged`
* on the class, and then call `MyClass.createPropertiesForAttributes()` once
* on the class to generate property accessors for each observed attribute
* prior to instancing. Any `observedAttributes` will automatically be
* prior to instancing. Last, call `this._flushProperties()` once to enable
* the accessors.
*
* Any `observedAttributes` will automatically be
* deserialized via `attributeChangedCallback` and set to the associated
* property using `dash-case`-to-`camelCase` convention.
*
Expand Down Expand Up @@ -129,13 +132,25 @@
_initializeProperties() {
this.__serializing = false;
this.__dataCounter = 0;
this.__dataInitialized = false;
this.__dataInvalid = false;
// initialize data with prototype values saved when creating accessors
this.__data = {};
this.__dataPending = null;
this.__dataOld = null;
if (this.__dataProto) {
this._initializeProtoProperties(this.__dataProto);
this.__dataProto = null;
}
// Capture instance properties; these will be set into accessors
// during first flush. Don't set them here, since we want
// these to overwrite defaults/constructor assignments
for (let p in this.__dataHasAccessor) {
if (this.hasOwnProperty(p)) {
this.__dataInstanceProps = this.__dataInstanceProps || {};
this.__dataInstanceProps[p] = this[p];
delete this[p];
}
}
}

Expand All @@ -157,6 +172,22 @@
}
}

/**
* Called at ready time with bag of instance properties that overwrote
* accessors when the element upgraded.
*
* The default implementation sets these properties back into the
* setter at ready time. This method is provided as an override
* point for customizing or providing more efficient initialization.
*
* @param {Object} props Bag of property values that were overwritten
* when creating property accessors.
* @protected
*/
_initializeInstanceProperties(props) {
Object.assign(this, props);
}

/**
* Ensures the element has the given attribute. If it does not,
* assigns the given value to the attribute.
Expand Down Expand Up @@ -347,15 +378,31 @@
* @protected
*/
_createPropertyAccessor(property, readOnly) {
saveAccessorValue(this, property);
Object.defineProperty(this, property, {
get: function() {
return this.__data[property];
},
set: readOnly ? function() { } : function(value) {
this._setProperty(property, value);
}
});
if (!this.hasOwnProperty('__dataHasAccessor')) {
this.__dataHasAccessor = Object.assign({}, this.__dataHasAccessor);
}
if (!this.__dataHasAccessor[property]) {
this.__dataHasAccessor[property] = true;
saveAccessorValue(this, property);
Object.defineProperty(this, property, {
get: function() {
return this.__data[property];
},
set: readOnly ? function() { } : function(value) {
this._setProperty(property, value);
}
});
}
}

/**
* Returns true if this library created an accessor for the given property.
*
* @param {string} property Property name
* @return {boolean} True if an accessor was created
*/
_hasAccessor(property) {
return this.__dataHasAccessor && this.__dataHasAccessor[property];
}

/**
Expand Down Expand Up @@ -417,7 +464,7 @@
* @protected
*/
_invalidateProperties() {
if (!this.__dataInvalid) {
if (!this.__dataInvalid && this.__dataInitialized) {
this.__dataInvalid = true;
microtask.run(() => {
if (this.__dataInvalid) {
Expand All @@ -433,15 +480,48 @@
* pending changes (and old values recorded when pending changes were
* set), and resets the pending set of changes.
*
* Note that this method must be called once to enable the property
* accessors system. For elements, generally `connectedCallback`
* is a normal spot to do so.
*
* @protected
*/
_flushProperties() {
let oldProps = this.__dataOld;
let changedProps = this.__dataPending;
this.__dataPending = null;
this.__dataCounter++;
this._propertiesChanged(this.__data, changedProps, oldProps);
this.__dataCounter--;
if (!this.__dataInitialized) {
this.ready()
} else if (this.__dataPending) {
let changedProps = this.__dataPending;
this.__dataPending = null;
this.__dataCounter++;
this._propertiesChanged(this.__data, changedProps, this.__dataOld);
this.__dataCounter--;
}
}

/**
* Lifecycle callback called the first time properties are being flushed.
* Prior to `ready`, all property sets through accessors are queued and
* their effects are flushed after this method returns.
*
* Users may override this function to implement behavior that is
* dependent on the element having its properties initialized, e.g.
* from defaults (initialized from `constructor`, `_initializeProperties`),
* `attributeChangedCallback`, or values propagated from host e.g. via
* bindings. `super.ready()` must be called to ensure the data system
* becomes enabled.
*
* @public
*/
ready() {
// Update instance properties that shadowed proto accessors; these take
// priority over any defaults set in constructor or attributeChangedCallback
if (this.__dataInstanceProps) {
this._initializeInstanceProperties(this.__dataInstanceProps);
this.__dataInstanceProps = null;
}
this.__dataInitialized = true;
// Run normal flush
this._flushProperties();
}

/**
Expand Down
Loading

0 comments on commit 9997a04

Please sign in to comment.