diff --git a/firebase-database-behavior.html b/firebase-database-behavior.html
index 0cf7108..60c38af 100644
--- a/firebase-database-behavior.html
+++ b/firebase-database-behavior.html
@@ -46,7 +46,23 @@
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 +111,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 d0538b1..3bfc618 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 d503f83..466d0d3 100644
--- a/firebase-query.html
+++ b/firebase-query.html
@@ -289,6 +289,7 @@
this.syncToMemory(function() {
this.__map = {};
this.set('data', this.zeroValue);
+ this._setExists(null);
});
}
@@ -305,13 +306,14 @@
query.off('child_changed', this.__onFirebaseChildChanged, this);
query.off('child_moved', this.__onFirebaseChildMoved, this);
}
-
- this._onOnce = true;
- this._query = query
+
+ this._setExists(null);
+ this._onOnce = true;
+ this._query = query;
// does the on-value first
- query.off('value', this.__onFirebaseValue, this)
- query.on('value', this.__onFirebaseValue, this.__onError, this)
+ query.off('value', this.__onFirebaseValue, this);
+ query.on('value', this.__onFirebaseValue, this.__onError, this);
}
},
@@ -338,6 +340,7 @@
}.bind(this))
this.set('data', data);
+ this._setExists(true);
}
const query = this.query
@@ -371,6 +374,7 @@
this.__map[key] = value;
this.splice('data', previousChildIndex + 1, 0, value);
+ this._setExists(true);
},
__onFirebaseChildRemoved: function(snapshot) {
@@ -390,6 +394,9 @@
this.splice('data', this.__indexFromKey(key), 1);
}
});
+ if (this.data.length === 0) {
+ this._setExists(false);
+ }
});
}
},
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);
+ });
+ });
+
+ });
+
});