As one learns a lot in 18 months, I would like to update my answer. The below example is how I would solve the problem today. (See bottom for original solution)
function dataFactory($http) {
//Service interface, all properties and methods will be set to this object.
var dataFactory={};
//Instead of using $q, the function will just return the http-promise containing the response data.
dataFactory.getItems=function() {
return $http
.get('data.json')
.then(function(response) {
return response.data;
});
}
//Return object containing the service interface.
return dataFactory;
}
//Use $inject property to specifiy your DI objects, rather than using array syntax.
dataFactory.$inject=['$http'];
function firstController(DataFactory) {
//Use this with controllerAs instead of injecting $scope.
var vm=this;
vm.items=[];
DataFactory
.getItems()
.then(function(items) {
vm.items=items;
}, function(err) {
//error handler
alert("Got an error");
})
}
//same here, use $inject property.
firstController.$inject=['dataFactory'];
angular
.module('testApp', [])
.factory('DataFactory', dataFactory)
.controller('FirstController', firstController);
And the HTML
<!-- Use controller as syntax -->
<div ng-controller="firstController as first">
<ul>
<!-- Reference the controller by value given in controller as statement -->
<li ng-repeat="item in first.items">{{item.name}}</li>
</ul>
</div>
This is how I would write the code. However, this is not how I would solve it. I wouldn't make any changes the data service, But I would change the implementation of the controller. Either I would resolve the items data through the router, or I would bundle the controller and html as a directive, or as of 1.5 a component.
Using a directive
function itemsDirective() {
function controller(DataFactory) {
var vm=this;
vm.items=[];
DataFactory
.getItems()
.then(function(items) {
vm.items=items;
}, function(err) {
//error handler
alert("Got an error");
})
}
controller.$inject=['dataFactory'];
return {
restrict:'E',
template:'<div ng-controller="firstController as first">
<ul>
<li ng-repeat="item in first.items">{{item.name}}</li>
</ul>
</div>',
controller: controller,
controllerAs: 'first'
}
}
angular
.module('testApp')
.directive('itemsDirective', itemsDirective);
Old answer (Jul 23 '14 at 20:26)
Because the value doesn't get set before it returns it's value. Having that said you might wanna restructure your Service(factory), also use $q to handle the promise. Consider the following example:
var App = angular.module('testApp', []);
App.factory('DataFactory', ['$http', '$q', function($http, $q) {
var getItems = function() {
var deffered = $q.defer();
$http.get('data.json').success(function(data) {
deffered.resolve(data);
});
return deffered.promise;
};
return {
getItems: getItems
};
}]);
App.controller('firstCtrl', ['$scope', 'DataFactory',function($scope, DataFactory) {
$scope.items = DataFactory.getItems();
}]);
It's common practice to use $q while working with async tasks such as http-request.