From Angular to Aurelia Part 2: Services
Continuing the series about the move from Angular 1.x to Aurelia the second part is all about explaining the transition from Angular’s Service concepts.
Services, Factories, … lazers, ninjas and foo
One of the biggest complaints I often hear about Angular 1.x is the crazy naming for it’s concepts. Instead of having one unified way to name a certain features you get a band-wagon full of terms and concepts, most of the time sounding more complicated then they actually are. So let’s start out by first analyzing the status-quo of Angular’s service concepts.
Mother of Services: The $provide object
To do so take a look at the following figure.
We start out with the $provide object which’s purpose is to register other components during the module configuration phase by leveraging the $injector service. It’s purpose is to create, load and retrieve a service instance. The actual creation of a service happens via service providers. Therefore $provide offers several methods, which more or less do the same thing, just on different abstraction levels. No matter which way you choose in the end the result is that Angular’s Dependency Injection cares about creating a singleton instance for your new service.
The Service Provider
The actual service provider is a function named provider which may be used like this:
1 | app.provider('Demo', function () { |
As you see the function takes the name of the new service as first argument, followed by function which should return a object containing at least the property $get
. This one is called during the initialization phase and should return the body of your service.
By looking at the syntax one can clearly understand that this isn’t actually the most convenient way to declare services nevertheless it offers the most flexibility. So the next way to create a service is by utilizing a service factory, which more or less may be understood as the service blueprint — the service body.
Creating a Service Blueprint
The service blueprint comes in two flavors. The first is by using a factory method the other by working with a service class. Now depending on who you ask there are wild speculations about why one approach is better then the other. Funny enough though they are essentially the same. Lets look at the implementation of the factory method.
1 | function factory(name, fn) { |
and here’s how you would create a service using it:
1 | app.factory('DemoFactory', function() { |
Note the example leverages the revealing module pattern
Next we’ll look at the implementation of the service method:1
2
3
4
5function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
Again followed by how you’d create your own service with it:
1 | var DemoClass = function() { |
So now that you’ve seen both of them you may judge by yourself. But it’s clear that in the end whatever you do, results in a provider. Also don’t let anybody fool you that you have to take X because Y is only doable that way :)
There is one scenario where using the service method is benefitial, which is when it comes to using ES6 classes with NG1. Take a look at my angular_es6 repo showing how you can leverage JSPM, Babel and Gulp to work with modern language features.
Services in Aurelia
I hope you’re still with me after reading through the Angular concepts, but be assured the Aurelia way is much shorter. Frankly it’s so short that the only thing you need to do is the following:
1 | export class MyService { |
and use it in your ViewModel like this:
1 | import {inject} from 'aurelia-framework'; |
You might start to ask yourself where the registration API, a provider or whatever else is. And exactly this is one of the core concepts of Aurelia. Hide the complexity and actually any evidence of the framework itself as much as possible. A service in it’s essence is nothing else then a singleton of a VanillaJS class. So by exporting it and thus making it visible for the rest of the app you’re already done. The requester just has to leverage Aurelia’s Dependency Injection, which by default creates singletons of each class registered, and have it inject the instance into your constructor.
Conclusion
I hope to have given you an idea of both concepts. Decide for yourself which approach you like better and let me know what you think about it in the comments below.
Photo credit: The Forgotten Memories Theatre via photopin (license)