AngularJS Services for Registering AngularJS Components

The $provide service is used to register components such as services so that they can be injected in order to satisfy dependencies (the $injector service does the actual injection, as I describe in the “Managing Injection” section later in this chapter). For the most part, the methods defined by the $provide service are exposed and accessed through the Module type, but there is one specialized method that is not available through Module that offers a useful, albeit niche, feature. Table 24-2 lists the methods defined by the $provide service.

The method that is not exposed via the Module type is decorator, which is used to intercept requests for a service in order to provide different or additional functionality. In Listing 24-1, you can see how I have used a decorator to alter the behavior of the $log service in a new HTML file called components.html that I added to the angularjs folder.

Listing 24-1. The Contents of the components.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Components</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

angular.module(“exampleApp”, [])

.config(function($provide) {

$provide.decorator(“$log”, function ($delegate) {

$delegate.originalLog = $delegate.log;

$delegate.log = function (message) {

$delegate.originalLog(“Decorated: ” + message);

}

return $delegate;

});

})

.controller(“defaultCtrl”, function ($scope, $log) {

$scope.handleClick = function () {

$log.log(“Button Clicked”);

};

});

</script>

</head>

<body ng-controller=”defaultCtrl”>

<div class=”well”>

<button class=”btn btn-primary” ng-click=”handleClick()”>Click Me!</button>

</div>

</body>

</html>

This example application consists of a button that uses the ng-click directive to trigger a scope behavior called handleClick, which writes a message to the console using the $log service that I described in Chapter 19.

I have highlighted the important part of this example, which is contained in a call to the Module.config method (described in Chapter 9). My configuration function declares a dependency on the $provide service, which allows me to call the decorator method.

The arguments to the decorator method are the name of the service that you want to decorate (expressed as a literal string) and a decorator function that must declare a dependency on $delegate, which is used to pass the original service to your function.

Tip You must use a string value for the first argument to the decorator method, such as “$log” and not just $log. This argument tells AngularJS which service you want to decorate and is not used to declare a dependency.

In my example, I set the first argument to “$log“, which tells AngularJS that I want to decorate the $log service that I described in Chapter 19. This means AngularJS will instantiate the $log service object and pass it as the $delegate argument of the decorator function. Within the decorator function, I am free to make whatever changes I want to the $delegate object, and the result that I return will be used to resolve dependencies on the $log service when it is required in other parts of the application.

Tip Your decorator function must return the object you want used to resolve dependencies for the service you specified. If you don’t return a value, then dependencies will be resolved with the JavaScript undefined value.

Here are the decorations I made to the service in this example:

$provide.decorator(“$log”, function ($delegate) {

$delegate.originalLog = $delegate.log;

$delegate.log = function (message) {

$delegate.originalLog(“Decorated: ” + message);

}

return $delegate;

});

I rename the log method so that it is called originalLog and add a new log method that prepends the word Decorated to the log message. You can see the effect by starting the application, clicking the button, and looking at the output in the JavaScript console:

Decorated: Button Clicked

You can change a service in any way that you want, but you must remember that the object you return from your decorator function will be passed to components that already have an expectation about the nature of the service object. There is no point, for example, in renaming the log method in the $log service so that it is called detailedLog because no component that declares a dependency on the $log service will expect a method of that name and will continue to use the original method name. As a consequence, I find that decorating services is most useful for making small adjustments—most often for writing a message to the JavaScript console when a service method is called, which can be helpful when debugging complex problems.

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *