AngularJS Services and cross controller communication & data persistence

With each day that passes application expectations seem to grow and grow as do users expectations for them. As a developer, we are looking for the most efficient ways of doing this. Techniques like ajax and frameworks like Angular are means to accomplish some of these tasks.

As great as these frameworks and tools are there is one pressing issue that we face. A lot of the cool, cutting edge tools we have aim to solve common problems though they have different ways of doing the same thing. It isn't always simple to figure out the correct way to do something with them either.

In this post we'll take a quick look at what angular services are and how to allow different parts of your app communicate together to share data.

What is a Service Class?

The term "service" is used in many connotations in development. You have service classes, web services, services on your server, on your computer and on and on. In order to understand this fully let's take a step back from Angular, from JavaScript even, and look at what a service class really is. In a general programing sense, a service class is a re-useable group of code whose purpose doesn't fit exactly into another area even though it might be related to that area.

Let's break that crystal clear definition down into plain english. You can think of a service class as a helper class or a library. For instance, you may have a users model. It's responsibility is to crud the users table. (Create, Read, Update, and Delete). It isn't responsible for grouping results of users based on query criteria, it isn't responsible for doing text manipulation on the name of the user ect. That stuff would probably be better off being in the a UserService.

Services

In Angular, controllers aren't really the place to be doing data manipulation because when you change routes, refresh the page, or anything similar, the controllers are blown away for memory efficiency. Thus any data or state stored in the controller will get reset with each change. Furthermore, in angular it is considered bad practice to manipulate the DOM or to do too much data manipulation directly in the controller. Instead, the controller delegates this task to a service. The core responsibilities of services are: holding logic and behavior, performing DOM manipulation, and storing globally accessible application data stores.

The diffrent ways to make a service

There are three main ways to make a service in angular. Those methods include the following ways: Factory, Service, Provider. The main difference with these methods is just in the way that they make and return the service.

1) Factory

With a factory, you actually create an object inside the factory function and return it. It's kind of like the module revealing pattern.

This factory method for creating a service looks something like this:

var myModule = angular.module('myModule', []);

myModule.controller('MyController', ['$scope','notify', function ($scope, notify) {  
   $scope.callNotify = function(msg) {
     notify(msg);
   };
 }]);

myModule.factory('notify', ['$window', function(win) {  
   var msgs       = [];
   var factory    = {};

   factory.addMsg = function(msg){
       msgs.push(msg);
       if (msgs.length > 1) {
         win.alert(msgs.join("\n"));
         msgs = [];
       }
   } 

   return factory;
 }]);

* adapted slightly from the angularjs site

Using this method, a function or an object is returned that is the service. If an object is returned, it is the public API through which the service communicates.

2) Next up is the service method

This method of setting things up builds the service as if it were making an object from a constructor function. Using this method, you first define a method that will act as a constructor. Then you attach your service to the module by calling the service method.

//..
myModule.service('notify', ['$window', function(win) {  
   var msgs = [];
   this.message = function(msg) {
     msgs.push(msg);
     if (msgs.length == 3) {
       win.alert(msgs.join("\n"));
       msgs = [];
     }
   };
 }]);

In the example above, we used the array notation of Angular, with the last parameter being the method that comprises the service's constructor function. With this guy, The public API is comprised of whatever is added to it's instance using the this keyword.

When this method is used angular first checks to see if the service instance exists. If so, it returns the preexisting service. If not, it creates a singleton instance of the service and returns that. Using this method you can still maintain private scope like with the factory method above by using variables that are declared inside the service but not attached to the instance.

3. The Provider method

The final way we will discuss to make angular services is the provider method. This method creates a service proiThe provider method is a great candidate when you need to do some configuration before your service is instantiated.
With this method there is a $get function that you define that is used to get the object that returns the data.

Protip: factory and service are just shortcut methods to provider.

Resources:

AngularJS Fundamentals
ng-book