Search code examples
javascriptangularjsangularjs-serviceangularjs-resource

AngularJS 1.2.19 return values from nested $resource service


I'm fairly new to angularJS, but wrapping up a project and trying to clean up some code by moving a lot of business logic from my controller into a service call.

Currently my three factory service ($resource) calls all work correctly and the results are combined in the controller. But I am not having luck moving all this logic into the service call and returning the combined object, an array.

Here is a much simplified version of my project:

var Myservices = angular.module('Myapp');

Myservices.factory('ResourceA', ['$resource',
function($resource) {
    return $resource('/api/resourcea');
}
]);

Myservices.factory('ResourceB', ['$resource',
function($resource) {
    return $resource('/api/resourceb');
}
]);

Myservices.factory('ResourceC', ['$resource',
function($resource) {
    return $resource('/api/resourcec');
}
]);


Myservices.factory('Results', ['ResourceA', 'ResourceB', 'ResourceC',
function (ResourceA, ResourceB, ResourceC) {

    var resourcea, resourceb, resourcec;
    var results = [];

    return {
        get: function() {
            ResourceB.query().$promise.then(function(data) {        // these calls all work
                resourceb = data;
            });
            ResourceC.query().$promise.then(function(data) {
                resourcec = data;
            });

            ResourceA.query().$promise.then(function(data) {
                resourcea = data;

            // other code here combines above resource calls and 
            // populates results[] array successfully ...
            //  
                return results;     // this is loaded correctly, but I can't return it
            });
        }
    }
}]);

In debugging through this, I do indeed have an array of results in 'results'.

Now back in my controller I have the following code (simplified for your reading pleasure):

Myapp.controller('MyCtrl',['$scope', '$http', '$log','Results',
function($scope,$http,$log, Results){

// lot of initialization stuff here


// I don't know the correct call to get the 'results' array from my service back to my controller
// these all produce errors
Results.query().$promise.then(function(data){   //Object doesn't support property or method 'query'
    $log.info('Results: ' + data);
}
Results.query().then(function(data){            //Object doesn't support property or method 'query'
    $log.info('Results: ' + data);
}
Results.get().$promise.then(function(data){     //Unable to get property '$promise' of undefined or null reference
    $log.info('Results: ' + data);
}
Results.get().then(function(data){              //Unable to get property 'then' of undefined or null reference
    $log.info('Results: ' + data);
}
Results.get().success.then(function(data){      //Unable to get property 'success' of undefined or null reference
    $log.info('Results: ' + data);
}
}]);

As you can see, I have begun to resort to 'Trial and Error' with more of the latter than I desire. I am obviously missing something, but feel I am close.

Thank you in advance for any suggestions.

Cheers, Dan


Solution

  • It is not clear how you combine the results from ResourceA.query(), ResourceB.query() and ResourceC.query(), but the approach is probably incorrect, since the data are fetched asynchronously and you seem to be trying to combine them synchronously.
    Furthermore, you can't call Results.query(), because there is no such method.

    You need to combine the A, B, C results asynchronously.
    This also means you can't return results from your Results.get() method. You need to return a promise that gets resolved once the data is fetched and combined.

    You can combine multiple promises into one using $q.all().

    E.g.:

    Myservices.factory('Results', function ($q, ResourceA, ResourceB, ResourceC) {
        var results = [];
    
        return {
            get: function () {
                var dataA = ResourceA.query();
                var dataB = ResourceB.query();
                var dataC = ResourceC.query();
    
                var resultsPromise = $q.all({
                    a: dataA.$promise,
                    b: dataB.$promise,
                    c: dataC.$promise
                }).then(function (data) {
                    var results = [].concat(dataA).concat(dataB).concat(dataC);
                    return results;
                }).catch(function (error) {/* Handle the error... */});
    
                return resultsPromise;
            }
        };
    });
    

    Then, you can use it in your controller like this:

    Myapp.controller('MyCtrl', function ($scope, $http, $log, Results) {
        ...
        Results.get().then(function (results) {
            $scope.results = results;
            $log.info(results);
        });
    });