AngularJS - Introduction to Services
AngularJS is a Javascript MVC framework from the fine folks over at Google. The focus of Angular is building complex HTML based client applications. Its design philosophy is data first, where your data will be updating the DOM. Contrast this to a framework like JQuery where the DOM will update your data.
This is the eight in a series of posts on AngularJS where we are using Chemistry data from the periodic table to help us understand the framework. The others posts are
- AngularJS - Introduction
- AngularJS - Introducing AngularJS Controllers
- AngularJS - Introducing NG-Repeat
- AngularJS - More with NG-Repeat
- AngularJS - Image Binding
- AngularJS - Introducing Templates
- AngularJS - Introducing Routing
- AngularJS - Introduction to Services
- AngularJS - Introduction to Directives
- AngularJS - Further with Directives
- AngularJS - Best Practices
- AngularJS - CSS Animations
Note: AngularJS does not allow for more than one ng-app directive. When I have multiple angular posts on the home page of my blog, only one application will work. I need to refactor the entire site to account for this. All of that to say this, you are best clicking on a single article so you can see the pages in action.
In AngularJS, when we want to create common code to be shared across our application, we create services. In the Angular world, the controller is the traffic cop, which directs data to your view for binding. Logic for retrieving that data falls to a service.
Services are stateless object that have shared functions that can be used in multiple controllers or views. The functions on services are also available throughout; they can be accessed in directives, controllers, filters, etc.
For an example of a real world service that I have used in the past…. When creating a select list in HTML for an AngularJS application, you usually have an ID associated with a selected element from the list. Often you will display all the properties of the JSON object. In JavaScript, to find this element, you need to loop through all the elements in an array until you get a match on the key. Depending on the size of your application, you end up writing this logic many, many times. To minimize this, I have written a helper application that creates an array that allows for an element to be accessed by a key value, thus reducing the need for repetitive array looping.
Generally, there are two ways to create services within your application. The most common is to use module.service
within
your application. The second is module.factory
. There are a couple of other ways, but we will skip those. AngularJS
services are really singleton objects. The object from services are then available across your application via dependency
injection, which we will look at soon.
The main difference between the two service creation methods is how they are used. The module.service
approach creates an
instance of a function. A good use case for this approach is the generic array lookup function mentioned above. The
module.factory
approach is that the returned value is returned by invoking a function reference. This essentially allows you
to treat the service like a class that you can new to make new instances.
The syntax for module.service
is
var chemistryApp = angular.module('chemistryApp', []);
//
chemistryApp.service('chemistryService', function(){
this.elementName= function(element){
return element.name;
};
});
//
function ChemCtrl($scope, chemistryService)
{
...
$scope.nameFromService = chemistryService.elementName(elements[0]);
}
The syntax for module.factory
is
var chemistryApp = angular.module('chemistryApp', []);
//
chemistryApp.factory('chemistryService', function(){
return {
nameFromService: function(element){
return element.name;
}
}
});
//
function ChemCtrl($scope, chemistryService)
{
...
$scope.nameFromFactory = chemistryService.elementName(elements[0]);
}
Once we create our service, we want to be able to use this within our application. This is done via the magical gremlins
that drive the Dependency Injection model in AngularJS. We just pass the service name to our controller when we instantiate
it. We showed this above, but just to be sure, by passing chemistryService
to the controller, it is service is
available within the
controller scope
function ChemCtrl($scope, chemistryService)
{
...
$scope.nameFromFactory = chemistryService.elementName(elements[0]);
}
Let's look at a more advanced scenario. I am pretty much stealing this demo from my Skyline Technologies colleague Brian Mahloch. Brian came up with a great demo for demonstrating services using the Periodic Data, which he kindly let me steal.
What we are going to do is determine the type of bonds two elements would make, based on the differences in their electronegativity. We will create a service that does two things, calculate the differences in electronegativty and then based on the difference determine the type of bond.
Our service then looks like
chemistryApp.service('chemistryService', function () {
this.calculateElectronegativityDifference = function (element1, element2) {
return Math.abs(element1.electronegativity - element2.electronegativity);
};
this.convertElectronegativityDifferenceToName = function (difference) {
if (difference > 2.0) {
return 'Ionic Bond';
} else if (difference >= 0.5 < 1.6) {
return 'Polar Covalent Bond';
} else {
return 'NonPolar Covalent Bond';
}
};
});
Our controller creation then gets updated so that we are injecting the service into the parameter list. Next, since $scope is our conduit for the view to talk to the service, we create a function on the controller that will consume the service when we have two elements selected.
chemistryApp.controller('chemServiceCtrl', ['$scope', 'chemistryService',
function chemServiceCtrl($scope, $log, chemistryService) {
$scope.elements = periodicData.elements;
$scope.calculateBondPolarity = function () {
if ($scope.selectedElement1 && $scope.selectedElement2) {
$scope.currentBondDifference = chemistryService.calculateElectronegativityDifference($scope.selectedElement1, $scope.selectedElement2);
$scope.currentBondType = chemistryService.convertElectronegativityDifferenceToName($scope.currentBondDifference);
}
};
/* private methods */
}]
);
Tying it all together, we have something like this.
We can now start to see how AngularJS provides a platform for creating web applications. With services, we are able to encapsulate logic and use it within multiple controllers in our application.
You can either visit http://angularperiodic.azurewebsites.net/ to see the code in action and as always find the code out on GitHub.