Cyril
Cyril

Reputation: 167

Synchronization needed in asynchronous code

I am trying to create a factory for angular module which returns a JSON object received through angular's $http.get().

Within the callback function for success(), I am trying to assign the object to the variable products. It seems products is getting returned from the factory function before the assignment could take place.

Can someone advise how the below code can be synchronized? How do we delay execution of return products; until $http.get('/products').success(); is complete? Also is there a better approach?

angular.module('app', ['ngRoute'])
    .factory('products', ['$http',function ($http) {
        var products;
        $http.get('/products').success(function(data,status,header,config){
            products=data;
            console.log(products); //[Object, Object, Object]
        });
        console.log(products); //undefined
        return products;
    }])

UPDATE

angular.module('app', [])
        .factory('members1', ['$http',
            function ($http) {
                var members1=[];
                $http({
                    method: 'GET',
                    url: '/members.json'
                }).success(function (data, status, headers, cfg) {
                    members1=data;
                });
                return members1;
            }])
        .factory('members2', ['$http',
            function ($http) {
                var members2=[];
                $http({
                    method: 'GET',
                    url: '/members.json'
                }).success(function (data, status, headers, cfg) {
                    angular.copy(data,members2);
                });
                return members2;
            }])
        .controller('controller1', ['$scope','members1','members2',
            function ($scope,members1,members2) {
                $scope.members1=members1;
                $scope.members2=members2;
            }]);

above code has two identical factories with just one difference (angular.copy instead of assignment). The one with angular.copy works for the below template.

<div ng-controller="controller1">
    <div>
        Using Members1 Factory
        <ul>
            <li ng-repeat="member in members1">{{member.name}}</li>
        </ul>
    </div>
    <div>
        Using Members2 Factory
        <ul>
            <li ng-repeat="member in members2">{{member.name}}</li>
        </ul>
    </div>
</div>

view :

Using Members1 Factory
Using Members2 Factory
Name1
Name2
Name3

Upvotes: 1

Views: 298

Answers (4)

Michael Kang
Michael Kang

Reputation: 52837

I prefer the approach of returning the data from the service (which would be initially empty) and populate it once the $http promise is resolved, all-the-while preserving the array reference:

angular.module('app', ['ngRoute'])
    .factory('products', ['$http',function ($http) {
        var products = [];
        $http.get('/products').success(function(data,status,header,config){
            angular.copy(data, products);
            console.log(products); //[Object, Object, Object]
        });
        console.log(products); // empty initially, populated when promise resolved.
        return products;
    }])

Upvotes: 1

Ammar Khan
Ammar Khan

Reputation: 2585

I belived, you want to encapsulate your data logic inside services. Here is a cleaner approach.

var app=angular.module('app',['ngRoute']);

app.factory('products', ['$http',function ($http,$q) {

        return{
           getAll:function(){
               var deffered = $q.defer();
               $http.get('/products').success(function(data,status,header,config){
                    deffered.resolve(data);

               }).error(function(status){
                    deffered.reject(status);
               });

               return deffered.promise;
           }
        };


    }]);

app.controller('myController',function myController($scope, products){
     products.getAll().then(function(data){
          //the property that needs to bind on view
          $scope.products=data;
     });
});

Upvotes: 1

biofractal
biofractal

Reputation: 19123

The good news is that you are very close. The $http.get method is designed to do just what you want and so its return value is a promise (if that doesn't mean much to you then you need to go learn about the angular $q library)

So you don't try and return the products themselves, instead you just return the promise and use it

Upvotes: 0

Pepijn
Pepijn

Reputation: 1204

The best way to go with this problem in my opinion is to use the promise that $http returns by default.

angular.module('app', ['ngRoute'])
    .factory('products', ['$http',function ($http) {
        return $http.get('/products');
    }]);

Then in your controller/directive/anywhere where you want to inject your factory you could use something like:

angular.module('app').controller('ExampleCtrl', function ($scope, products) {

    products.then(function (data) {
        $scope.products = data;
    });

});

Upvotes: 1

Related Questions