Satyajit Patnaik
Satyajit Patnaik

Reputation: 111

Getting the object obtained from asynchronous $http service in angularJS to be accessible globally in $scope

I am working using Angular JS. I am trying to get a json object obtained using $http service to be accessible in a $scope variable. Inside all the asynchronous AJAX ($http.get()) calls, if I try to print the data obtained stored inside a $scope variable and print it, it works successfully and shows me the expected data. But outside scope of the asynchronous method, the same $scope variable assigned with data obtained loses hold of it and prints undefined.

Code:

var app = angular.module('chariot', ['ngRoute', 'ngFileUpload']);
    app.factory('getTestcaseFactory', ['$http', '$routeParams', '$q', function($http, $routeParams, $q) {
       return {
              list: function(){
                var deferred = $q.defer();
                $http.get('/testcase/' + $routeParams.testcase)
                .success(function(data, status, headers, config) {
                    deferred.resolve(data);
                })
                .error(function(data, status, headers, config) {
                    deferred.reject("Error fetching XML file: " + status + ' ' + JSON.stringify(headers));
                });
                return deferred.promise;
              }
            }; 
    }
    ]);
app.controller('testcaseCapCtrl', ['$scope', '$routeParams', '$http', 'getTestcaseFactory', function($scope, $routeParams, $http, getTestcaseFactory) {
    $scope.myjsonobj = '';
    var fetchTestcaseDetails = function() {
    getTestcaseFactory.list()
        .then(
            function(data) {
                $scope.xml.file = data;
                var x2js = new X2JS();
                var jsonObj = x2js.xml_str2json($scope.xml.file);
                $scope.xml.json = JSON.stringify(jsonObj, null, 2);
                $scope.model = jsonObj;
                console.log($scope.model);//PRINTS THE RIGHT DATA
            },
            function(data) {
                alert(data);
            });
    }
fetchTestcaseDetails();
console.log($scope.model); //Prints undefined
}]);

Upvotes: 1

Views: 162

Answers (3)

doldt
doldt

Reputation: 4506

The solutions posted so far are fundemantally wrong, as they depend on an arbitrary timeout, and nothing guarantees that the async answer will be available by then.

As I suggested in a comment above, here are 2 more data/event-driven solutions.

  1. You either only print in the callback function (which you're already doing in your example)
  2. Or since you're using angular anyway, you can set up a watch (which in the background uses a not-too-nice dirty checking solution, but at least it's abstracted away from your code)

If you want to run a function called processData once the data is available, solution 1 you already do in your example, see this line:

console.log($scope.model);//PRINTS THE RIGHT DATA

Here you could call any other function to trigger the continuation of the process, just call whatever function:

console.log($scope.model);//PRINTS THE RIGHT DATA
processData();

Using angular's $watch mechanism (2.):

$scope.$watch(
    function(){return $scope.model;},
    function(newValue, oldValue){
        if(newValue != null){
            console.log($scope.model);
            processData();
        }
})

This will print the data when it is available after the async callback, and the program will continue with processing from then.

Upvotes: 0

Satyajit Patnaik
Satyajit Patnaik

Reputation: 111

This saves my day! $timeout to the rescue. Thanks @Dan Moldovan for the answer! As the $http service is asynchronous, we have to set a timer to wait for a time interval till the point the data is really received in the promise.success section and then it can be assigned to a variable inside $scope.

var app = angular.module('chariot', ['ngRoute', 'ngFileUpload']);
        app.factory('getTestcaseFactory', ['$http', '$routeParams', '$q', function($http, $routeParams, $q) {
           return {
                  list: function(){
                    var deferred = $q.defer();
                    $http.get('/testcase/' + $routeParams.testcase)
                    .success(function(data, status, headers, config) {
                        deferred.resolve(data);
                    })
                    .error(function(data, status, headers, config) {
                        deferred.reject("Error fetching XML file: " + status + ' ' + JSON.stringify(headers));
                    });
                    return deferred.promise;
                  }
                }; 
        }
        ]);
    app.controller('testcaseCapCtrl', ['$scope', '$routeParams', '$timeout', 'getTestcaseFactory', function($scope, $routeParams, $timeout, getTestcaseFactory) {
        var fetchTestcaseDetails = function() {
        getTestcaseFactory.list()
            .then(
                function(data) {
                    $scope.xml.file = data;
                    var x2js = new X2JS();
                    var jsonObj = x2js.xml_str2json($scope.xml.file);
                    $scope.xml.json = JSON.stringify(jsonObj, null, 2);
                    $scope.model = jsonObj;
                    console.log($scope.model);//PRINTS THE RIGHT DATA
                },
                function(data) {
                    alert(data);
                });
        }
    fetchTestcaseDetails();
    $timeout(function() {
      console.log($scope.model); //Prints the Data now
    }, 2000);
    }]);

Upvotes: 0

Dan Moldovan
Dan Moldovan

Reputation: 3591

By the time

console.log($scope.model);

executes, the $http request would have not gone through yet, and this is why it prints undefined. Once the $http request is done, your $scope.model will update accordingly. You can test this by using a $timeout

$timeout(function () {
    console.log($scope.model);
}, 5000);

Don't forget to inject $timeout in your controller.

Upvotes: 0

Related Questions