Reputation: 1735
I have a question about async loading with angular and Firebase. I've got a User model and a Language model; a user can have many languages, meaning that the User model has a property called "languages," which is an array.
I can get the current user with:
$scope.user = User.findByUsername($routeParams.username)
I can then take some action once the user is loaded. The action I want to take is to find the language object from the database based on the user's language ID. That is, instead of saying 'John User speaks IDXXXXXXX', I want to say 'John User speaks English.'
$scope.user.$on('loaded', function(){
angular.forEach($scope.user.languages, function(id){
$scope.userLanguages[id] = Language.find(id);
});
});
This sometimes works, but not always. It seems obvious that the problem is with the way I'm finding a language by ID — sometimes it's super quick and $scope.userLanguages[id] means something and sometimes it isn't quick enough and id is 'undefined'. It's almost like I need to add another $on('loaded') listener for Language.find(). But then I get into a complicated set of nested listeners, which seems crazy.
What am I missing here?
// The basic structure of data in my Firebase database
languages
-IDXXXXXXXX
name: 'English',
localName: 'English'
-IDXXXXXXXX
name: 'Spanish',
localName: 'Espanol'
users
-SomeName
username: 'Whatever',
languages: [IDXXXX, IDXXXY, etc]
In crafting my question, it occurred to me that the following might work. It does, but it feels weird to put one $on inside of another. Is this a sign that I should be looking for a better way to structure my data and code? Or perhaps it's fine...
$scope.user.$on('loaded', function(){
angular.forEach($scope.user.languages, function(id){
var lang = Language.find(id);
lang.$on('loaded', function(){
$scope.userLanguages[id] = Language.find(id);
$scope.languages[id].selected = true;
});
});
});
Upvotes: 0
Views: 309
Reputation: 40582
Since the loaded
event is guaranteed to only get called once, it's fine to nest them. However, assuming Language.find
is returning a $firebase
instance, it's probably not necessary; you can assign it immediately; it will still update when the data arrives.
$scope.user.then(function() { // as of 0.7.2, this is equivalent to $on('loaded')
angular.forEach($scope.user.languages, function(id) {
$scope.userLanguages[id] = Language.find(id);
});
});
One thing to keep in mind is that your 'loaded' event only ever fires once, so if the user's index of languages changes in the future, neither $scope.userLanguages
nor $scope.languages
will get updated, since this is not monitored in real-time.
If you wanted to add real-time monitoring, you could do something like the following, rather than using the loaded
event:
$scope.user.$child('languages').$on('child_added', function(snap) {
var id = snap.snapshot.name;
$scope.userLanguages[id] = Language.find(id);
});
Upvotes: 1