Rails
Learn to Build Modern Web Apps with AngularJS and Ruby on Rails

Creating Angular Services

Make services in AngularJS in order to reuse code and keep a source of truth between controllers.

Up to this point, we've been storing important data directly in the controller. While this works, it has some disadvantages:

  • when the controller goes out of scope, we lose the data
  • that data cannot be easily accessed from other controllers or directives
  • the data is difficult to mock, which is important when writing automated tests

To rectify this problem, we're going to refactor our $scope.posts variable into a service.

My First Service... Is Really a Factory

In Angular, services are declared much like controllers. Inside app.js, we're going to attach a new service to our flapperNews module.

Create a factory for posts in app.js (our MainCtrl controller should appear below this):
angular.module('flapperNews', [])
.factory('posts', [function(){
  // service body
}])

By Angular conventions, lowerCamelCase is used for factory names that won't be new'ed.

You may be wondering why we're using the keyword factory instead of service. In angular, factory and service are related in that they are both instances of a third entity called provider.

The difference between them are nuanced but if you'd like to learn more check out Angular Factory vs Service vs Provider.
Now that we've created our service, lets go ahead and move our posts variable to it:
.factory('posts', [function(){
  var o = {
    posts: []
  };
  return o;
}])

What we're doing here is creating a new object that has an array property called 'posts'. We then return that variable so that our o object essentially becomes exposed to any other Angular module that cares to inject it. You'll note that we could have simply exported the posts array directly, however, by exporting an object that contains the posts array we can add new objects and methods to our services in the future.

Injecting the Service

Our next step is to inject the service into our controller so we can access its data. Simply add the name of the service as a parameter to the controller we wish to access it from:

Inject posts service into MainCtrl
.controller('MainCtrl', [
'$scope',
'posts',
function($scope, posts){...

As you remember, you can only have two way data binding with scope variables. To display the array of our posts that exist in the posts factory (posts.posts), we'll need to set a scope variable in our controller to mirror the array returned from the service.

Bind the $scope.posts variable in our controller to the posts array in our service:
$scope.posts = posts.posts;

Now any change or modification made to $scope.posts will be stored in the service and immediately accessible by any other module that injects the posts service.