Dave Dave - 5 months ago 7
AngularJS Question

AngularJS $scope dropping value after it is assigned

I am hoping someone can help me understand an annoying problem I am having with $scope in AngularJS. Please see the comments in my code below:

app.controller('MyController', function ($scope, $routeParams, $http, $timeout) {
$scope.id = $routeParams.id;

$http.get("http://server/api/Blah/GetData/" + $scope.id).success(function (data) {
$scope.data = data;
alert($scope.data.MyObject.Property); //displays the expected value. - Not Undefined or null
}).error(function (data) {
alert(data);
});

$scope.$on('$viewContentLoaded', function () {
$timeout(function () {
var d = document.getElementById("iframe");

d.contentDocument.documentElement.innerHTML = $scope.data.MyObject.Property; //Now MyObject is magically undefined.

}, 0);
});
});


The call to the WEB API returns a valid object which is assigned to $scope.data. I display an alert to make sure that $scope.data.MyObject.Property exists, which it does. The expected value is displayed.

Now when I try accessing $scope.data.MyObject.Property in the $viewContentLoaded code, the $scope.data.MyObject is no longer in the $scope. The console reports the following:


HTML1300: Navigation occurred.
File: route.html
TypeError: Unable to get property 'MyObject' of undefined or null reference
at Anonymous function (http://server/script/route.js:43:13)
at Anonymous function (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js:158:234)
at e (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js:45:348)
at Anonymous function (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js:48:275)


Why is $scope dropping the value of $scope.data.MyObject? What makes this problem even more frustrating is if I put an alert(""); in the $viewContentLoaded code, the $scope.data.MyObject value is no longer undefined. What is going on here?

Answer

You need to know the timing of how your code get executed.

This is fixed code with some logging:

app.controller('MyController', function ($scope, $routeParams, $http, $timeout) {
    $scope.id = $routeParams.id;

    console.log(1);

    var promise = $http.get("http://server/api/Blah/GetData/" + $scope.id).success(function (data) {
        $scope.data = data;
        console.log(2);
        alert($scope.data.MyObject.Property); //displays the expected value. - Not Undefined or null
    }).error(function (data) {
        alert(data);
    });

    $scope.$on('$viewContentLoaded', function () {
        $timeout(function () {
            var d = document.getElementById("iframe");

            console.log(3);
            // d.contentDocument.documentElement.innerHTML = $scope.data.MyObject.Property;

            promise.then(function () {
                console.log(4);
                d.contentDocument.documentElement.innerHTML = $scope.data.MyObject.Property;
            });

        }, 0);
    });
});

You may expect the result logs is 1234, but actually it can be 1324. In later case, the code in $viewContentLoaded is executed before the $http.get success. So it $scope.data is still null.

The solution is using Promise (or $q in angular world). So that you can wait for the result of $http.get. You have guarantee that 4 is always executed after 2 (assuming it succeeded).