i am new in Vue JS and in Firebase. My target is get all 'eventos' that has same category. I mean, let's i have two eventos, an eventos category "SMIX" and another has "DAM". Now i want to get the eventos has category 'SMIX'
My data structure is here :
created() {
var datos = []
firebase.database().ref('usuarios').on("value", data => {
data.forEach(function(user){
user.child("eventos").orderByChild("categoria").equalTo("SMIX")
.forEach(function(evento){
datos.push(evento.val())
});
});
this.eventos = datos;
});
}[My data Structure][1]
There are several errors and points to be noted in your code:
Firstly, if you receive the error user.child(...).orderByChild is not a function
it is because with data.forEach(function(user) {...}), user is a DataSnapshot (see the forEach() doc), and by calling the child() method on this DataSnapshot you get another DataSnapshot... which does not have a orderByChild() method.
The orderByChild() method is a method of a Reference, so you need to do
user.child(...).ref.orderByChild()
using the ref property of the DataSnapshot
Secondly, you cannot do
user.ref.child("eventos").orderByChild("categoria").equalTo("SMIX")
.forEach()
because you need to use the once() or on() methods to get the data at a database location represented by a Reference.
Thirdly, Since you are going to execute several queries within a loop, you need to use the once() method instead of the on() method. The on() method set a listener that continuously "listens for data changes at a particular location."
Finally, note that you need to use Promise.all() to manage the parallel asynchronous queries to the database.
So, having noted all the points above, the following code will do the trick (to put in created()):
var datos = []
firebase.database().ref('usuarios').once('value')
.then(dataSnapshot => {
var promises = [];
dataSnapshot.forEach(user => {
promises.push(user.ref.child("eventos").orderByChild("categoria").equalTo("SMIX").once('value'));
});
return Promise.all(promises);
})
.then(results => {
//console.log(results);
results.forEach(dataSnapshot => {
dataSnapshot.forEach(evento => {
datos.push(evento.val());
});
});
this.eventos = datos;
});
I've been looking at the documentation for Synchronized Arrays https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-extending-the-services and https://www.firebase.com/docs/web/libraries/angular/guide/extending-services.html#section-firebasearray
I'm using Firebase version 2.2.7 and AngularFire version 1.1.2
Using the code below, I'm having trouble recognizing $$removed events.
.factory("ExtendedCourseList", ["$firebaseArray", function($firebaseArray) {
// create a new service based on $firebaseArray
var ExtendedCourseList= $firebaseArray.$extend({
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
console.log("new course");
return course;
},
$$removed: function(snap){
console.log("removed");
return true;
}
});
return function(listRef) {
return new ExtendedCourseList(listRef);
}
}])
.factory("CompanyRefObj", function(CompanyRef) {
//CompanyRef is a constant containing the url string
var ref = new Firebase(CompanyRef);
return ref;
})
.factory('CourseList', function (localstorage,$rootScope,ExtendedCourseList,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = ExtendedCourseList(CompanyRefObj.child(companyID).child("courses"));
)
If I run this code, only the $$added events will be triggered. To simulate the remove events I use the web-interface at Firebase to display data, where I press the remove button and accept the data being deleted permanently.
Additionally, if I delete the $$removed function, the extended service still won't synchronize when a record is deleted.
If I modify my code to use the $firebaseArray instead of extending the service (as seen above) both add and remove events will be recognized.
.factory('CourseList', function (localstorage,$rootScope,$firebaseArray,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = $firebaseArray(CompanyRefObj.child(companyID).child("courses"));
)
Finally, are there any bad practices I've missed that can cause some of the extended functions to not work?
Solved
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
//Modified below
course.$id = course_key;
//End of modification
console.log("new course");
return course;
}
After posting about the issue at firebase/angularfire github I received an answer that solved my issue. When $$added got overridden by the code provided, the $firebaseArray also lost its internal record $id.
Adding this line of code: course.$id = course_key; before returning the course, made AngularFire recognize when the record was removed from the server.
I'd like to pull data from the Firebase database in order to pass into crossfilter. However, I'm having difficulty understanding the structure output data from Firebase needs to be in in order to be an argument for crossfilter.
The following is the structure of the data that is outputted from Firebase:
[object Object](undefined) = {"-JStYZoJ7PWK1gM4n1M6":{"FID":"quake.2013p618454","agency":"WEL(GNS_Primary)","depth":"24.5703","latitude":"-41.5396","longitude":"174.1242","magnitude":"1.7345","magnitudetype":"M","origin_geom":"POINT (174.12425 -41.539614)","origintime":"2013-08-17T19:52:50.074","phases":"17","publicid":"2013p618454","status":"automatic","type":"","updatetime":"2013-08-17T19:54:11.27"},
"-JStYZsd6j4Cm6GZtrrD":{"FID":"quake.2013p618440","agency":"WEL(GNS_Primary)","depth":"26.3281","latitude":"-38.8725","longitude":"175.9561","magnitude":"2.6901","magnitudetype":"M","origin_geom":"POINT (175.95611 -38.872468)","origintime":"2013-08-17T19:45:25.076","phases":"13","publicid":"2013p618440","status":"automatic","type":"","updatetime":"2013-08-17T19:48:15.374"},
"-JStYZw6bhnFhYYwe0JI":{"FID":"quake.2013p618439","agency":"WEL(GNS_Primary)","depth":"27.0312","latitude":"-41.8992","longitude":"174.3117","magnitude":"4.6968","magnitudetype":"M","origin_geom":"POINT (174.31173 -41.899212)","origintime":"2013-08-17T19:44:28.998","phases":"130","publicid":"2013p618439","status":"automatic","type":"","updatetime":"2013-08-17T19:48:39.064"},
"-JStY_-q5x78w9VkRfpw":{"FID":"quake.2013p618432","agency":"WEL(GNS_Primary)","depth":"5.9961","latitude":"-41.7495","longitude":"174.02","magnitude":"1.8642","magnitudetype":"M","origin_geom":"POINT (174.02 -41.749481)","origintime":"2013-08-17T19:40:45.348","phases":"11","publicid":"2013p618432","status":"automatic","type":"","updatetime":"2013-08-17T19:42:33.279"},
"-JStY_4cCuMKZkGfgH9P":{"FID":"quake.2013p618429","agency":"WEL(GNS_Primary)","depth":"5.9375","latitude":"-41.7008","longitude":"174.0876","magnitude":"2.1629","magnitudetype":"M","origin_geom":"POINT (174.08765 -41.700788)","origintime":"2013-08-17T19:39:16.665","phases":"19","publicid":"2013p618429","status":"automatic","type":"","updatetime":"2013-08-17T19:41:32.969"}...
which I generated with the following code
function PullFirebase() {
new Firebase('https://myfirebase.firebaseIO.com/quakes').on('value', function (snapshot) {
var Data = snapshot.val();
function printData(data) {
var f = eval(data);
console.log(data + "(" + f.length + ") = " + JSON.stringify(f).replace("[", "[\n\t").replace(/}\,/g, "},\n\t").replace("]", "\n]"));
}
printData(Data);
});
}
PullFirebase();
I receive this error when I try to put the 'Data' above from firebase into crossfilter
[object Object](undefined) = {}
It seems that I need to turn Firebase output into an object literal, because I know the following structure works when it's taken in as an argument for firebase:
"[object Object],[object Object],[object Object],[object Object],[object Object](5) = [{"FID":"quake.2013p618454","publicid":"2013p618454","origintime":"2013-08-17T19:52:50.074","longitude":"174.1242","latitude":"-41.5396","depth":25,"magnitude":"1.7345","magnitudetype":"M","status":"automatic","phases":"17","type":"","agency":"WEL(GNS_Primary)","updatetime":"2013-08-17T19:54:11.27","origin_geom":"POINT (174.12425 -41.539614)","dtg":"2013-08-17T23:52:50.000Z","lat":-41.5396,"long":174.1242,"mag":1.7},
{"FID":"quake.2013p618440","publicid":"2013p618440","origintime":"2013-08-17T19:45:25.076","longitude":"175.9561","latitude":"-38.8725","depth":26,"magnitude":"2.6901","magnitudetype":"M","status":"automatic","phases":"13","type":"","agency":"WEL(GNS_Primary)","updatetime":"2013-08-17T19:48:15.374","origin_geom":"POINT (175.95611 -38.872468)","dtg":"2013-08-17T23:45:25.000Z","lat":-38.8725,"long":175.9561,"mag":2.7},
{"FID":"quake.2013p618439","publicid":"2013p618439","origintime":"2013-08-17T19:44:28.998","longitude":"174.3117","latitude":"-41.8992","depth":27,"magnitude":"4.6968","magnitudetype":"M","status":"automatic","phases":"130","type":"","agency":"WEL(GNS_Primary)","updatetime":"2013-08-17T19:48:39.064","origin_geom":"POINT (174.31173 -41.899212)","dtg":"2013-08-17T23:44:28.000Z","lat":-41.8992,"long":174.3117,"mag":4.7},
{"FID":"quake.2013p618432","publicid":"2013p618432","origintime":"2013-08-17T19:40:45.348","longitude":"174.02","latitude":"-41.7495","depth":6,"magnitude":"1.8642","magnitudetype":"M","status":"automatic","phases":"11","type":"","agency":"WEL(GNS_Primary)","updatetime":"2013-08-17T19:42:33.279","origin_geom":"POINT (174.02 -41.749481)","dtg":"2013-08-17T23:40:45.000Z","lat":-41.7495,"long":174.02,"mag":1.9},
{"FID":"quake.2013p618429","publicid":"2013p618429","origintime":"2013-08-17T19:39:16.665","longitude":"174.0876","latitude":"-41.7008","depth":6,"magnitude":"2.1629","magnitudetype":"M","status":"automatic","phases":"19","type":"","agency":"WEL(GNS_Primary)","updatetime":"2013-08-17T19:41:32.969","origin_geom":"POINT (174.08765 -41.700788)","dtg":"2013-08-17T23:39:16.000Z","lat":-41.7008,"long":174.0876,"mag":2.2}]
A simple example, using the on('value', approach that you have in your question:
new Firebase('https://myfirebase.firebaseIO.com/quakes').on('value', function (snapshot) {
var quakes = [];
snapshot.forEach(function(childSnapshot) {
quakes.push(childSnapshot.val());
});
var filter = new crossfilter(quakes);
console.log(xf.groupAll().reduceCount().value());
var quakesByMagnitude = xf.dimension(function(q) { return q.magnitude; });
quakesByMagnitude.filterRange([1,2]); // filters the original list
console.log(xf.groupAll().reduceCount().value());
});
This fiddle shows this approach: http://jsfiddle.net/SkW6T/
Note that I provided a similar answer to your other question: How to pull only the nodes/values from firebase and not the keys?. There I also explain an alternative approach, where you monitor the quakes list for changes instead of iterating over all children on every change.
I have no issues when using implicit updates (angelFire). However I need for some of my data use explicit updating. So I implemented angelFireCollection on the exact same ref I was using previously but despite the console.log explicitly saying that the read was granted and trying it with both with the onloadcallback and without, I don't get data directly into my assigned variable AND once the callback fires I get a strange looking object that DOES contain the data but not in the form I expect. My scope variable ends up with an empty collection. Never gets populated. Here is the code:
var streamController = function ($rootScope, $scope, $log, $location, angularFireCollection, profileService) {
//Wait for firebaseLogin...
$rootScope.$watch('firebaseAuth', init);
function init() {
if ($rootScope.firebaseAuth == false) {
return
};
var refUsers = new Firebase($rootScope.FBURL+'/users/'+$rootScope.uid);
$scope.profile = angularFireCollection(refUsers, function onload(snapshot) {
console.log(snapshot)
});
};
};
myApp.gwWebApp.controller('StreamController', ['$rootScope', '$scope', '$log', '$location', 'angularFireCollection', 'profileService',
streamController]);
}());
Here is what the console.log looks like ( ie; what snapshot looks like ):
>snapshot
T {z: R, bc: J, V: function, val: function, xd: function…}
Here is the earlier message before the snapshot was returned:
Firebase Login Succeeded! fbLoginController.js:16
FIREBASE: Attempt to read /users/529ccc5d1946a93656320b0a with auth={"username":"xxxxxxx#me.com","id":"529ccc5d1946a93656320b0a"} firebase.js:76
FIREBASE: /: "auth.username == 'admin'" firebase.js:76
FIREBASE: => false firebase.js:76
FIREBASE: /users firebase.js:76
FIREBASE: /users/529ccc5d1946a93656320b0a: "auth.id == $user" firebase.js:76
FIREBASE: => true firebase.js:76
FIREBASE:
FIREBASE: Read was allowed.
and finally the desired binding that ends up with an empty array: again from the console:
$scope.profile
[]
Anyone know what I could possibly be doing wrong?? This is like 5 lines of code. Frustrating.
I have put stops in angelFireCollection factory function and can see that the data is getting added to the collection in the callbacks inside that function but my binded variable never gets updated.
UPDATE
Ok experimenting with a plnkr. It seems that angularFireCollection EXPECTS your returning a LIST of items. The snapshot returns properly if you inspect snapshot.val() it will be whatever object structure was stored in firebase. IF you use angularFireCollection it does indeed bind to the variable HOWEVER it turns a non-list object into a garbled mess and you can not access the object user the normal dot operator. This is either a bug or it is a severe limitation of angularFireCollection which will cause me to revaluate how easily I can use firebase as the backend. I can't share my plnkr because it is accessing non-public data but tomorrow if i have time I will create a public firebase with an object store and demonstrate.
Ok. So it appears that indeed angularFireCollection is MEANT to be array based. Which is fine. It would be VERY helpful if the angularFire documentation was updated to make that clear. As such it is not an implicit vs explicit update technique.
For an explicit non-array based approach I have come up with the following code. Had I not been mislead by the documentation I would have gone down this path originally.
var MainCtrl = function($scope, angularFire) {
$scope.test = {};
var _url = 'https://golfwire.firebaseio.com/tmp';
var _ref = new Firebase(_url);
var promise = angularFire(_ref, $scope, 'implicit');
promise.then ( function(data){
$scope.explicit=angular.copy($scope.implicit );
});
}
You then work locally with the 'explicit' copy and when ready just update the 'implicit' by assigning: $scope.implicit = $scope.explicit.
Here is a plnkr: http://plnkr.co/edit/bLJrL1