Reputation: 974
I'm Trying to fetch some data from a firebase , I'm using angular firebase plugin. I double checked debugging in inspector, the url is the correct one. It responds back, that means that the url is correct but the callback's arguments is undefined.
I'm using loaded because I need it to fire once. I tried value but shame thing.
I think I exhausted all my energy on this for today so a second opinion would be great.
P.S. I really wonder why they are not using a promise instead of a callback.
var seekMatch = function(player) {
var deferred = $q.defer();
angular.forEach(matches.$getIndex(), function(matchId) {
var matchRef = firebaseRef('matches/' + matchId); // <-- double checked, the url sends me to the correct firebase record
var matchDB = $firebase(matchRef);
matchDB.$on('loaded', function(data) {
console.log(data); // <----- return's undefined
if (matchMakingFormula(data.playerA, player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
});
return deferred.promise;
};
I'm adding all the code here to give you a better idea of what I'm trying to do.
'use strict';
angular.module('angularfireApp')
.factory('FBmatchService', ['$rootScope' , '$q', '$firebase', 'firebaseRef',
function ($rootScope, $q, $firebase, firebaseRef) {
// Service logic
var matchesRef = firebaseRef( '/matches/' );
var matches = $firebase(matchesRef);
var match = null;
var matchMakingFormula = function (playerA , playerB) {
return playerA.type !== playerB.type
&& distanceFormula( playerA.location.lat , playerA.location.long, playerB.location.lat , playerB.location.long ) < 1
};
var distanceFormula = function (lat1 , lon1 , lat2, lon2) {
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
};
var getMatch = function (matchId) {
match = matches.$getIndex(matchId);
return match;
};
var seekMatch = function ( player ) {
var deferred = $q.defer();
angular.forEach(matches.$getIndex() , function (matchId){
var matchRef = firebaseRef( 'matches/'+matchId );
var matchDB = $firebase( matchRef );
matchDB.$on('loaded',function (data) {
console.log(data);
if (matchMakingFormula(data.playerA , player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
});
return deferred.promise;
};
// Public API here
return {
get: function (matchId) {
return getMatch(matchId);
},
seek: function (points) {
return seekMatch(points);
//return match.promise;
},
new: function (points) {
//return match.promise;
},
join: function (match) {
//return match;
}
};
}]);
Thanks in advance. Cheers and have fun!
Upvotes: 0
Views: 554
Reputation: 974
OK, finally "found" the solution. Thanks to kato that remind me to check my version.
Current version 0.7.2 preview works for me. Thing is that is not on bower yet and I assumed that I had the latest version while updating from bower. Which was wrong.
collection.$child( matchId ).$on('loaded' , function(match){ //<---- match now returns the proper object but also null or {} empty object sometimes if empty.
if (match) {
if (valid(match)){ //<-- so you need to validate the output not just !match
deferred.resolve(match);
}
else
{
deferred.reject('invalid');
}
}
else
{
deferred.reject('no match');
}
});
Either way is always a good idea to validate your endpoints before consuming them for recovery and error catching reasons.
Better update from github because the project seems to advance much quicker than it's bower registry.
Cheers and have fun.
Upvotes: 1
Reputation: 974
Another think to bear in mind is that this
matchDB.$on('loaded', function(data) {
console.log(matchDB); // <--- notice what is going on here
if (matchMakingFormula(data.playerA, player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
return's this
matchDB: Object
$add: function (item) {
$auth: function (token) {
$bind: function (scope, name) {
$child: function (key) {
$getIndex: function () {
$off: function (type, callback) {
$on: function (type, callback) {
$remove: function (key) {
$save: function (key) {
$set: function (newValue) {
$transaction: function (updateFn, applyLocally) {
playerA: Object // <---- notice this. it was the content of the object in firebase
__proto__: Object
Which is completely crazy... It is actually merging the matchDB which is the DB reference of match with the object that I'm expecting from the firebase.
"13131314141"{
"playerA" : {
"location" : {
"lat" : 51.6021821,
"long" : "-02582276"
},
"type" : "monster"
}
}
You can actually make a solution out of this. but how you can take the result in the callback to use it as a promise deferred.resolve?
I can understand that they did this to be able to do
$scope.match = $matchDB.$on('loaded' , function(){});
but this doesn't serve my purpose that is decoupling firebase from my controllers and I don't really think it is actually a neat solution.
Please don't accept this as a solution because it is not a real one. You can hack your way to make it one but there is probably a better way to do it or at least the project is too young and soon a proper solution will be available.
Upvotes: 0
Reputation: 974
i fixed that with a small hack in angularfire.js
line 336ish in the $on handler
336. callback();
change with
336. callback(self._snapshot);
line 587ish in the end of _wrapTimeout function add
587. //hack to preserve the snapshot from the timeout wipeouts
if ( evt === "loaded" ) {
self._snapshot = param;
}
I hope this will help you for now. I will try to find a proper solution for this.
Upvotes: 0