Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularfireCollection: know when the data is fully loaded

I am writing a small Angular web application and have run into problems when it comes to loading the data. I am using Firebase as datasource and found the AngularFire project which sounded nice. However, I am having trouble controlling the way the data is being displayed.

At first I tried using the regular implicit synchronization by doing:

angularFire(ref, $scope, 'items');

It worked fine and all the data was displayed when I used the model $items in my view. However, when the data is arriving from the Firebase data source it is not formatted in a way that the view supports, so I need to do some additional structural changes to the data before it is displayed. Problem is, I won't know when the data has been fully loaded. I tried assigning a $watch to the $items, but it was called too early.

So, I moved on and tried to use the angularfireCollection instead:

$scope.items = angularFireCollection(new Firebase(url), optionalCallbackOnInitialLoad);

The documentation isn't quite clear what the "optionalCallbackOnInitialLoad" does and when it is called, but trying to access the first item in the $items collection will throw an error ("Uncaught TypeError: Cannot read property '0' of undefined").

I tried adding a button and in the button's click handler I logged the content of the first item in the $items, and it worked:

console.log($scope.items[0]);

There it was! The first object from my Firebase was displayed without any errors ... only problem is that I had to click a button to get there.

So, does anyone know how I can know when all the data has been loaded and then assign it to a $scope variable to be displayed in my view? Or is there another way?

My controller:

app.controller('MyController', ['$scope', 'angularFireCollection',
  function MyController($scope, angularFireCollection) {
    $scope.start = function()
    {
        var ref = new Firebase('https://url.firebaseio.com/days');
        console.log("start");

        console.log("before load?");

        $scope.items = angularFireCollection(ref, function()
        {
            console.log("loaded?");
            console.log($scope.items[0]); //undefined
        });  

        console.log("start() out");     
    };

    $scope.start();

    //wait for changes
    $scope.$watch('items', function() {
        console.log("items watch");
        console.log($scope.items[0]); //undefined
    });

    $scope.testData = function()
    {
         console.log($scope.items[0].properties); //not undefined
    };  
  }
]);

My view:

<button ng-click="testData()">Is the data loaded yet?</button>

Thanks in advance!

like image 307
iafiawik Avatar asked Sep 22 '13 16:09

iafiawik


2 Answers

So, does anyone know how I can know when all the data has been loaded and then assign it to a $scope variable to be displayed in my view? Or is there another way?

Remember that all Firebase calls are asynchronous. Many of your problems are occurring because you're trying to access elements that don't exist yet. The reason the button click worked for you is because you clicked the button (and accessed the elements) after they had been successfully loaded.

In the case of the optionalCallbackOnInitialLoad, this is a function that will be executed once the initial load of the angularFireCollection is finished. As the name implies, it's optional, meaning that you don't have to provide a callback function if you don't want to.

You can either use this and specify a function to be executed after it's loaded, or you can use $q promises or another promise library of your liking. I'm partial to kriskowal's Q myself. I'd suggest reading up a bit on asynchronous JavaScript so you get a deeper understanding of some of these issues.

Be wary that this:

$scope.items = angularFireCollection(ref, function()
        {
            console.log("loaded?");
            console.log($scope.items[0]); //undefined
        });  

does correctly specify a callback function, but $scope.items doesn't get assigned until after you've ran the callback. So, it still won't exist.

If you just want to see when $scope.items has been loaded, you could try something like this:

$scope.$watch('items', function (items) {
    console.log(items)
});
like image 139
Eric Hotinger Avatar answered Oct 04 '22 05:10

Eric Hotinger


In my project I needed to know too when the data has been loaded. I used the following approach (implicit bindings):

$scope.auctionsDiscoveryPromise = angularFire(firebaseReference.getInstance() + "/auctionlist", $scope, 'auctionlist', []);

$scope.auctionsDiscoveryPromise.then(function() {
    console.log("AuctionsDiscoverController auctionsDiscoveryPromise resolved");
    $timeout(function() {
        $scope.$broadcast("AUCTION_INIT");
    }, 500);

}, function() {
    console.error("AuctionsDiscoverController auctionsDiscoveryPromise rejected");
});

When the $scope.auctionsDiscoveryPromise promise has been resolved I'm broadcasting an event AUCTION_INIT which is being listened in my directives. I use a short timeout just in case some services or directives haven't been initialized yet.

like image 43
Alexander Burakevych Avatar answered Oct 04 '22 04:10

Alexander Burakevych