Design pattern is very important if you want to build a manageable and scalable application. In AngularJS, we could develop module, which is also regarded as a dependency, for specific feature and it could be injected to the controller whenever it is needed. This dependency injection design help us to build clean and well structured application and reduce redundant code.
There are different ways to create the dependency. Most common terms are service, factory and provider which are all singleton and you can find a lot of discussion online about their differences, when and how to use them. Here are some notes which summarize the AngularJS documentation about writing Provider.
1. When a dependency is injected to the controller, the injector from AngularJS framework will register a recipe such that the injected controller could make use of the type provided by the dependency.
2. They are 5 different kinds of recipes which are:
- Provider recipe
- Value recipe
- Factory recipe
- Service recipe
- Constant recipe
3. Provider recipe is the most verbose and comprehensive. It is the fundamental recipe to build dependency. The other 4 recipes are just syntactic sugar on top of the Provider recipe.
4. The following section will look into each recipe and give more thorough details.
Value recipe
The simplest way to create dependency. Since it is the simplest one, we could only provide a string from it.
Example: Build a module such that it could provide the controller a client id.
index.html
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>AngularJS Value Recipe Example</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script> <script src="js/main.js"></script> </head> <body> <div ng-controller="MainController"> I need to know the client id and it is {{ clientId }}. </div> </body> </html>
js/main.js
var app = angular.module('myApp', []); // Create a dependency called clientId app.value('clientId', 'abc1234567890') app.controller('MainController', ['$scope', 'clientId', function MainController($scope, clientId) { $scope.clientId = clientId; }]);
Factory recipe
While Value recipe only provides a string literal, Factory recipe could do exactly the same.
js/main.js
var app = angular.module('myApp', []); // Create a dependency called clientId // app.value('clientId', 'abc1234567890') app.factory('clientId', function clientIdFactory() { return 'abc1234567890'; }); app.controller('MainController', ['$scope', 'clientId', function MainController($scope, clientId) { $scope.clientId = clientId; }]);
As you can see, we could use the return keyword to provide the string literal to the controller. We could also provide any type to the controller including:
- Primitive type including integer, string literal…
- Object literal
- Function
- Instance of a custom type
Much more than that, Factory recipe is more powerful compared to Value recipe because:
- Ability to use other services (have dependencies)
- Allow logical operation before return (initialization)
- Allow delay/lazy loading (not to be covered in this post – reference)
Example: Build a dependency called apiToken which provides the controller the token and it depends on the clientId dependency.
index.html
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>AngularJS Factory Recipe Example</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script> <script src="js/main.js"></script> </head> <body> <div ng-controller="MainController"> I need to know the api token and it is {{ apiToken }}. </div> </body> </html>
js/main.js
var app = angular.module('myApp', []); // Create a dependency called clientId app.value('clientId', 'abc1234567890') // Create a dependency called apiToken which depends on clientId app.factory('apiToken', ['clientId', function apiTokenFactory(clientId) { // Initialization before return var encrypt = function(data1, data2) { // NSA-proof encryption algorithm: return (data1 + ':' + data2).toUpperCase(); }; var secret = 'this_is_the_secret_string'; var apiToken = encrypt(clientId, secret); // This factory provides you the apiToken return apiToken; }]); app.controller('MainController', ['$scope', 'apiToken', function MainController($scope, apiToken) { $scope.apiToken = apiToken; }]);
Service recipe
In the Factory recipe, we could inject any type to the controller including instance of custom type. This is very useful for developers who would like to stick on Object Oriented Programming. Let’s say we have defined a Counter.
function Counter(apiToken) { this.apiToken = apiToken; this.count = 0; this.inc = function() { this.count++; } this.dec = function() { this.count--; } }
We could use the Factory recipe to return a counter instance together with the new keyword.
// Create a dependency called counter which depends on apiToken app.factory('counter', ['apiToken', function counterFactory(apiToken) { return new Counter(apiToken); }]);
When we need to inject a new instance of a custom type, we could use Service recipe which follows a design pattern called Constructor Injection. The Service recipe is a bit simpler as depicted below.
// Create a dependency called counter which depends on apiToken // app.factory('counter', ['apiToken', function counterFactory(apiToken) { // return new Counter(apiToken); // }]); app.service('counter', ['apiToken', Counter]);
Example: Build a dependency called counter which depends on apiToken and provide the instructor a instance of Counter.
index.html
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>AngularJS Service Recipe Example</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script> <script src="js/main.js"></script> </head> <body> <div ng-controller="MainController"> I should have a counter which has been counted 3 times. (Count = {{ count }}) </div> </body> </html>
js/main.js
var app = angular.module('myApp', []); // Counter function Counter(apiToken) { this.apiToken = apiToken; this.count = 0; this.inc = function() { this.count++; } this.dec = function() { this.count--; } } // Create a dependency called clientId app.value('clientId', 'abc1234567890') // Create a dependency called apiToken which depends on clientId app.factory('apiToken', ['clientId', function apiTokenFactory(clientId) { // Initialization before return var encrypt = function(data1, data2) { // NSA-proof encryption algorithm: return (data1 + ':' + data2).toUpperCase(); }; var secret = 'this_is_the_secret_string'; var apiToken = encrypt(clientId, secret); // This factory provides you the apiToken return apiToken; }]); // Create a dependency called counter which depends on apiToken and provide a instance of Counter app.service('counter', ['apiToken', Counter]); app.controller('MainController', ['$scope', 'counter', function MainController($scope, counter) { counter.inc(); counter.inc(); counter.inc(); counter.inc(); counter.dec(); $scope.count = counter.count; }]);
Provider recipe
Provider recipe is the most powerful recipe but in most cases, the other recipes are good enough so using Provider recipe will be too much. The only use case of Provider recipe is when you want to build a dependency which is used in multiple applications and you want to allow application-wide configuration of the dependency before it is injected to the controller.
Example: Build a dependency called counter which depends on apiToken and provide the constructor an instance of Counter. But unlike the Service recipe example, we could decide the initial count value of the counter in the app.config().
index.html
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>AngularJS Provider Recipe Example</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script> <script src="js/main.js"></script> </head> <body> <div ng-controller="MainController"> I should have a counter which has been counted 10 + 3 times . (Count = {{ count }}) </div> </body> </html>
js/main.js
var app = angular.module('myApp', []); // Counter function Counter(apiToken, count) { this.apiToken = apiToken; this.count = count; this.inc = function() { this.count++; } this.dec = function() { this.count--; } } // Create a dependency called clientId app.value('clientId', 'abc1234567890') // Create a dependency called apiToken which depends on clientId app.factory('apiToken', ['clientId', function apiTokenFactory(clientId) { // Initialization before return var encrypt = function(data1, data2) { // NSA-proof encryption algorithm: return (data1 + ':' + data2).toUpperCase(); }; var secret = 'this_is_the_secret_string'; var apiToken = encrypt(clientId, secret); // This factory provides you the apiToken return apiToken; }]); // Create a dependency called counter which depends on apiToken and provide a instance of Counter // The counter instance is configurable on application level app.provider('counter', function CounterProvider() { // Default count is zero without app level configuration this.initialCount = 0; // Setter for configuration in app.config() this.setInitialCount = function(count) { this.initialCount = count; } // Define the connterFactory in the this.$get this.$get = ['apiToken', function counterFactory(apiToken) { return new Counter(apiToken, this.initialCount); }]; }) // Application level configuration for the counter provider app.config(['counterProvider', function(counterProvider) { counterProvider.setInitialCount(10); }]); app.controller('MainController', ['$scope', 'counter', function MainController($scope, counter) { counter.inc(); counter.inc(); counter.inc(); counter.inc(); counter.dec(); $scope.count = counter.count; }]);
First we modified the Counter constructor so it now takes a count as the second input parameter.
Second, we define the counter Provider recipe which defines the setter for app.config() and also the counterFactory in this.$get method. This method is a factory function just like the one we used in Factory recipe.
Finally, we configure the initialCount in the app.config() so the injected counter will start counting from 10 instead of the default 0. You can comment the app.config() part and see what happens to the injected counter.
Constant recipe
From the Provider recipe example, we could configure the dependency in app.config(). It is regarded as the configuration phase inside the AngularJS application life cycle which is executed before the run phase. In the configuration phase, the app has no idea about other dependencies. For example, the following example WILL NOT WORK.
// Create a dependency called initialCount app.value('initialCount', 100); ... // Application level configuration for the counter provider app.config(['counterProvider', 'initialCount', function(counterProvider, initialCount) { counterProvider.setInitialCount(initialCount); }]);
That means we got to have some dependencies which are available for both configuration and run phases and that’s why we have the Constant recipe. The following example works as expected.
// Create a constant dependency called initialCount which is available for both configuration and run phases app.constant('initialCount', 100); ... // Application level configuration for the counter provider app.config(['counterProvider', 'initialCount', function(counterProvider, initialCount) { counterProvider.setInitialCount(initialCount); }]);
Done =)
Reblogged this on Dinesh Ram Kali..
LikeLiked by 1 person
Reblogged this on Raoul Grosser.
LikeLike
This is the best way of explanation so far found in Internet for the title Factory vs Service vs Provider. Thank u so much.
I am a beginner in Angular js and very curious to know? What is the difference from above example? With respect to example posted, even new keyword can return us the instance of Counter from Factory (ie return new Counter(apiToken); ). Then whats that Service role. Even Service returns same instance of Object like how Factory returned.
It would be really helpful if you could share me the explanation using JSFIDDLE demo.
If I am not wrong both Factory & Service mentioned above are giving us same instance of COUNTER. And these are just two different methodology that can be used in our application.
Am i Correct. Correct me.
LikeLike
In my opinion, factory and service actually could serve the same purpose but just the syntax is different. Both of them will return a singleton. It is more about the context which determines whether u should use a factory or service.
In the following example, i got 2 controllers and both of them need the counter and display the count value. since it is a singleton, both controllers will share the same counter. In this case, i will use service
But if i want individual counter for different controllers, i would use a counter factory (which is a singleton) and ask for a new instance of counter.
This is what my preference and my interpretation on how to use them, but there exists other way of explanations and usages. hope it helps. =)
LikeLike