Reputation: 1776
I have an angular controller that uses a factory to return a response from a JSON file. When I try to traverse the data returned I get the following error seen in the comment. However if I use the realArticleData object inside of HTML within angular brackets it transverses perfectly. I believe it has something to do with the promise returned by the factory but I am not sure.
Controller Snippet:
function SpecialOrderItemsCtrl($scope, $location, articleDataService) {
$scope.realArticleData = articleDataService.getArticles();
//This throws TypeError: Cannot read property 'thdCustomer' of undefined
$scope.custInfo = $scope.realArticleData.articles.thdCustomer;
}
HTML Snippet:
<div ng-controller=SpecialOrderItemsCtrl>
<label>Raw Article JSON</label>
<p>{{realArticleData}}</p>
<label>Json transversed by one level</label>
<p>{{realArticleData.articles}}</p>
<label>THD CUstomer </label>
<p>{{realArticleData.articles.thdCustomer}}</p>
</div>
Factory:
function articleDataService($rootScope, $http) {
articleDataService.data = {};
articleDataService.getArticles = function() {
$http.get('articleData.json')
.success(function(data) {
articleDataService.data.articles = data;
});
return articleDataService.data;
};
return articleDataService;
}
sendDesign.factory('articleDataService', articleDataService);
Upvotes: 2
Views: 1200
Reputation: 20576
The key point to remember is that promises do not return values, they do something with a value when or if a value arrives.
This answer is based on answers from @VladimirGurovich and @tymeJV.
Here's a simple factory:
var app = angular.module('myApp', []);
app.factory('Names', ['$http', function($http) {
return {
url: 'names.json',
fetch: function() {
return $http.get(this.url, {cache: true}).then(function(json) {
return json.data;
});
}
};
}]);
The Names
factory itself returns an object. The fetch
method returns the promise chain from $http.get
. This uses .then()
instead of .success()
so the result can be chained. The factory could return the $http
promise directly, but giving the operation a name seems clearer to me.
@VladimirGurovich's answer handles caching manually, but Angular can do that for us, just send a config object like {config: true}
to $http.get
along with the url.
Here's a simple little controller which depends on data from the Names
factory:
app.controller('MainCtrl', ["$scope", "Names", function($scope, Names) {
Names.fetch().then(function(data) {
$scope.names = data.names;
});
}]);
When the Names.fetch()
promise completes, $scope.names
is populated with the returned data.
Here's a Plunker based on this: http://plnkr.co/edit/nMx1XL
Upvotes: 2
Reputation: 8463
You are dealing with promises. Therefore, you need to deal with them asynchronously. as in
angular.module('plunker', []).controller('SpecialOrderItemsCtrl', ['$scope', 'articleDataService', function($scope, articleDataService){
// when promise is resolved, set the scope variable
articleDataService.getArticles().then(function(articles){
// store any data from json that you want on the scope for later access in your templates
$scope.articles = articles;
})
}]).factory('articleDataService',['$http', '$q', function($http, $q){
var data = null;
return {
getArticles: function() {
var promise;
if(data === null) {
promise = $http.get('articleData.json').then(function(response){
// cache the response
data = response;
return response;
});
} else {
var deferred = $q.defer();
// promise is immediately resolved with the cached data
// this way both cached and async data can be handled consistently
promise = deferred.promise;
}
return promise;
}
} ;
}]);
Plunkr link: http://plnkr.co/edit/7Oas2T
Upvotes: 4
Reputation: 104775
Change your service to:
articleDataService.getArticles = function() {
$http.get('articleData.json').then(function(result) {
return result.data
});
}
And in your controller:
articleDataService.getArticles().then(function(data) {
$scope.realArticleData = data;
$scope.custInfo = $scope.realArticleData.articles.thdCustomer;
});
You need to follow the promise pattern back to the controller, else you're not waiting for your call to finish and you'll be receiving undefined
Upvotes: 2