mrmjct
mrmjct

Reputation: 3

Delay returning variable until data synced from Firebase

I am using angular and firebase using angularfire in an app. I have a number of text documents stored in firebase. I want to take all of these documents and return an array of all the paragraphs in these documents for manipulation with angular.

I created a service which syncs the data from firebase with a variable documents. This the service has the following function to change this to an array of paragraphs.

getParas: function(){
    var paras = [];
    for(var i=0;i< documents.length;i++){
        paras = paras.concat(documents[i].text.split('\n'));
    }
    return paras;
}

However because of the time the data takes to sync from firebase the function returns before the changes have been made to the paras variable.

I've tried using documents.$loaded().then(function(){... but am not sure how to return the result of this from the getParas() function.

I've seen article, but a filter doesn't seem like the best option for this?

Thanks

Matthew

Upvotes: 0

Views: 749

Answers (2)

Kato
Kato

Reputation: 40582

Since it wasn't the question, I'm going to ignore the fact that you should probably just store the paragraphs in a separate path, so you can just fetch them ready to go, rather than going through this extraneous step of parsing them each time they are read. But I will point out that storing the data how you will read it back is a big step in the right direction.

For several reasons, using $loaded is incorrect here. First of all, you've taken dynamic, real-time data and turned it into a static asset that will not update when the source data changes. Might as well just store it in a file on the web server and fetch it with HTTP if this is the case. Secondly, utilizing $loaded() generally means you're creating extra work for yourself and trying to micromanage things AngularFire already does very well, like managing asynchronous downloads and manipulating data in the synchronized array.

Most likely, you just want to use $extendFactory to create an additional method on the array prototype.

// create a factory that has a getParas method
app.factory('ListFactory', function($FirebaseArray) {
    return $FirebaseArray.$extendFactory({
        getParas: function() {
            var paras = [];
            for(var i=0; i < this.$list.length; i++){
                paras = paras.concat(this.$list[i].text.split('\n'));
            }
            return paras;
        }
    });
});

// create the synchronized list
app.factory('List', function(ListFactory, $firebase) {
    return function(ref) {
        return $firebase(ref, {arrayFactory: ListFactory}).$asArray();
    }
});

app.controller('ctrl', function(List, $window, $scope) {
     var ref = new $window.Firebase(URL);
     $scope.list = List(ref);
});

Then in your view, just reference the new method:

<pre>
{{list.getParas()|json}}
</pre>

Upvotes: 1

Samuel Norling
Samuel Norling

Reputation: 56

You have two options.

  1. Resolve the "documents" variable before the page is loaded with routes.
  2. Use promises, and make sure "getParas" is called after the data is set to the "documents" variable.

Below is a solution with promises that does it all in the controller, you could start with this then try to incorporate it into your service.

.controller("YourCtrl", ["$scope", "$q", function($scope, $q){
  var promise = getFB();
  promise.then(function(){
    afterPromise();
  })

  function getFB(){
    var documentRef = new Firebase("YOURFIREBASERURL/documents")
    var fireDataDef = $q.defer()

    documentRef.once("value", function(snapshot){
      $scope.documents = snapshot.val();
      fireDataDef.resolve();
    })
    return fireDataDef.promise;
  }
function afterPromise(){
    var $scope.paras = [];
    for(var i=0;i< $scope.documents.length;i++){
        $scope.paras = $scope.paras.concat(documents[i].text.split('\n'));
    }
  }
}]

Upvotes: 2

Related Questions