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, if you have not had a look at our previous articles of this series, go through the following links.
In the previous articles, we discussed the basic structure of AngularJS, including the 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 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, which means AngularJS only instantiates a service when an application component depends on it. At the same time, an angularjs service is a singleton object, which means each component of the services dependent on a service gets a reference to the single instance generated by the service factory. AngularJS offers several useful services, such as $http. But, like filters, we can create our 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. 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 the Model-View-Controller pattern as we discussed in the …. Article. Services are mainly used to cross-cut 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 object that provides functionality that can be used throughout the AngularJS application –. Still, 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 |
$animate |
Animates 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 |
$document |
Provides a jqLite object that contains the DOM window.document object. |
$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 |
$interpolate |
Processes a string that contains binding expressions to create a function that can be used to generate content. |
$interval |
It provides an enhanced wrapper around the window.setInterval function. |
$location |
It 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 to generate content. |
$provide |
Implements 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’s URL path. |
$routeParams |
Provides information about URL routes. |
$sanitize |
Replaces dangerous HTML characters with their display-safe counterparts. |
$swipe |
Recognizes swipe gestures. |
$timeout |
It provides an enhanced wrapper around the window.setTimeout function. |
$window |
Provides a reference to the DOM window object. |
I will discuss the in-built services of AngularJS in a later article. The main focus of this article is 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. To do this, we create three files as follows.
ServiceApp.Js
var serviceApp = angular.module('ServiceApp', []);
serviceApp.factory("logService", function() {
var messageCount = 0;
return {
log: function(msg) {
console.log("(LOG + " + messageCount++ + ") " + msg);
}
};
});
In the above file, I first create an angular module named serviceApp to define the factory service, which creates a log message on execution.
App.js
var testApp = angular.module('TestApp', ['ServiceApp']);
Now, I will define another angular module named test app in which I inject the ServiceApp module. This test app module will be used from the html page for the controller.
Factory.html
<!DOCTYPE html>
<html ng-app="TestApp">
<head>
<title>AngularJS Factory</title>
<script src="angular.js"></script>
<link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />
<script src="serviceApp.js"></script>
<script src="app.js"></script>
<script src="Factory.js"></script>
</head>
<body ng-controller="FactoryController">
<div class="well">
<div class="btn-group" tri-button counter="data.totalClicks" source="data.device">
<button class="btn btn-default" ng-repeat="item in data.device">
{{item}}
</button>
</div>
<h5>Total Clicks: {{data.totalClicks}}</h5>
</div>
</body>
</html>
Factory.js
testApp.controller('FactoryController', function ($scope, logService) {
$scope.data = {
device: ["Mobile", "Laptops", "IPad"],
totalClicks: 0
};
$scope.$watch('data.totalClicks', function (newVal) {
logService.log("Total click count: " + newVal);
});
});
testApp.directive("triButton", function (logService) {
return {
scope: { counter: "=counter" },
link: function (scope, element, attrs) {
element.on("click", function (event) {
logService.log("Button click: " + event.target.innerText);
scope.$apply(function () {
scope.counter++;
});
});
}
};
});
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 changed the code of the serviceapp.js file.
var serviceApp = angular.module('ServiceApp', []);
var baseLogger = function () {
this.messageCount = 0;
this.log = function (msg) {
console.log(this.msgType + ": " + (this.messageCount++) + " " + msg);
};
};
var debugLogger = function () { };
debugLogger.prototype = new baseLogger();
debugLogger.prototype.msgType = "Debug";
var errorLogger = function () { };
errorLogger.prototype = new baseLogger();
errorLogger.prototype.msgType = "Error";
serviceApp.service("logService", debugLogger);
serviceApp.service("errorService", errorLogger);
Another approach to defining the service objects with the service method is by using prototype and constructor functions. For this, the first thing I have to do is 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 debugger 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 an error logger. 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 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.
var serviceApp = angular.module('ServiceApp', []);
serviceApp.provider("logService", function() {
var counter = true;
var debug = true;
return {
messageCounterEnabled: function(setting) {
if (angular.isDefined(setting)) {
counter = setting;
return this;
} else {
return counter;
}
},
debugEnabled: function(setting) {
if (angular.isDefined(setting)) {
debug = setting;
return this;
} else {
return debug;
}
},
$get: function() {
return {
messageCount: 0,
log: function(msg) {
if (debug) {
console.log("(LOG" + (counter ? " + " + this.messageCount++ + ") " : ") ") + msg);
}
}
};
}
}
});