Using deeply nested object from JSON in AngularJS - strange behavior
Solution 1
Your $http
request is asynchronous.
app.controller('MainCtrl', function($scope, JsonSvc) {
JsonSvc.read('data.json', $scope);
//$scope.data.level1.level2 doesn't exist yet at this point in time
//and throws an exception
$scope.nestedObj = $scope.data.level1.level2;
//$scope.data.level1.level2 doesn't exist yet at this point in time
//and throws an exception
//once Angular does dirty checking this one will work since the
//$http request finished.
$scope.getLen = function () {
return $scope.data.level1.level2.length
};
});
Since you have three scope objects that rely on that data it would be best to assign those in the call back.
app.factory('JsonSvc', function ($http) {
return {read: function(jsonURL, scope) {
$http.get(jsonURL).success(function (data, status) {
scope.data = data;
scope.nestedObj = scope.data.level1.level2;
scope.getLen = function () {
return scope.data.level1.level2.length;
};
});
}};
});
If you do not want to set it all up on the call back, you could also use $broadcast()
and $on()
app.factory('JsonSvc', function ($http, $rootScope) {
return {
read: function (jsonURL, scope) {
$http.get(jsonURL).success(function (data, status) {
scope.data = data;
$rootScope.$broadcast("jsonDone");
});
}
};
});
app.controller('MainCtrl', function ($scope, JsonSvc) {
JsonSvc.read('data.json', $scope);
$scope.name = "world";
$scope.$on("jsonDone", function () {
$scope.nestedObj = $scope.data.level1.level2;
$scope.getLen = function () {
return $scope.data.level1.level2.length;
};
});
});
Solution 2
Ray, another option is to return the $http.get call since its a promise and use the .then() function to declare $scope.nestedObj or anything else you want to do with data once it returns.
Here's my example: http://plnkr.co/edit/GbTfJ9
You can read more about promises in Angular here: http://docs.angularjs.org/api/ng.$q
Ray Shan
Updated on June 04, 2022Comments
-
Ray Shan almost 2 years
I'm trying to understand how AngularJS sees an object from a deeply nested JSON. Here's an example plunker. The data comes from service and is assigned to
$scope.data
. The javascript code seems to want me to declare every level of the object first before usage, but referencing a deep level within object from the view HTML always works, and using the deep level in a function kinda works. It's rather inconsistent.I'm not sure if my understanding of
$scope
is lacking, or if this has something to do with promise objects. Advise please?HTML
<body ng-controller="MainCtrl"> Referencing nested obj in view works: {{data.level1.level2}} <br> Using nested obj within declared scope var doesn't work: {{nestedObj}} <br> Using nested obj in a function works but throws TypeError: {{getLen()}} </body>
Javascript
var app = angular.module('app', []); app.factory('JsonSvc', function ($http) { return {read: function(jsonURL, scope) { $http.get(jsonURL).success(function (data, status) { scope.data = data; }); }}; }); app.controller('MainCtrl', function($scope, JsonSvc) { JsonSvc.read('data.json', $scope); // Using nested obj within declared scope var doesn't work // Uncomment below to break whole app // $scope.nestedObj = $scope.data.level1.level2; // Using nested obj in a function works but throws TypeError // Declaring $scope.data.level1.level2 = [] first helps here $scope.getLen = function () {return $scope.data.level1.level2.length}; });
JSON
{ "level1": { "level2": [ "a", "b", "c" ] } }
-
Ray Shan almost 11 yearsThanks Mark. I see how
$http
request is not finished. But how can I map JSON object to$scope.data
exactly once it's finished? I thought.success
would take care of it but apparently not. If the JSON is large with many levels, will each level have to be mapped manually and separately?