From 844dcfd0c04091e9a5fa16f25befa8075c7b2e50 Mon Sep 17 00:00:00 2001 From: christophe-g Date: Sat, 17 Jun 2017 19:13:13 +0200 Subject: [PATCH 1/5] added exists attribute, reflecting whether we have data at a given path. An "empty-result" event is fired by firebase-document or firebase-query to notify that the path does not hold any data. --- firebase-database-behavior.html | 23 ++++++++++++++++++++++- firebase-document.html | 8 +++++++- firebase-query.html | 9 ++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/firebase-database-behavior.html b/firebase-database-behavior.html index 0cf7108..6ccdcd9 100644 --- a/firebase-database-behavior.html +++ b/firebase-database-behavior.html @@ -14,6 +14,12 @@ (function() { 'use strict'; + /** + * the element is notified that the path does not hold any data (fired by firebase-document or firebase query) + * + * @event empty-result + */ + /** @polymerBehavior Polymer.FirebaseDatabaseBehavior */ Polymer.FirebaseDatabaseBehaviorImpl = { properties: { @@ -46,7 +52,20 @@ disabled: { type: Boolean, value: false - } + }, + + /** + * `exists` is set to `true` when the data actually exists for the specified path; `false` otherwise. + * When we are unable to determine whether data exists or not (e.g. first round trip to the server not yet performed) the value is `null` + */ + exists: { + type: Boolean, + notify: true, + value: null, + readOnly: true, + reflectToAttribute: true + }, + }, observers: [ @@ -95,6 +114,8 @@ }, __pathChanged: function(path, oldPath) { + this._setExists(null); + if (!this.disabled && !this.valueIsEmpty(this.data)) { this.syncToMemory(function() { this.data = this.zeroValue; diff --git a/firebase-document.html b/firebase-document.html index 039e712..e8f6fa2 100644 --- a/firebase-document.html +++ b/firebase-document.html @@ -160,11 +160,13 @@ __onFirebaseValue: function(snapshot) { var value = snapshot.val(); + var exists = true; if (value == null) { value = this.zeroValue; this.__needSetData = true; - } + exists = false; + } if (!this.isNew) { this.async(function() { @@ -188,6 +190,10 @@ } } }); + this._setExists(exists); + if(!exists) { + this.fire('empty-result'); + } }); } } diff --git a/firebase-query.html b/firebase-query.html index 41246bb..64286d2 100644 --- a/firebase-query.html +++ b/firebase-query.html @@ -287,6 +287,7 @@ this.syncToMemory(function() { this.set('data', this.zeroValue); + this._setExists(null); }); } @@ -298,7 +299,8 @@ query.off('child_moved', this.__onFirebaseChildMoved, this); } - this._onOnce = true; + this._setExists(null); + this._onOnce = true; query.on('child_added', this.__onFirebaseChildAdded, this.__onError, this); query.on('child_removed', this.__onFirebaseChildRemoved, this.__onError, this); query.on('child_changed', this.__onFirebaseChildChanged, this.__onError, this); @@ -327,6 +329,7 @@ value = this.__snapshotToValue(snapshot); this.__map[key] = value; + this._setExists(true); this.splice('data', previousChildIndex + 1, 0, value); }, @@ -342,6 +345,10 @@ this.syncToMemory(function() { this.splice('data', this.__indexFromKey(key), 1); }); + if (this.data.length === 0) { + this._setExists(false); + this.fire('empty-result'); + } }); } }, From f6b0b14247f56ebfaf7e768ad25acd92101a7e53 Mon Sep 17 00:00:00 2001 From: christophe geiser Date: Sat, 13 Jan 2018 12:13:20 +0100 Subject: [PATCH 2/5] integrate @merlinnot comments --- firebase-database-behavior.html | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/firebase-database-behavior.html b/firebase-database-behavior.html index 6ccdcd9..60c38af 100644 --- a/firebase-database-behavior.html +++ b/firebase-database-behavior.html @@ -14,12 +14,6 @@ (function() { 'use strict'; - /** - * the element is notified that the path does not hold any data (fired by firebase-document or firebase query) - * - * @event empty-result - */ - /** @polymerBehavior Polymer.FirebaseDatabaseBehavior */ Polymer.FirebaseDatabaseBehaviorImpl = { properties: { @@ -55,8 +49,11 @@ }, /** - * `exists` is set to `true` when the data actually exists for the specified path; `false` otherwise. - * When we are unable to determine whether data exists or not (e.g. first round trip to the server not yet performed) the value is `null` + * `exists` is set to `true` when the data actually exists for the + * specified path; `false` otherwise. + * When we are unable to determine whether data exists or not + * (e.g. first round trip to the server not yet performed) the value + * is `null` */ exists: { type: Boolean, From 58ed184931dd7afdb39172d453d1236889840ee0 Mon Sep 17 00:00:00 2001 From: christophe geiser Date: Sat, 13 Jan 2018 12:15:56 +0100 Subject: [PATCH 3/5] integrate @merlinnot comment --- firebase-query.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firebase-query.html b/firebase-query.html index 9b6f94e..4b31e6b 100644 --- a/firebase-query.html +++ b/firebase-query.html @@ -300,7 +300,8 @@ query.off('child_changed', this.__onFirebaseChildChanged, this); query.off('child_moved', this.__onFirebaseChildMoved, this); } - + + this._setExists(null); this._onOnce = true; /* We want the value callback to batch load the initial data, @@ -312,8 +313,7 @@ * the callback if the query changes */ query.on('value', this.__onFirebaseValue, this.__onError, this); - this._setExists(null); - + query.on('child_added', this.__onFirebaseChildAdded, this.__onError, this); query.on('child_removed', this.__onFirebaseChildRemoved, this.__onError, this); query.on('child_changed', this.__onFirebaseChildChanged, this.__onError, this); From c8e301c18cfe909be91397ec84d852098aa7ee64 Mon Sep 17 00:00:00 2001 From: christophe geiser Date: Sat, 13 Jan 2018 12:21:42 +0100 Subject: [PATCH 4/5] integrate @merlinnot comments --- firebase-query.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/firebase-query.html b/firebase-query.html index 4b31e6b..1c00526 100644 --- a/firebase-query.html +++ b/firebase-query.html @@ -354,7 +354,6 @@ var key = snapshot.key; if (this.__initialLoadDone) { - var value = snapshot.val(); var previousChildIndex = this.__indexFromKey(previousChildKey); @@ -363,8 +362,8 @@ value = this.__snapshotToValue(snapshot); this.__map[key] = value; - this._setExists(true); this.splice('data', previousChildIndex + 1, 0, value); + this._setExists(true); } }, @@ -382,7 +381,6 @@ }); if (this.data.length === 0) { this._setExists(false); - this.fire('empty-result'); } }); } From 7290611c0da89652f12f209768a97128d6ee9916 Mon Sep 17 00:00:00 2001 From: christophe-g Date: Sat, 13 Jan 2018 14:01:02 +0100 Subject: [PATCH 5/5] added tests --- firebase-query.html | 1 + test/firebase-document.html | 53 +++++++++++++++++++++++++++++++++++++ test/firebase-query.html | 17 ++++++++++++ 3 files changed, 71 insertions(+) diff --git a/firebase-query.html b/firebase-query.html index 117cf4a..466d0d3 100644 --- a/firebase-query.html +++ b/firebase-query.html @@ -340,6 +340,7 @@ }.bind(this)) this.set('data', data); + this._setExists(true); } const query = this.query diff --git a/test/firebase-document.html b/test/firebase-document.html index b97adcf..3fb00e8 100644 --- a/test/firebase-document.html +++ b/test/firebase-document.html @@ -65,6 +65,59 @@ }); } }); + + function pushFirebaseValue(path, value) { + return firebase.app('test').database().ref(path).push(value); + } + + function clearFirebaseValue(path) { + return firebase.app('test').database().ref(path).set(null); + } + + var makeObject; + var root; + + setup(function() { + var objectId = 0; + makeObject = function(value) { + return { + val: value || objectId++ + }; + }; + + return pushFirebaseValue('/test', { ignore: 'me' }).then(function(snapshot) { + root = '/test/' + snapshot.key; + }); + }); + + suite('exists attribute', function() { + var query; + + setup(function() { + query = fixture('BasicStorage'); + query.path = root + '/list'; + return query.transactionsComplete; + }); + + test('exists is null when we change the path', function() { + query.path = '/myNewPath'; + expect(query.exists).to.be.equal(null); + }); + + test('exists is true when we have data false when we remove it.', function() { + var object = makeObject(); + + return pushFirebaseValue(query.path, object).then(function() { + expect(query.exists).to.be.equal(true); + }).then(function(){ + clearFirebaseValue(query.path); + }).then(function() { + expect(query.exists).to.be.equal(false); + }); + }); + + }); + }); diff --git a/test/firebase-query.html b/test/firebase-query.html index 777e7b5..fb9e059 100644 --- a/test/firebase-query.html +++ b/test/firebase-query.html @@ -176,6 +176,7 @@ var object = makeObject(); return pushFirebaseValue(query.path, object).then(function() { + expect(query.exists).to.be.equal(true); expect(query.data.length).to.be.equal(1); expect(query.data[0]).to.be.ok; expect(query.data[0].val).to.be.equal(object.val); @@ -241,6 +242,22 @@ expect(query.data[0].foo).to.be.eql(undefined); }); }); + + test('exists is null when template is stamped', function() { + expect(query.exists).to.be.equal(null); + }) + + test('exists is true when we have data false when we remove it.', function() { + var object = makeObject(); + + return pushFirebaseValue(query.path, object).then(function() { + expect(query.exists).to.be.equal(true); + }).then(function(){ + clearFirebaseValue(query.path); + }).then(function() { + expect(query.exists).to.be.equal(false); + }); + }); }); suite('querying against leaf node collections', function() {