Primer on AngularJS Services
Contents
What’s a service …
Much to my surprise, the Angular documentation provides a great definition of a service:
Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.
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.
Angular offers several useful services (like $http), but for most applications you’ll also want to create your own.
Services are powerful in that they help keep your code DRY by encapsulating functionality. From an architecture standpoint alone, services help separate out concerns, ensuring that each object is responsible for a single piece of functionality. For example, it’s common for beginners to put all of their app’s functionality into the controller. This is fine for smaller apps, but just know that it’s not a good practice and your controller will balloon quickly as your app scales.
Get in the habit early on to separate concerns. If your controller is handling more than just defining the scope or initial state of your app, connecting your models and views, then it’s are probably doing too much.
We are all (err, I am) guilty of this. Let’s look at a very simple app …
HTML:
<!doctype html>
<html lang="en" ng-app='myApp'>
<head>
<meta charset="UTF-8">
<title>Angular Boilerplate</title>
<!-- styles -->
<link href="http://netdna.bootstrapcdn.com/bootswatch/3.1.1/yeti/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="main.css" rel="stylesheet" media="screen">
</head>
<body>
<div class="container">
<div ng-controller="myController">
<h1>Enter Quantity:</h1>
<input type="number" ng-model="quantity"></p>
<h2>Total Cost: {{calculate(quantity) | currency}}</h2>
</div>
</div>
<!-- scripts -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" type="text/javascript"></script>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="main.js" type="text/javascript"></script>
</body>
</html>
Javascript:
var app = angular.module('myApp', [])
app.controller('myController', function($scope) {
$scope.quantity = 100;
$scope.calculate = function(number) {
return number * 10;
}
});
So, this just takes an input value (integer or floating point) and multiplies it by 10 in the calculate()
function, which then updates the DOM. Not only is the controller defining scope - but it also calculates the total. Despite this being a small app, too much is happening in the controller. We should separate out the calculate function()
into a separate service.
Creating a custom service
By moving the business logic out of the controller, abstracting much of the code, our controller becomes leaner. It’s a good practice to write fat services and lean controllers.
To do this, we are will use a service type called a factory, which is the most common type.
This is a good time to stop and learn the major service types - constants, values, services, providers, and decorators. Check out this excellent article for more on the various service types and how and when to use them. All are slightly different, but, in general, all are dependency injected modules of functionality.
Within the same JS file add the following code beneath the controller:
// Service
app.factory('calculateService', function(){
return {
calculate: function(number){
return number * 10
}
}
});
This code creates a service called calculateService
. You may be wondering why we have to use the factory()
method for this instead of just a regular function. It’s simple: That method registers the service with Angular; and with Angular aware of its existence, it can be dependency injected into the controller, giving us access to the defined functions - e.g, calculate()
within the controller. We can now use this in multiple places within our application, allowing for easy code reuse.
So, we have simply abstracted the logic of taking the user inputted number and multiplying it by 10.
Now update the controller:
app.controller('myController', function($scope, calculateService) {
$scope.quantity = 100;
$scope.calculate = function(number) {
return calculateService.calculate(number);
}
});
And you’re app should be working. Test it out. JSFiddle
Conclusion
Hopefully, you now have a better sense as to -
- What a service is,
- Why you should use them, and
- How to use them.
Want some practice? Create separate services for each piece of functionality in this app’s controller. Remember: The controller is responsible for defining scope, all else should be moved out of the controller altogether.
If you need help, start by creating a service that handles the actual API calls. Perhaps use a service name of getData
then set up functions for the different HTTP requests - i.e., readData()
for a GET request and writeData()
for a POST. Then when you use dependency injection to add this service to your controller, you can simply use the following syntax for accessing the readData()
function in the controller:
getData.readData(some_argument)
Presumably you would pass in an argument supplied by the user. Now you can access that function from the controller without knowing anything about the actual service except for how you use it. The controller is cleaner because you abstracted out all the messy code for making API calls.
Good luck!