Proper way to resolve an object going to be extended after resolving a different promise

Issue

I’m trying to make a resource paginator as a wrapper service to restangular :

.factory('Collection', Collection)
 function Collection(Restangular) {
    var _route;
    ...

    function Collection(collectionName) {
        if (typeof collectionName === "undefined") {
            throw new Error("collection name is missing");
        }
        _route = collectionName;
    };

    Collection.prototype = {

        setData: function(response) {
            var data = response.data.plain();
            var _links = helpers.parse_link_header(response.headers('Link'));
            ...

            angular.extend(this, data);
        },

        load: function(perPage) {
            var scope = this;
            Restangular.all(_route).getList({'page':1, 'per-page':perPage})
            .then(function(collectionData) {
                scope.setData(collectionData);
            });
        },

        firstPage: function() {...},
        nextPage: function() {...}),
        ...

And this is how I’m using it inside controller :

vm.images = new Collection('images');
vm.images.load(2);

images.load() will get data from server and once resolved it will send it to setData() method which will perform all the logic I need as parsing headers links, meta, .. then will extend the class with all data I’m expecting to have in my controller.

This works. It throws few logical errors like Cannot read property 'next' of undefined on page load but once the Model get extended everything works fine and I can do those kind of things in my HTML view file :

vm.images.nextPage();
vm.images.existNext();
vm.images.meta().$currentPage;

My question is :

What is the proper way to initialise my collection within an ui-router’s state resolve method and be sure that my object is extended with server response before the controller is instantiated ?

What I’ve tried so far :

I know that a promise should be returned somehow so I made the load method look like :

load: function(perPage) {
    var deferred = $q.defer();
    var scope = this;
    _perPage = perPage;
    Restangular.all(_route).getList({'page':1, 'per-page':perPage})
    .then(function(collectionData) {
        var e = scope.setData(collectionData)
        deferred.resolve(e);
    }, 
    function(response) {
      console.log("Error with status code", response.status);
      deferred.reject();
    });
    return deferred.promise;
},

I made the setData return somthing :

setData: function(response) {
    ...
    var newClass = angular.extend(this, data);
    return newClass;
},

Then I added this to my $stateProvider.state :

resolve: {
  Collection: 'Collection',
  images: function(Collection){
    var images = new Collection('images');
    images.load(2);
    return images; 
  }
},

vm.images = images inside controller is getting data as expected but the same errors are thrown same as without adding it at all, so the code inside it is getting executed but I can’t figure out how to make it wait for the model to be extended with server responses.

Any help would be appreciated & Thanks in advance !

Solution

In the resolver, you should return the load promise:

images: function (Collection){
    var images = new Collection('images');
    return images.load(2);
}

Notice also that, in the load function, you could use the promise returned by getList:

load: function (perPage) {
    return Restangular.all(_route).getList({'page':1, 'per-page':perPage})
        .then(scope.setData)
        .catch(function (response) {
            return $q.reject('Error with status code ' + response.status);
        });
}

Answered By – Michael P. Bazos

Answer Checked By – Cary Denson (AngularFixing Admin)

Leave a Reply

Your email address will not be published.