adam Kiryk
adam Kiryk

Reputation: 1735

Trouble with Async requests for data in Angular with Firebase & AngularFire

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

Answers (1)

Kato
Kato

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

Related Questions