Dependency Injection and in-built services

18 Sep by vichu

Dependency Injection and in-built services

Reading Time: 9 minutes

Dependency Injection in AngularJS

The entire service concept in AngularJS is heavily dependent on and driven by its Dependency Injection system. Any service known to AngularJS (internal or our own) can be simply injected into any other service, directive, or controller by stating it as a dependency. AngularJS will automatically figure out what the service is, what it further depends on, and create the entire chain before injecting a fully instantiated service.

Dependency Injection is a concept that started more on the server side, to basically propagate reuse, modularity, and testability of code. Dependency Injection states that instead of creating an instance of a dependent service when we need it, the class or function should ask for it instead. Something else (usually known as an injector) would then be responsible for figuring out how to create it and pass it in. Consider a case where we had a service called $http that could make server calls. Now let’s take two cases, with and without Dependency Injection:

// Without Dependency Injection
function fetchDashboardData() {
var $http = new HttpService();
return $http.get('my/url');
}
// With Dependency Injection
function fetchDashboardData($http) {
return $http.get('my/url');
}

In the first function, a new instance of $http is created whenever a server call needs to happen. In the second, the $http service instance itself gets passed in.



What are the disadvantages of the former?
  • Because it creates a new instance using the new keyword, any test we write for this function is dependent on HttpService implicitly.
  • If we need to extend HttpService to provide for offline functionality, or change it to sockets, we will be forced to change each implementation where new is called.
  • It is inherently tied to HttpService, making it hard to reuse for other cases, such as with sockets or offline as mentioned previously.

Dependency Injection allows us to:

  • Change the underlying implemention of a dependency without manually changing each dependent function
  •  Change the underlying implementation just for the test, to prevent it from making server calls
  • Explicitly state what needs to be included and present before this function or constructor can execute AngularJS guarantees that the function we provide to the service declaration will be executed only once (lazily, the first time something that needs the dependency is loaded), and future dependents will get that very same instance. That is, AngularJS services are singletons for the scope of our application. Two controllers or services that ask for ServiceA will get the very same instance, instead of two different instances.

Using Built-In AngularJS Services

Before we go off and try to create our own services, let’s take a look at some existing core AngularJS services and how we might use them in our own applications. The simplest one we can start working with is the $log service.

The $ Prefix in AngularJS
AngularJS prefixes all the services that are provided by the AngularJS library with the $ sign. So you will see services like $log, $http, $window, and so on. This is used as a namespacing technique so that when you see a service, you can immediately figure out whether a service is coming from AngularJS or somewhere else. Conversely, when you create your own services, do not prefix them with a $ sign. It will just end up confusing you and your team at some point in time.

Before we can use any AngularJS service (in a controller, service, or otherwise), we need to inject it in. Let’s see how we can write a very simple controller that pulls in the $log service:

See the Pen log function by Vishal Srinivasan (@vishal_srini) on CodePen.0

In this example, the HTML has been simplified down to a single button, which triggers MainCtrl.logStuff(). It uses the ng-click directive that we’ve seen before.
Our first major change is in the controller definition. So far, we have had the name of the controller as the first argument to the controller() function, and an array with the controller definition inside it. Now when we depend on a service, we first add the dependency as a string in the array (this is what we call the safe style of Dependency Injection). After we declare it as a string, we then inject it as a variable (the name of our choosing) into the function that is passed as the last argument in the array.

AngularJS will pick up the individual strings in the array, look up the services internally, and inject them into the function in the order in which we have defined the strings. As soon as we have the service, we can use it as the API permits within our controller, so our logStuff function just logs a string to the console.

Safe Style of Dependency Injection

In the example, we defined our dependencies as follows:
myModule.controller(“MainCtrl”, [“$log”, function($log) {}]);
We could have also defined them as:
myModule.controller(“MainCtrl”, function($log) {});
That is, ditch the array syntax and directly provide our controller function. Why then would we go to this extra effort of typing in boilerplate if it doesn’t have any effect?
The reason for preferring the first syntax over the latter is that when we build our application for deployment, we often run our JavaScript through a step known as minification or uglification. In this step, our JavaScript is globbed into one single file, comments are dropped, spaces are removed, and finally, variables are renamed to make them shorter. So the $log variable might get renamed to xz (or some other random, shorter name). When we normally run our application and use the latter syntax (without the arrays), AngularJS is able to look at the name of the variable and figure out what service we need. When the uglification has finished, AngularJS has no clue what the variable xz previously referred to. The uglification and minification processes do not touch string constants. Therefore, the first example would get translated to something like:
myModule.controller(“MainCtrl”, [“$log”, function(xz) {}]);
while the latter example would translate to something like:
myModule.controller(“MainCtrl”, function(xz) {});
In the former, AngularJS still has the string “$log” to tell it what the service originally was, while it doesn’t have that in the latter. Recent developments like the ng-min library allow us to write code in the latter way and have it automatically convert to the former, but it can have edge cases. So it might be preferable to always use the safer style of Dependency Injection in case you don’t want to risk it.
In this blog, I will always use the safer style of Dependency Injection.

Order of Injection

We define our dependencies as strings. AngularJS inspects the strings, and injects the dependencies in the order in which they are listed:

myModule.controller(“MainCtrl”, [“$log”, “$window”, function($l, $w) {}]);

In this line of code, the $log service would be injected into the $l variable in the function, and the $window service would get injected into the $w variable:

myModule.controller(“MainCtrl”, [“$log”, “$window”, function($w, $l) {}]);

In this line of code, it is almost the exact same thing, except the $w and $l variables have been switched inside the function. AngularJS will ignore this and take its cue from the strings. So the $w variable will actually hold the $log service, and the $l variable would in fact hold the $window service.

So just be careful to keep the strings and the variables in sync and in the same order, or expect some craziness with your code. Common AngularJS Services



Some other AngularJS services that we will see or use on a common basis are:

$window

The $window service in AngularJS is nothing but a wrapper around the global window object. The sole reason for its existence is to avoid global state, especially in tests. Instead of directly working with the window object, we can ask for and work with $window. In the unit tests, the $window service can be easily mocked out (available for free with the AngularJS mocking library).

$location

The $location service in AngularJS allows us to interact with the URL in the browser bar, and get and manipulate its value. Any changes made to the $location service get reflected in the browser, and any changes in the browser are immediately captured in the $location service. The $location service has the following functions, which allow us to work with the URL:

absUrl

A getter that gives us the absolute URL in the browser (called $location.absUrl()).

url

A getter and setter that gets or sets the URL. If we give it an argument, it will set the URL; otherwise, it will return the URL as a string.

path

Again, a getter and setter that sets the path of the URL. Automatically adds the forward slash at the beginning. So $location.path() would give us the current
path of the application, and $location.path(“/new”) would set the path to /new.

search

Sets or gets the search or query string of the current URL. Calling $location.search() without any arguments returns the search parameter as an object. Calling $location.search(“test”) removes the search parameter from the URL, and calling $location.search(“test”, “abc”); sets the search parameter test to abc.

$http

We will deal with $http extensively in upcoming blog post, but it is the core AngularJS service used to make XHR requests to the server from the application. Using the $http service, we can make GET and POST requests, set the headers and caching, and deal with server responses and failures.

 



Byvichu

Love to code and Love to learn. A passionate technology lover who likes to sit with laptop even for weeks :D

Leave a Reply

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