There is a lot of confusion over the difference between the angular’s factory, service and provider methods. This article explains the different ways that an Angular service can be created.
The official Angular 1.4.0 documentation page describes services as: “…substitutable objects that are wired together using dependency injection (DI)” Stating: “You can use services to organize and share code across your app.” While a bit dry, I think this is actually a perfect description. I think that many who are new to Angular are confused about which of the many methods available should be used when creating a service. Fortunately, the answers are fairly simple and straightforward.
Five Ways to Create an Angular Service
The angular.value method
The value method, one the most overlooked approaches, is most commonly used to turn a global into an injectable Angular service. While the approach is simple, the application is powerful: the value of your service is testable and can be shared across your application.
Example # 1A
1 2 3 |
var app = angular.module('myApp', []); app.value(“speed”, 100); |
Example # 1B
1 2 3 |
var app = angular.module('myApp', []); app.value(“teamMember”,{title: ‘Project Manager’}); |
Examples 1A and 1B use the exact same syntax when creating an Angular service. The only difference is their value. Example # 1A’s “speed” service is a primitive: the number 100. Example # 1B’s value is an object with a “title” property.
The angular.constant method
This works the same as the angular.value method. The difference is that it is available during the configuration phase of your Angular application. Also, the value of a this constant can never be changed.
Example # 2
1 2 3 |
var app = angular.module('myApp', []); app.constant(“MAX_ITERATIONS”, 5); |
The angular.factory method
The angular.factory method takes two arguments: a string and a function. The string represents the name of the service, which will be used to gain access to this service (e.g. to inject this service into a controller as a dependency). The anonymous function that you provide as the second argument provides the actual implementation. When you register your service, Angular will execute this function.
Its return value is cached and will be shared with any component of your application that injects the service. It is important to note that this function will only be executed once.
This is the scenario most are thinking of when they intend to create an Angular service, and probably the most common approach.
Example # 3
1 2 3 4 5 6 7 8 9 |
var app = angular.module('myApp', []); myApp.factory('myService', function() { return { sayHello: function() { return "Hello" } }; }); |
In Example # 3, we create a service named “myService”. Any component that lists this service as a dependency will receive the object that you see returned. This object has one method: “sayHello”.
The angular.service Method
This approach is similar to the angular.factory method with one big exception: the function that you provide is treated as a constructor and is instantiated. It is important to note that this function is instantiated only once and every component that injects your service as a dependency shares the same exact cached object that this instantiation returns.
Example # 4
1 2 3 4 5 6 7 |
var app = angular.module('myApp', []); app.service('myService', function() { this.sayHello = function() { return "Hello" }; }); |
Example # 4 is very similar to Example # 3, except that the latter uses the JavaScript “this” keyword when defining the properties and methods of the object that the service returns. The reason is: the function provided when registering the service is instantiated (i.e. treated as a constructor), not just executed.
The angular.provider method
This approach is the most complex of the five. The angular.provider method is actually used under the hood by all four of the other service registration approache. While this approach is more verbose, it provides a powerful feature that the others do not: the service you are registering is available to your application during its configuration phase. This is useful when creating a service that will be shared across multiple applications and needs to be configured in order to work properly.
When using angular.provider, there are two important details to keep in mind:
- The callback function that you pass to the angular.provider method will be treated as a constructor – This means that the callback will be instantiated for each application that it is registered with, and its return value will be shared by all components that inject that service.
- The callback function that you pass to the angular.provider method must have a $get method – When you inject a service into a component that was created using the provider method, Angular will call that services’ $get method to retrieve the object to inject.
Example # 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var app = angular.module('myApp', []); app.provider('helloWorld', function() { this.name = 'Default'; this.$get = function() { var name = this.name; return { sayHello: function() { return "Hello, " + name + "!" } } }; this.setName = function(name) { this.name = name; }; }); app.config(function(helloWorldProvider){ helloWorldProvider.setName('World'); }); |
In Example # 5, we have created an angular service named “helloWorld”. This service is available during the configuration phase of your application. After registering the service, we call the app.config method, passing the “helloWorld” service as an argument to the anonymous callback function. Inside of that callback, we use the services “setName” method to set the value of its “name” property. We could use the “helloWorld” service in a different application and set that “name” value to something different, as needed.
Summary
Most of the time, the angular.factory method is the approach that is desired / used. This does not mean it is the “right” way to create an Angular service. The approach you take should be driven by the problem you are looking to solve. angular.value and angular.constant are often helpful when you need to share a simple value across your application (keeping in mind that the value returned by angular.constant cannot be changed). The angular.service method is helpful when you want your service to be instantiated, and the angular.provider method provides a way to configure your service before any component gains access to it.
Helpful Links for Understanding the Different Ways to Create an Angular Service
https://docs.angularjs.org/guide/services
http://blog.pluralsight.com/angularjs-step-by-step-services
https://blog.codecentric.de/en/2015/03/five-ways-to-write-better-angular-services/