Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate collection. Fixes #3946 #3970

Merged
merged 21 commits into from
Sep 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Polymer 2.0 will continue to use a [shim](https://github.com/webcomponents/shady
* `<style>` inside of a `<dom-module>`, but outside of `<template>` is no longer supported
* Imperatively created custom-styles (e.g. `document.createElement('style', 'custom-style')`) are no longer supported.

### Data layer
### Data system
* An element's template is not stamped & data system not initialized (observers, bindings, etc.) until the element has been connected to the main document. This is a direct result of the V1 changes that prevent reading attributes in the constructor.
* Re-setting an object or array no longer dirty checks, meaning you can make deep changes to an object/array and just re-set it, without needing to use `set`/`notifyPath`. Although the `set` API remains and will often be the more efficient way to make changes, this change removes users of Polymer elements from needing to use this API, making it more compatible with alternate data-binding and state management libraries.
* Propagation of data through the binding system is now batched, such that multi-property computing functions and observers run once with a set of coherent changes. Single property accessors still propagate data synchronously, although there is a new `setProperties({...})` API on Polymer elements that can be used to propagate multiple values as a coherent set.
Expand All @@ -238,6 +238,7 @@ Polymer 2.0 will continue to use a [shim](https://github.com/webcomponents/shady
* Setting/changing any function used in inline template annotations will cause the binding to re-compute its value using the new function and current property values
‘notify’ events not fired when value changes as result of binding from host
* In order for a property to be deserialized from its attribute, it must be declared in the `properties` metadata object
* The `Polymer.Collection` and associated key-based path and splice notification for arrays has been eliminated. See [explanation here](https://github.com/Polymer/polymer/pull/3970#issue-178203286) for more details.

### Removed API
* `Polymer.instanceof` and `Polymer.isInstance`: no longer needed, use
Expand Down
91 changes: 60 additions & 31 deletions src/properties/batched-effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
}

function computeLinkedPaths(inst, changedProps, computedProps) {
if (inst.__dataLinkedPaths) {
const links = inst.__dataLinkedPaths;
if (links) {
let link;
for (let a in inst.__dataLinkedPaths) {
let b = inst.__dataLinkedPaths[a];
for (let a in links) {
let b = links[a];
for (let path in changedProps) {
if (Polymer.Path.isDescendant(a, path)) {
link = Polymer.Path.translate(a, b, path);
Expand All @@ -68,20 +69,40 @@
inst.__dataInterimOld = inst.__dataInterimOld ?
utils.mixin(inst.__dataInterimOld, oldProps) : oldProps;
// Notify
if (runEffects(inst, inst.PROPERTY_EFFECT_TYPES.NOTIFY, props)) {
// Flush host
let host = inst.__dataHost;
if (host && host.setProperties) {
host._flushProperties();
}
let notified;
let notifyEffects = inst[inst.PROPERTY_EFFECT_TYPES.NOTIFY];
let id = effectUid++;
// Try normal notify effects; if none, fall back to try path notification
for (let prop in props) {
if (notifyEffects && runEffectsForProperty(inst, notifyEffects, id,
prop, inst.__data[prop], oldProps && oldProps[prop])) {
notified = true;
} else if (notifyPath(inst, prop, props[prop])) {
notified = true;
}
}
// Flush host if we actually notified and host was batching
let host;
if (notified && (host = inst.__dataHost) && host.setProperties) {
host._flushProperties();
}
// Combine & return interim data only for last entry
if (runId == inst._runId) {
changedProps = inst.__dataInterim;
oldProps = inst.__dataInterimOld;
inst.__dataInterim = null;
inst.__dataInterimOld = null;
return {changedProps: changedProps, oldProps: oldProps};
return { changedProps, oldProps };
}
}

function notifyPath(inst, prop, value) {
let rootProperty = Polymer.Path.root(prop);
if (rootProperty !== prop) {
let name = Polymer.CaseMap.camelToDashCase(rootProperty) + '-changed';
let options = { detail: {value, path: prop }};
inst._dispatchNotifyingEvent(new CustomEvent(name, options));
return true;
}
}

Expand All @@ -91,20 +112,29 @@
if (effects) {
let id = effectUid++;
for (let prop in props) {
let rootProperty = Polymer.Path.root(prop);
let fxs = effects[rootProperty];
if (fxs) {
for (let i=0, l=fxs.length, fx; (i<l) && (fx=fxs[i]); i++) {
if (Polymer.Path.matches(fx.path, prop) &&
(!fx.info || fx.info.lastRun !== id)) {
fx.fn(inst, prop, inst.__data[prop],
oldProps && oldProps[prop], fx.info, inst.__dataFromAbove);
if (fx.info) {
fx.info.lastRun = id;
}
ran = true;
}
if (runEffectsForProperty(inst, effects, id, prop, inst.__data[prop],
oldProps && oldProps[prop])) {
ran = true;
}
}
}
return ran;
}

function runEffectsForProperty(inst, effects, id, prop, value, old) {
let ran;
let rootProperty = Polymer.Path.root(prop);
let fxs = effects[rootProperty];
if (fxs) {
let fromAbove = inst.__dataFromAbove;
for (let i=0, l=fxs.length, fx; (i<l) && (fx=fxs[i]); i++) {
if (Polymer.Path.matches(fx.path, prop) &&
(!fx.info || fx.info.lastRun !== id)) {
fx.fn(inst, prop, value, old, fx.info, fromAbove);
if (fx.info) {
fx.info.lastRun = id;
}
ran = true;
}
}
}
Expand All @@ -123,11 +153,10 @@
// -- set properties machinery

_propertiesChanged(currentProps, changedProps, oldProps) {
// if (window.debug) {
// let c = Object.getOwnPropertyNames(changedProps);
// console.group(inst.localName + '#' + inst.id + ': ' + c);
// debugger;
// }
// ----------------------------
// let c = Object.getOwnPropertyNames(changedProps || {});
// console.group(this.localName + '#' + this.id + ': ' + c);
// ----------------------------
// Compute
let computedProps = runComputedEffects(this, changedProps, oldProps);
// Compute linked paths
Expand All @@ -148,9 +177,9 @@
runEffects(this, this.PROPERTY_EFFECT_TYPES.OBSERVE,
changedProps, oldProps);
}
// if (window.debug) {
// console.groupEnd(inst.localName + '#' + inst.id + ': ' + c);
// }
// ----------------------------
// console.groupEnd(this.localName + '#' + this.id + ': ' + c);
// ----------------------------
}

_setPropertyToNodeFromAnnotation(node, prop, value) {
Expand Down
57 changes: 42 additions & 15 deletions src/properties/property-effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -556,18 +556,17 @@

// data api

// Note: this implemetation only accepts key-based array paths
// Note: this implemetation only accepts normalized paths
function notifySplices(inst, array, path, splices) {
let change = {
keySplices: Polymer.Collection.applySplices(array, splices),
indexSplices: splices
};
let splicesPath = path + '.splices';
inst._setProperty(splicesPath, change);
inst._setProperty(path + '.length', array.length);
// All path notification values are cached on `this.__data__`.
// Null here to allow potentially large splice records to be GC'ed.
inst.__data[splicesPath] = {keySplices: null, indexSplices: null};
inst.__data[splicesPath] = {indexSplices: null};
}

function notifySplice(inst, array, path, index, added, removed) {
Expand Down Expand Up @@ -683,17 +682,17 @@
// system, and not in every step of the hot path.
// If `path` is an unmanaged property (property without an accessor)
// or a path, sets the value at that path. If the root of the path
// is a managed property, returns an index-to-key transformed path
// is a managed property, returns a normalized string path
// sutable for setting into the system via setProperty/setPendingProperty
// `path` can be a user-facing path string or array of path parts.
_setPathOrUnmanagedProperty(path, value) {
let rootProperty = Polymer.Path.root(Array.isArray(path) ? path[0] : path);
let _hasEffect = this._hasPropertyEffect(rootProperty);
let hasEffect = this._hasPropertyEffect(rootProperty);
let isPath = (rootProperty !== path);
if (!_hasEffect || isPath) {
if (!hasEffect || isPath) {
path = Polymer.Path.set(this, path, value);
}
if (_hasEffect) {
if (hasEffect) {
return path;
}
}
Expand Down Expand Up @@ -746,6 +745,8 @@
* @param {string} from Source path to link.
*/
linkPaths(to, from) {
to = Polymer.Path.normalize(to);
from = Polymer.Path.normalize(from);
this.__dataLinkedPaths = this.__dataLinkedPaths || {};
if (from) {
this.__dataLinkedPaths[to] = from;
Expand All @@ -764,6 +765,7 @@
* @param {string} path Target path to unlink.
*/
unlinkPaths(path) {
path = Polymer.Path.normalize(path);
if (this.__dataLinkedPaths) {
delete this.__dataLinkedPaths[path];
}
Expand Down Expand Up @@ -800,7 +802,6 @@
notifySplices(path, splices) {
let info = {};
let array = Polymer.Path.get(this, path, info);
// Notify change to key-based path
notifySplices(this, array, info.path, splices);
}

Expand Down Expand Up @@ -837,19 +838,25 @@
*
* @method set
* @param {(string|Array<(string|number)>)} path Path to the value
* to write. The path may be specified as a string (e.g. `foo.bar.baz`)
* to write. The path may be specified as a string (e.g. `'foo.bar.baz'`)
* or an array of path parts (e.g. `['foo.bar', 'baz']`). Note that
* bracketed expressions are not supported; string-based path parts
* *must* be separated by dots. Note that when dereferencing array
* indices, the index may be used as a dotted part directly
* (e.g. `users.12.name` or `['users', 12, 'name']`).
* (e.g. `'users.12.name'` or `['users', 12, 'name']`).
* @param {*} value Value to set at the specified path.
* @param {Object=} root Root object from which the path is evaluated.
* When specified, no notification will occur.
*/
set(path, value) {
if (!this._hasReadOnlyEffect(path)) {
if ((path = this._setPathOrUnmanagedProperty(path, value))) {
this._setProperty(path, value);
}
set(path, value, root) {
if (root) {
Polymer.Path.set(root, path, value);
} else {
if (!this._hasReadOnlyEffect(path)) {
if ((path = this._setPathOrUnmanagedProperty(path, value))) {
this._setProperty(path, value);
}
}
}
}

Expand Down Expand Up @@ -1005,7 +1012,27 @@
return ret;
}

/**
* Notify that a path has changed.
*
* Example:
*
* this.item.user.name = 'Bob';
* this.notifyPath('item.user.name');
*
* @param {string} path Path that should be notified.
* @param {*=} value Value at the path (optional).
*/
notifyPath(path, value) {
if (arguments.length == 1) {
// Get value if not supplied
let info = {};
value = Polymer.Path.get(this, path, info);
path = info.path;
} else if (Array.isArray(path)) {
// Normalize path if needed
path = Polymer.Path.normalize(path);
}
this._setProperty(path, value);
}

Expand Down
Loading