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

Side by Side 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, 1 month 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 unified diff | Download patch
OLDNEW
1 <!-- 1 <!--
2 @license 2 @license
3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt 4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also 7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 --> 9 -->
10 10
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 } 226 }
227 }, 227 },
228 228
229 listeners: { 229 listeners: {
230 'firebase-child-added': '_onFirebaseChildAdded', 230 'firebase-child-added': '_onFirebaseChildAdded',
231 'firebase-child-removed': '_onFirebaseChildRemoved', 231 'firebase-child-removed': '_onFirebaseChildRemoved',
232 'firebase-child-changed': '_onFirebaseChildChanged', 232 'firebase-child-changed': '_onFirebaseChildChanged',
233 'firebase-child-moved': '_onFirebaseChildMoved', 233 'firebase-child-moved': '_onFirebaseChildMoved',
234 }, 234 },
235 235
236 created: function() {
237 this._pendingSplices = [];
238 this._lastLocallyAddedIndex = null;
239 },
240
236 /** 241 /**
237 * Add an item to the document referenced at `location`. A key associated 242 * Add an item to the document referenced at `location`. A key associated
238 * with the item will be created by Firebase, and can be accessed via the 243 * with the item will be created by Firebase, and can be accessed via the
239 * Firebase Query instance returned by this method. 244 * Firebase Query instance returned by this method.
240 * 245 *
241 * @param {Object} data A value to add to the document. 246 * @param {Object} data A value to add to the document.
242 * @return {Object} A Firebase Query instance referring to the added item. 247 * @return {Object} A Firebase Query instance referring to the added item.
243 */ 248 */
244 add: function(data) { 249 add: function(data) {
245 var query; 250 var query;
246 251
247 this._log('Adding new item to collection with value:', data); 252 this._log('Adding new item to collection with value:', data);
248 253
249 query = this.query.ref().push(); 254 query = this.query.ref().push();
250 query.set(data); 255 query.set(data);
251 256
252 return query; 257 return query;
253 }, 258 },
254 259
255 /** 260 /**
256 * Remove an item from the document referenced at `location`. The item 261 * Remove an item from the document referenced at `location`. The item
257 * is assumed to be an identical reference to an item already in the 262 * is assumed to be an identical reference to an item already in the
258 * `data` Array. 263 * `data` Array.
259 * 264 *
260 * @param {Object} data An identical reference to an item in `this.data`. 265 * @param {Object} data An identical reference to an item in `this.data`.
261 */ 266 */
262 remove: function(data) { 267 remove: function(data) {
263 if (data == null || data.__firebaseKey__ == null) { 268 if (data == null || data.__firebaseKey__ == null) {
264 this._error('Failed to remove unknown value:', data); 269 // NOTE(cdata): This might be better as an error message in the
270 // console, but `Polymer.Base._error` throws, which we don't want
271 // to happen in this case.
272 this._warn('Refusing to remove unknown value:', data);
265 return; 273 return;
266 } 274 }
267 275
268 this._log('Removing collection item "' + data.__firebaseKey__ + '"', dat a.value); 276 this._log('Removing collection item "' + data.__firebaseKey__ + '"', dat a.value);
269 277
270 this.removeByKey(data.__firebaseKey__); 278 this.removeByKey(data.__firebaseKey__);
271 }, 279 },
272 280
273 /** 281 /**
274 * Look up an item in the local `data` Array by key. 282 * Look up an item in the local `data` Array by key.
275 * 283 *
276 * @param {String} key The key associated with the item in the parent 284 * @param {String} key The key associated with the item in the parent
277 * document. 285 * document.
278 */ 286 */
279 getByKey: function(key) { 287 getByKey: function(key) {
280 return this._valueMap[key]; 288 return this._valueMap[key];
281 }, 289 },
282 290
283 /** 291 /**
284 * Remove an item from the document associated with `location` by key. 292 * Remove an item from the document associated with `location` by key.
285 * 293 *
286 * @param {String} key The key associated with the item in the document 294 * @param {String} key The key associated with the item in the document
287 * located at `location`. 295 * located at `location`.
288 */ 296 */
289 removeByKey: function(key) { 297 removeByKey: function(key) {
298 if (!this.query) {
299 this._error('Cannot remove items before query has been initialized.');
300 return;
301 }
302
290 this.query.ref().child(key).remove(); 303 this.query.ref().child(key).remove();
291 }, 304 },
292 305
293 _applyLocalDataChanges: function(change) { 306 _localDataChanged: function(change) {
294 var pathParts = change.path.split('.'); 307 var pathParts = change.path.split('.');
295 var firebaseKey;
296 var key;
297 var value;
298 308
309 // We don't care about self-changes, and we don't respond directly to
310 // length changes:
299 if (pathParts.length < 2 || pathParts[1] === 'length') { 311 if (pathParts.length < 2 || pathParts[1] === 'length') {
300 return; 312 return;
301 } 313 }
302 314
303 if (pathParts[1] === 'splices') { 315 // Handle splices via the adoption process. `indexSplices` is known to
304 this._applySplicesToRemoteData(change.value.indexSplices); 316 // sometimes be null, so guard against that.
317 if (pathParts[1] === 'splices' && change.value.indexSplices != null) {
318 this._adoptSplices(change.value.indexSplices);
305 return; 319 return;
306 } 320 }
307 321
308 key = pathParts[1]; 322 // Otherwise, the change is happening to a sub-path of the array.
309 value = Polymer.Collection.get(change.base).getItem(key); 323 this._applySubPathChange(change);
324 },
310 325
311 // Temporarily remove the client-only `__firebaseKey__` property: 326 _applySubPathChange: function(change) {
312 firebaseKey = value.__firebaseKey__; 327 var key = change.path.split('.')[1];
328 var value = Polymer.Collection.get(change.base).getItem(key);
329 var firebaseKey = value.__firebaseKey__;
330
331 // We don't want to accidentally reflect `__firebaseKey__` in the
332 // remote data, so we remove it temporarily. `null` values will be
333 // discarded by Firebase, so `delete` is not necessary:
313 value.__firebaseKey__ = null; 334 value.__firebaseKey__ = null;
314
315 this.query.ref().child(firebaseKey).set(value); 335 this.query.ref().child(firebaseKey).set(value);
316
317 value.__firebaseKey__ = firebaseKey; 336 value.__firebaseKey__ = firebaseKey;
318 }, 337 },
319 338
320 _applySplicesToRemoteData: function(splices) { 339 _adoptSplices: function(splices) {
321 this._log('splices', splices); 340 this._pendingSplices = this._pendingSplices.concat(splices);
322 splices.forEach(function(splice) {
323 var added = splice.object.slice(splice.index, splice.index + splice.ad dedCount);
324 341
325 splice.removed.forEach(function(removed) { 342 // We can afford apply removals synchronously, so we do that first
326 this.remove(removed); 343 // and save the `added` operations for the `debounce` below.
344 this._applyLocalDataChange(function() {
345 splices.forEach(function(splice) {
346 splice.removed.forEach(function(removed) {
347 this.remove(removed);
348 }, this);
327 }, this); 349 }, this);
350 });
328 351
329 added.forEach(function(added) { 352 // We async until the next turn. The reason we want to do this is
330 this.add(added); 353 // that splicing within a splice handler will break the data binding
331 }, this); 354 // system in some places. This is referred to as the "re-entrancy"
332 }, this); 355 // problem. See polymer/polymer#2491.
356 this.debounce('_adoptSplices', function() {
357 this._applyLocalDataChange(function() {
358 var splices = this._pendingSplices;
359
360 this._pendingSplices = [];
361
362 splices.forEach(function(splice) {
363 var added = splice.object.slice(splice.index, splice.index + splic e.addedCount);
364
365 added.forEach(function(added, index) {
366 this._lastLocallyAddedIndex = splice.index + index;
367 this.add(added);
368 }, this);
369 }, this);
370
371 this._lastLocallyAddedIndex = null;
372 });
373 });
333 }, 374 },
334 375
335 _computeQuery: function(location, limitToFirst, limitToLast, orderByMethod Name, startAt, endAt, equalTo) { 376 _computeQuery: function(location, limitToFirst, limitToLast, orderByMethod Name, startAt, endAt, equalTo) {
336 var query; 377 var query;
337 378
338 if (!location) { 379 if (!location) {
339 return; 380 return;
340 } 381 }
341 382
383 this._log('Recomputing query.', arguments);
384
342 query = new Firebase(location); 385 query = new Firebase(location);
343 386
344 if (orderByMethodName) { 387 if (orderByMethodName) {
345 if (orderByMethodName === 'orderByChild') { 388 if (orderByMethodName === 'orderByChild') {
346 query = query[orderByMethodName](this.orderByChild); 389 query = query[orderByMethodName](this.orderByChild);
347 } else { 390 } else {
348 query = query[orderByMethodName](); 391 query = query[orderByMethodName]();
349 } 392 }
350 } 393 }
351 394
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 496
454 persistable[property] = value[property]; 497 persistable[property] = value[property];
455 } 498 }
456 499
457 return persistable; 500 return persistable;
458 }, 501 },
459 502
460 _onFirebaseChildAdded: function(event) { 503 _onFirebaseChildAdded: function(event) {
461 this._applyRemoteDataChange(function() { 504 this._applyRemoteDataChange(function() {
462 var value = this._valueFromSnapshot(event.detail.childSnapshot); 505 var value = this._valueFromSnapshot(event.detail.childSnapshot);
506 var key = value.__firebaseKey__;
463 var previousValueKey = event.detail.previousChildName; 507 var previousValueKey = event.detail.previousChildName;
464 var index = previousValueKey != null ? 508 var index = previousValueKey != null ?
465 this.data.indexOf(this._valueMap[previousValueKey]) + 1 : 0; 509 this.data.indexOf(this._valueMap[previousValueKey]) + 1 : 0;
510 var lastLocallyAddedValue;
466 511
467 this._valueMap[value.__firebaseKey__] = value; 512 this._valueMap[value.__firebaseKey__] = value;
468 513
469 this.splice('data', index, 0, value); 514 // NOTE(cdata): The rationale for this conditional dance around the
515 // last locally added index (since you will inevitably be wondering
516 // why we do it):
517 // There may be a "locally" added value which was spliced in. If
518 // there is, it may be in the "wrong" place in the array. This is
519 // due to the fact that Firebase may be applying a sort to the
520 // data, so we won't know the correct index for the locally added
521 // value until the `child_added` event is fired.
522 // Once we get the `child_added` event, we can check to see if the
523 // locally added value is in the right place. If it is, we just
524 // `set` it to the Firebase-provided value. If it is not, then
525 // we grab the original value, splice in the Firebase-provided
526 // value in the right place, and then (importantly: at the end)
527 // find the locally-added value again (since its index may have
528 // changed by splicing-in Firebase's value) and splice it out of the
529 // array.
530 if (this._lastLocallyAddedIndex === index) {
531 this.set(['data', index], value);
532 } else {
533 if (this._lastLocallyAddedIndex != null) {
534 lastLocallyAddedValue = this.data[this._lastLocallyAddedIndex];
535 }
536
537 this.splice('data', index, 0, value);
538
539 if (this._lastLocallyAddedIndex != null) {
540 this.splice('data', this.data.indexOf(lastLocallyAddedValue), 1);
541 }
542 }
470 }); 543 });
471 }, 544 },
472 545
473 _onFirebaseChildRemoved: function(event) { 546 _onFirebaseChildRemoved: function(event) {
547 if (this._receivingLocalChanges) {
548 this._valueMap[event.detail.oldChildSnapshot.key()] = null;
549 // NOTE(cdata): If we are receiving local changes, that means that
550 // the splices have already been performed and items are already
551 // removed from the local data representation. No need to remove
552 // them again.
553 return;
554 }
555
474 this._applyRemoteDataChange(function() { 556 this._applyRemoteDataChange(function() {
475 var key = event.detail.oldChildSnapshot.key(); 557 var key = event.detail.oldChildSnapshot.key();
476 var value = this._valueMap[key]; 558 var value = this._valueMap[key];
477 var index; 559 var index;
478 560
479 if (!value) { 561 if (!value) {
480 this._error('Received firebase-child-removed event for unknown child "' + key + '"'); 562 this._error('Received firebase-child-removed event for unknown child "' + key + '"');
481 return; 563 return;
482 } 564 }
483 565
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 } 615 }
534 }); 616 });
535 617
536 FirebaseCollection.ORDER_VALUE_TYPES = { 618 FirebaseCollection.ORDER_VALUE_TYPES = {
537 string: String, 619 string: String,
538 number: Number, 620 number: Number,
539 boolean: Boolean 621 boolean: Boolean
540 }; 622 };
541 })(); 623 })();
542 </script> 624 </script>
OLDNEW
« 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