Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Unified Diff: lib/src/firebase-element/firebase-collection.html

Issue 1418513006: update elements and fix some bugs (Closed) Base URL: git@github.com:dart-lang/polymer_elements.git@master
Patch Set: code review updates Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/firebase-element/firebase-auth.html ('k') | lib/src/firebase-element/firebase-document.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/firebase-element/firebase-collection.html
diff --git a/lib/src/firebase-element/firebase-collection.html b/lib/src/firebase-element/firebase-collection.html
index 64de6f60c413bcff1bf86291a829b080a05765fe..612fc837d2e3b2bd03f5d47b098982176d37792a 100644
--- a/lib/src/firebase-element/firebase-collection.html
+++ b/lib/src/firebase-element/firebase-collection.html
@@ -233,6 +233,11 @@ manipulation of the collection without direct knowledge of Firebase key values.
'firebase-child-moved': '_onFirebaseChildMoved',
},
+ created: function() {
+ this._pendingSplices = [];
+ this._lastLocallyAddedIndex = null;
+ },
+
/**
* Add an item to the document referenced at `location`. A key associated
* with the item will be created by Firebase, and can be accessed via the
@@ -261,7 +266,10 @@ manipulation of the collection without direct knowledge of Firebase key values.
*/
remove: function(data) {
if (data == null || data.__firebaseKey__ == null) {
- this._error('Failed to remove unknown value:', data);
+ // NOTE(cdata): This might be better as an error message in the
+ // console, but `Polymer.Base._error` throws, which we don't want
+ // to happen in this case.
+ this._warn('Refusing to remove unknown value:', data);
return;
}
@@ -287,49 +295,82 @@ manipulation of the collection without direct knowledge of Firebase key values.
* located at `location`.
*/
removeByKey: function(key) {
+ if (!this.query) {
+ this._error('Cannot remove items before query has been initialized.');
+ return;
+ }
+
this.query.ref().child(key).remove();
},
- _applyLocalDataChanges: function(change) {
+ _localDataChanged: function(change) {
var pathParts = change.path.split('.');
- var firebaseKey;
- var key;
- var value;
+ // We don't care about self-changes, and we don't respond directly to
+ // length changes:
if (pathParts.length < 2 || pathParts[1] === 'length') {
return;
}
- if (pathParts[1] === 'splices') {
- this._applySplicesToRemoteData(change.value.indexSplices);
+ // Handle splices via the adoption process. `indexSplices` is known to
+ // sometimes be null, so guard against that.
+ if (pathParts[1] === 'splices' && change.value.indexSplices != null) {
+ this._adoptSplices(change.value.indexSplices);
return;
}
- key = pathParts[1];
- value = Polymer.Collection.get(change.base).getItem(key);
+ // Otherwise, the change is happening to a sub-path of the array.
+ this._applySubPathChange(change);
+ },
+
+ _applySubPathChange: function(change) {
+ var key = change.path.split('.')[1];
+ var value = Polymer.Collection.get(change.base).getItem(key);
+ var firebaseKey = value.__firebaseKey__;
- // Temporarily remove the client-only `__firebaseKey__` property:
- firebaseKey = value.__firebaseKey__;
+ // We don't want to accidentally reflect `__firebaseKey__` in the
+ // remote data, so we remove it temporarily. `null` values will be
+ // discarded by Firebase, so `delete` is not necessary:
value.__firebaseKey__ = null;
-
this.query.ref().child(firebaseKey).set(value);
-
value.__firebaseKey__ = firebaseKey;
},
- _applySplicesToRemoteData: function(splices) {
- this._log('splices', splices);
- splices.forEach(function(splice) {
- var added = splice.object.slice(splice.index, splice.index + splice.addedCount);
+ _adoptSplices: function(splices) {
+ this._pendingSplices = this._pendingSplices.concat(splices);
- splice.removed.forEach(function(removed) {
- this.remove(removed);
+ // We can afford apply removals synchronously, so we do that first
+ // and save the `added` operations for the `debounce` below.
+ this._applyLocalDataChange(function() {
+ splices.forEach(function(splice) {
+ splice.removed.forEach(function(removed) {
+ this.remove(removed);
+ }, this);
}, this);
+ });
- added.forEach(function(added) {
- this.add(added);
- }, this);
- }, this);
+ // We async until the next turn. The reason we want to do this is
+ // that splicing within a splice handler will break the data binding
+ // system in some places. This is referred to as the "re-entrancy"
+ // problem. See polymer/polymer#2491.
+ this.debounce('_adoptSplices', function() {
+ this._applyLocalDataChange(function() {
+ var splices = this._pendingSplices;
+
+ this._pendingSplices = [];
+
+ splices.forEach(function(splice) {
+ var added = splice.object.slice(splice.index, splice.index + splice.addedCount);
+
+ added.forEach(function(added, index) {
+ this._lastLocallyAddedIndex = splice.index + index;
+ this.add(added);
+ }, this);
+ }, this);
+
+ this._lastLocallyAddedIndex = null;
+ });
+ });
},
_computeQuery: function(location, limitToFirst, limitToLast, orderByMethodName, startAt, endAt, equalTo) {
@@ -339,6 +380,8 @@ manipulation of the collection without direct knowledge of Firebase key values.
return;
}
+ this._log('Recomputing query.', arguments);
+
query = new Firebase(location);
if (orderByMethodName) {
@@ -460,17 +503,56 @@ manipulation of the collection without direct knowledge of Firebase key values.
_onFirebaseChildAdded: function(event) {
this._applyRemoteDataChange(function() {
var value = this._valueFromSnapshot(event.detail.childSnapshot);
+ var key = value.__firebaseKey__;
var previousValueKey = event.detail.previousChildName;
var index = previousValueKey != null ?
this.data.indexOf(this._valueMap[previousValueKey]) + 1 : 0;
+ var lastLocallyAddedValue;
this._valueMap[value.__firebaseKey__] = value;
- this.splice('data', index, 0, value);
+ // NOTE(cdata): The rationale for this conditional dance around the
+ // last locally added index (since you will inevitably be wondering
+ // why we do it):
+ // There may be a "locally" added value which was spliced in. If
+ // there is, it may be in the "wrong" place in the array. This is
+ // due to the fact that Firebase may be applying a sort to the
+ // data, so we won't know the correct index for the locally added
+ // value until the `child_added` event is fired.
+ // Once we get the `child_added` event, we can check to see if the
+ // locally added value is in the right place. If it is, we just
+ // `set` it to the Firebase-provided value. If it is not, then
+ // we grab the original value, splice in the Firebase-provided
+ // value in the right place, and then (importantly: at the end)
+ // find the locally-added value again (since its index may have
+ // changed by splicing-in Firebase's value) and splice it out of the
+ // array.
+ if (this._lastLocallyAddedIndex === index) {
+ this.set(['data', index], value);
+ } else {
+ if (this._lastLocallyAddedIndex != null) {
+ lastLocallyAddedValue = this.data[this._lastLocallyAddedIndex];
+ }
+
+ this.splice('data', index, 0, value);
+
+ if (this._lastLocallyAddedIndex != null) {
+ this.splice('data', this.data.indexOf(lastLocallyAddedValue), 1);
+ }
+ }
});
},
_onFirebaseChildRemoved: function(event) {
+ if (this._receivingLocalChanges) {
+ this._valueMap[event.detail.oldChildSnapshot.key()] = null;
+ // NOTE(cdata): If we are receiving local changes, that means that
+ // the splices have already been performed and items are already
+ // removed from the local data representation. No need to remove
+ // them again.
+ return;
+ }
+
this._applyRemoteDataChange(function() {
var key = event.detail.oldChildSnapshot.key();
var value = this._valueMap[key];
« no previous file with comments | « lib/src/firebase-element/firebase-auth.html ('k') | lib/src/firebase-element/firebase-document.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698