Learn AngularJS From Beginning: Service - Part Three

In this article, we will discuss the basics of the AngularJS service. This is part three of the article series.

I am here to continue the discussion around AngularJS. Today, we will go through one of the main features of the AngularJS i.e., filter. Also in case you have not had a look at our previous articles of this series, go through the following links:

In the previous articles, we discuss the AngularJS basic structure including control and model. Also we discuss how to use the built in filter and how to create custom filters. Now, in this article, we will discuss about service or factory in Angular JS.

What is Service in Angular JS?

Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your application. AngularJS services are lazily instantiated means AngularJS only instantiates a service when an application component depends on it. At the same time, angularjs service is a singleton objects means each component of the services dependent on a service gets a reference to the single instance generated by the service factory. AngularJS offer several useful services like $http etc. But like filters, we can create own service as desired.

Actually services underlie everything; they provide cross-cutting functionality, request and manipulate data, integrate with external services, and incorporate business logic, and yet they're as simple as a JSON object. Services are like the foundation of a building. Sure, a building can be built without a foundation, but it won't last for long. And without services in your application, it will soon become so unwieldy that it too will not last for long. The following figure shows the AngularJS components and their interactions:

When and Why to use and create Services

Services are used to encapsulate functionality that we need to reuse in an application but don’t fit clearly into Model-View-Controller pattern as we discussed in the …. Article. Services are mainly used for the purpose of cross cutting concerns. The AngularJS Module defines three methods for defining services : factory, service and provider. The result of using these methods is the same – a service objects that provides functionality that can be used throughout the AngularJS application – but the way that the service object is created and managed by each method is slightly different. Below I mentioned the inbuilt services of AngularJS.

Name Descriptions
$anchorScroll Scrolls the browser window to a specified anchor
$animateAnimates the content transitions.
$compile Processes an HTML fragment to create a function that can be used to generate content.
$controller A wrapper around the $injector service that instantiates controllers
$documentProvides a jqLite objects that contains the DOM window.documentobject.
$exceptionHandler Handles exceptions that arise in the application.
$filter Provides access to filters
$http Creates and manages Ajax requests
$injector Creates instances of AngularJS components
$interpolateProcesses a string that contains binding expressions to create a function that can be used to
generate content.
$interval Provides an enhanced wrapper around the window.setInterval function.
$location Provides a wrapper around the browser location object.
$log Provides a wrapper around the global console object.
$parse Processes an expression to create a function that can be used togenerate content.
$provideImplements many of the methods that are exposed by Module.
$q Provides deferred objects/promises.
$resource Provides support for working with RESTful APIs.
$rootElement Provides access to the root element in the DOM.
$rootScope Provides access to the top of the scope hierarchy
$route Provides support for changing view content based on the browser’sURL path.
$routeParams Provides information about URL routes.
$sanitize Replaces dangerous HTML characters with their display-safecounterparts.
$swipe Recognizes swipe gestures.
$timeout Provides an enhanced wrapper around the window.setITimeout function.
$window Provides a reference to the DOM window object.

I will discuss the about this in-built services of AngularJS in later article. The main focus of this article is that what are the different ways to create custom services as per our requirement in AngularJS. 

Using Factory method

The simplest method of defining a service is to use the Module.factory method, passing an argument, the name of the service and a factory function that returns the service objects. For doing this, we create three files as follows :- 

  1. var serviceApp = angular.module('ServiceApp', []);  
  3. serviceApp.factory("logService"function () {  
  4.     var messageCount = 0;  
  5.     return {  
  6.         log: function (msg) {  
  7.             console.log("(LOG + " + messageCount++ + ") " + msg);  
  8.         }  
  9.     };  
  10. });  
In the above file, I first create an angular module named serviceApp for define the factory service which create log message on execution.
  1. var testApp = angular.module('TestApp', ['ServiceApp']);  
Now, I define another angualr module named testApp in which I inject the ServiceApp module. This testApp module will be used from html page for controller.
  1. <!DOCTYPE html>  
  2. <html ng-app="TestApp">  
  3. <head>  
  4.     <title>AngularJS Factory</title>  
  5.     <script src="angular.js"></script>  
  6.     <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />  
  7.     <script src="serviceApp.js"></script>  
  8.     <script src="app.js"></script>  
  9.     <script src="Factory.js"></script>  
  10. </head>  
  11. <body ng-controller="FactoryController">  
  12.     <div class="well">  
  13.         <div class="btn-group" tri-button counter="data.totalClicks" source="data.device">  
  14.             <button class="btn btn-default" ng-repeat="item in data.device">  
  15.                 {{item}}  
  16.             </button>  
  17.         </div>  
  18.         <h5>Total Clicks: {{data.totalClicks}}</h5>  
  19.     </div>  
  20. </body>  
  21. </html>  
  1. testApp.controller('FactoryController'function ($scope, logService) {  
  2.     $scope.data = {  
  3.         device: ["Mobile""Laptops""IPad"],  
  4.         totalClicks: 0  
  5.     };  
  7.     $scope.$watch('data.totalClicks'function (newVal) {  
  8.         logService.log("Total click count: " + newVal);  
  9.     });  
  11. });  
  15. testApp.directive("triButton"function (logService) {  
  16.     return {  
  17.         scope: { counter: "=counter" },  
  18.         link: function (scope, element, attrs) {  
  19.             element.on("click"function (event) {  
  20.                 logService.log("Button click: " + event.target.innerText);  
  21.                 scope.$apply(function () {  
  22.                     scope.counter++;  
  23.                 });  
  24.             });  
  25.         }  
  26.     }  
  27. });  

The output is as follows:

I have added a script element to import the services.js file into the HTML document, which ensures that the service is available for use. After that, it is simply a matter of adding an argument to the factory function of the controller to declare its dependency on the service. The name of the argument must match the name used to create the service because AngularJS inspects the arguments of factory functions and uses them to perform dependency injection. That means you can define the argument in any order, but it does prevent you from picking your own argument names.

Using the Service Method

The Module.service method also creates service objects, but in a slightly different way. When AngularJS needs to satisfy a dependency for a service defined by the factory method, it simply uses the object returned by the factory function, but for a service defined with the service method, AngularJS uses the object returned by the factory function as a constructor and uses the JavaScript new keyword to create the service object. The new keyword isn’t widely used in JavaScript development, and when it is used, it causes a lot of confusion.

Because most developers are familiar with the class-based inheritance used by languages such as C# and Java and not the prototype-based inheritance used by JavaScript.

For this we just change the code of serviceapp.js file, 
  1. var serviceApp = angular.module('ServiceApp', []);  
  3. var baseLogger = function () {  
  4.     this.messageCount = 0;  
  5.     this.log = function (msg) {  
  6.         console.log(this.msgType + ": " + (this.messageCount++) + " " + msg);  
  7.     }  
  8. };  
  9. var debugLogger = function () { };  
  11. debugLogger.prototype = new baseLogger  
  13. debugLogger.prototype.msgType = "Debug";  
  15. var errorLogger = function () { };  
  17. errorLogger.prototype = new baseLogger();  
  19. errorLogger.prototype.msgType = "Error";  
  20. serviceApp.service("logService", debugLogger);  
  21. serviceApp.service("errorService", errorLogger);  
Another approach to define the service objects with service method is by using prototype and constructor function. For this the first thing I have to create a constructor function, which is essentially a template for defining functionality that will be defined on new objects. My constructor function is called baseLogger, and it defines the messageCount variable and the log method you saw in the previous section. The log method passes an undefined variable called msgType to the console.log method, which I’ll set when I use the baseLogger constructor function as a template.
The next step I take is to create a new constructor function called debugLogger and set its prototype to a new object created using the new keyword and the baseLogger keyword. The new keyword creates a new object and copies the properties and functions defined by the constructor function to the new object. The prototype property is used to alter the template. I call it once to ensure that the debugLogger constructor inherits the property and method from the baseLogger constructor and again to define the msgType property. The whole point of using constructors is that you can define functionality in the template once and then have it applied to multiple objects—and to that end, I have repeated the process to create a third constructor function called errorLogger. The use of the new keyword in both cases means that I define the messageCount property and the log method once but have it apply to both to objects that are created by the debugLogger and errorLogger constructors and the objects that are created from them. To finish the example, I register the debugLogger and errorLogger constructors as services. Notice that when I pass the constructors to the service method. AngularJS will call the new method to create the service objects. 

Note that I didn’t make any changes in the controller or directive since they only communicate with the service.

 Using the Provider Method

The Module.provider method allows you to take more control over the way that a service object is created or configured. The arguments to the provider method are the name of the service that is being defined and a factory function. The factory function is required to return a provider object that defines a method called $get, which in turn is required to return the service object. When the service is required, AngularJS calls the factory method to get the provider object and then calls the $get method to get the service object. Using the provider method doesn’t change the way that services are consumed, which means that I don’t need to make any changes to the controller or directive in the example. They continue to declare the dependence on the service and call the service method of the service object they are provided with. The advantage of using the provider method is that you can add functionality to the provider method that can be used to configure the service object.

To demonstrate this process, I again change the serviceapp.js file as below- 
  1. var serviceApp = angular.module('ServiceApp', []);  
  3. serviceApp.provider("logService"function () {  
  4.     var counter = true;  
  5.     var debug = true;  
  6.     return {  
  7.         messageCounterEnabled: function (setting) {  
  8.             if (angular.isDefined(setting)) {  
  9.                 counter = setting;  
  10.                 return this;  
  11.             } else {  
  12.                 return counter;  
  13.             }  
  14.         },  
  15.         debugEnabled: function (setting) {  
  16.             if (angular.isDefined(setting)) {  
  17.                 debug = setting;  
  18.                 return this;  
  19.             } else {  
  20.                 return debug;  
  21.             }  
  22.         },  
  23.         $get: function () {  
  24.             return {  
  25.                 messageCount: 0,  
  26.                 log: function (msg) {  
  27.                     if (debug) {  
  28.                         console.log("(LOG" + (counter ? " + " + this.messageCount++ + ") " : ") ") + msg);  
  29.                     }  
  30.                 }  
  31.             };  
  32.         }  
  33.     }  
  34. });