Services
A controller's job is to wire a template to some data — it shouldn't also be the place where you fetch that data from a server or contain business logic that has nothing to do with the view. AngularJS's answer is the service: a plain object, shared across the whole app, that a controller can ask for by name.
$http: AngularJS's built-in AJAX service
$http is a service AngularJS provides out of the box for making requests. Like $scope, it's injected into your controller just by naming it as a parameter:
app.controller('UsersController', function($scope, $http) { $scope.users = []; $http.get('/api/users').then(function(response) { $scope.users = response.data; }); });
$http.get() sends a GET request to /api/users and returns a promise. When the response arrives, .then()'s callback runs and assigns the parsed JSON body to $scope.users — any ng-repeat bound to it re-renders automatically.
Notice that AngularJS is finding $http for you just because the controller function's parameter is named $http — this is dependency injection: you declare what you need by name, and AngularJS supplies it, rather than you importing or instantiating it yourself.
Writing a custom service
Once logic needs to be shared between more than one controller — formatting rules, cached data, anything that isn't view-specific — pulling it into a service keeps controllers small and that logic reusable:
app.service('CartService', function() { var items = []; this.add = function(item) { items.push(item); }; this.getTotal = function() { return items.reduce(function(sum, i) { return sum + i.price; }, 0); }; }); app.controller('CheckoutController', function($scope, CartService) { CartService.add({ name: 'Mug', price: 12 }); CartService.add({ name: 'Notebook', price: 8 }); $scope.total = CartService.getTotal(); });
$scope.total is 20
CartService is requested by name in CheckoutController's parameter list, the same way $http was — any controller that lists CartService as a parameter gets access to exactly the same cart, not a fresh copy.
CartService's items array stays populated across the whole application rather than resetting per controller. This is powerful (it's how you share state cleanly) and also a footgun: if a service holds data that should really be request-scoped or user-scoped, singleton state can leak between places you didn't intend, especially in a large app with many controllers touching the same service.