1

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
1
  • 2
    You can't. Instead, return a promise. Commented Jun 15, 2014 at 20:01

4 Answers 4

1

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;
    }])
Sign up to request clarification or add additional context in comments.

6 Comments

This was exactly what I wanted. So angular.copy() observes the source and updates the destination as and when the source changes?
Its the $http.get that is asynchronous. Angular.copy does preserve the source - its when the async call returns that it actually populates the array.
The above answer is the 'angular' approach - it does not attempt to delay the return of $http.get, or use any kind of synchronization.
could you explain why the code in the question doesn't work, though it seems to be similar to the code you supplied, apart from "var products = [];" and "angular.copy(data, products);"
in a controller, i am trying to do "$scope.products=products". With my code, the view doesn't reflect the data, but if I change "products=data" to "angular.copy(data, products)", it works!
|
1

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;
    });

});

Comments

1

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;
     });
});

1 Comment

$http already returns a promise, so it's unneccessary to make oe yourself.
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.