Sharing Code Between AngularJS Controllers, Part I

Typically, there are several ways to share methods among controllers. The officially recommended way is using services. The main idea and process is defining methods in services and the controllers get the services injected by adding it to the controller function as params.

But wait, what happens if several controllers share the same piece of code asynchronously? i.e, Whether or not “race” will happen if several controllers access the “same” method?

Finally we will have another method helps share code between controllers, which is not that standard, but will also show us another option in the development.

Let’s begin!


# Using Service

The standard way to share code between controllers is using services. Actually service is also the recommended way by the AngularJS officially. In the AngularJS documentation, we can find a perfect example to show how to do that with services. The key word I think using service is better to be “dependency injection” which helps how controllers get hold of their dependencies.

Wait, Answer me the first question!

OK. let’s take a look at the following jsfiddle demo first:


<div ng-controller="HelloCtrl">
    <p></p>
    <p></p>
</div>
<br />
<div ng-controller="GoodbyeCtrl">
    <p></p>
    <p></p>
</div>

//angular.js example for factory vs service
var app = angular.module('myApp', []);

app.factory('testFactory', function(){
    return {
        sayHello: function(){
    	var i = 100;
      var values = [];
    	while(i > 0 ){
       values.push(i);
       i--;
      }
       return values;
    }
    }               
});

app.service('testService', function(){
    this.sayHello= function(){
    	var i = 100;
      var values = [];
    	while(i > 0 ){
       values.push(i);
       i--;
      }
       return values;
    };          
});

function HelloCtrl($scope, testService, testFactory)
{
    $scope.fromService = testService.sayHello();
    $scope.fromFactory = testFactory.sayHello();
}

function GoodbyeCtrl($scope, testService, testFactory)
{
    $scope.fromService = testService.sayHello();
    $scope.fromFactory = testFactory.sayHello();
}

Run this demo, we can see, no matter we inject AngularJS service or factory (what’s the difference? See here), we always have the array printed correctly.

But the “instinct” keeps saying: if two controllers are accessing the same piece of code and variables, the result will be messed up. I will say, it is right, but also wrong.

If we have a variable i = true and process A prints i every second which will last 1 minute, and during A’s execution, process B comes in and changes the value to false, after B’s operation, the printed values by A will definitely be false, false, false..... This is called race.

In distributing computing, sometimes we have to share the same variable between processes/threads, if so, then we need to apply the so-called lock to guarantee the mutual exclusion or build a more complex non-blocking algorithm to exclude such race condition.

But wait, is there any simple way to do that?

Yes, we have. Just clone a new one. Say var j = i; and let process A prints j which has exactly the same value as i. Then no matter how B changes the value of i, the result of A’s execution will be the same.

So now, informally we could imagine that the two controllers are the above two processes. The service they injected are actually two seperate instances or two clones of service i.

In the AngularJS service document, we have the above example abstracted as two properties:

Angular services are:

  • Lazily instantiated – Angular only instantiates a service when an application component depends on it.
  • Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.

So, no worries about the asynchronization.