📜 ⬆️ ⬇️

Understanding the types of services in AngularJS (constant, value, factory, service, provider)

Angulyar comes with different types of services or services, each of which is used in its own situation.
Keep in mind that services, regardless of the type, are always singletones (singles).

Note: Singleton is a design pattern that restricts a class in such a way that it can have only one instance. It is with this instance that work is carried out wherever it is used.

Go to the types of services
')

Constant


app.constant('fooConfig', { config1: true, config2: "Default config2" }); 

The constant is often used for default configuration in directives. So if you are creating a directive, and you want to be able to pass on to it some standard parameters, the constant is a good way to do this.

The value of a constant is specified during the definition and cannot be changed in another way. The value of a constant can be a primitive or an object. The same constant can be configured on the config stage of the module. Value can only be used at the run stage and further (note from comments).

Value


 app.value('fooConfig', { config1: true, config2: "Default config2 but it can changes" }); 

A variable is like a constant, but can be changed. It is often used to set up directives. The variable is similar to the truncated version of the factory, only contains values ​​that cannot be calculated in the service itself.

Factory


 app.factory('foo', function() { var thisIsPrivate = "Private"; function getPrivate() { return thisIsPrivate; } return { variable: "This is public", getPrivate: getPrivate }; }); // ... app.factory('bar', function(a) { return a * 2; }); 

The factory is the most frequently used service. It is also the easiest to understand.

A factory is a service that can return any type of data. It does not contain rules for creating this data. You just need to return something. When working with objects, I like working with an open module template , but you can use a different approach if you want.

As mentioned above, all types are singletones, so if we change foo.variable in one place, it will change in other places too.

Service


 app.service('foo', function() { var thisIsPrivate = "Private"; this.variable = "This is public"; this.getPrivate = function() { return thisIsPrivate; }; }); 

Service (do not confuse a common name with a specific type) works the same as a factory. The difference is that the service uses the constructor, so when you use it for the first time, it will execute new Foo(); to create an instance of an object. Keep in mind that the same object will return in other places if you use this service there.

In fact, the service is equivalent to the following code:

 app.factory('foo2', function() { return new Foobar(); }); function Foobar() { var thisIsPrivate = "Private"; this.variable = "This is public"; this.getPrivate = function() { return thisIsPrivate; }; } 

Foobar is a class , and we create an instance of it in our factory, use it for the first time, and then return it. Like the service, an instance of the Foobar class will only be created once and the next time the factory returns the same instance again.

If we already have a class, and we want to use it in our service, we can do this:

 app.service('foo3', Foobar); 

Provider


A provider is a factory configured in a special way. In fact, the factory of the last examples will look something like this:

 app.provider('foo', function() { return { $get: function() { var thisIsPrivate = "Private"; function getPrivate() { return thisIsPrivate; } return { variable: "This is public", getPrivate: getPrivate }; } }; }); 

The provider expects the $get function, which will be what we embed in other parts of our application. Therefore, when we inject foo into the controller, the $get function is injected $get

Why use this form when the factory is much simpler? Because the provider can be configured in the configuration function. So you can do something like this:

 app.provider('foo', function() { var thisIsPrivate = "Private"; return { setPrivate: function(newVal) { thisIsPrivate = newVal; }, $get: function() { function getPrivate() { return thisIsPrivate; } return { variable: "This is public", getPrivate: getPrivate }; } }; }); app.config(function(fooProvider) { fooProvider.setPrivate('New value from config'); }); 

Here we brought thisIsPrivate beyond the $get function, and then created the setPrivate function to be able to change thisIsPrivate in the configuration function. Why do you need to do this? Isn't it easier to just add a setter in a factory? There is another goal.

We want to implement a specific object, but we want to have a way to customize it for our needs. For example: for a resource wrapper service using JSONP, we want to be able to customize the URL that will be used by us or third-party services, such as restangular . The provider allows us to configure it in advance for our purposes.

Please note that in the configuration function you need to specify nameProvider as the name, not name . name specified in all other cases.

Seeing this, we recall that we have already configured some services in our applications, for example, in $routeProvider and $locationProvider configure routing and html5mode, respectively.

Bonus 1: Decorator


So, you decided that some foo service lacked the greet function and you want to add it. Is it worth changing a factory? Not! You can decorate it:

 app.config(function($provide) { $provide.decorator('foo', function($delegate) { $delegate.greet = function() { return "Hello, I am a new function of 'foo'"; }; return $delegate; }); }); 

$provide is what Angulyar uses to create all internal services. We can use it manually if we want, or simply use the functions provided in our modules (you need to use $provide for decoration). $provide has a decorator function that allows us to decorate our services. It receives the name of the service being decorated, and the callback receives $delegate , which is the original instance of the service.

Here we can do whatever we want we want to decorate our service. In our case, we added the greet function to the original service. Then returned a new modified service.

Now, when used, it will have a new greet function.

The ability to decorate services is convenient when using services from third-party developers, which can be decorated without the need to copy into your project and further modifications.

Note: Constant cannot be decorated.

Bonus 2: creating new instances


Our services are singletones, but we can create a singleton factory that creates new instances. Before you dive in, keep in mind that having singleton services is a good approach that we don’t want to change. But in those rare cases when you need to generate new instances, you can do it like this:

 //   function Person( json ) { angular.extend(this, json); } Person.prototype = { update: function() { //  (   :P) this.name = "Dave"; this.country = "Canada"; } }; Person.getById = function( id ) { //  -,   Person  id return new Person({ name: "Jesus", country: "Spain" }); }; //   app.factory('personService', function() { return { getById: Person.getById }; }); 

Here we create a Person object that receives some JSON data to initialize the object. Then we created a function in our prototype (functions in the prototype for instances of Person ) and functions directly in Person (similar to the class functions).

So we have a class function that will create a new Person object based on the identifier that we pass (as it will be in real code) and each instance will be able to update itself. Now you just need to create a service that will use it.

Every time we call personService.getById we create a new Person object, so we can use this service in different controllers, and even when the factory is a singleton, it creates new objects.

Respect Josh David Miller for his example.

Bonus 3: CoffeeScript


CoffeeScript can be conveniently combined with services, as they provide a nicer way to create classes. Let's look at Bonus Example 2 using CoffeeScript:

 app.controller 'MainCtrl', ($scope, personService) -> $scope.aPerson = personService.getById(1) app.controller 'SecondCtrl', ($scope, personService) -> $scope.aPerson = personService.getById(2) $scope.updateIt = () -> $scope.aPerson.update() class Person constructor: (json) -> angular.extend @, json update: () -> @name = "Dave" @country = "Canada" @getById: (id) -> new Person name: "Jesus" country: "Spain" app.factory 'personService', () -> { getById: Person.getById } 

Now he looks prettier in my humble opinion.
Now he looks worse in the humble opinion of the translator.

Conclusion


Services are one of the most attractive features of Angulyar. There are many ways to create them; you just need to choose the right one that is best suited in our case.

Source: https://habr.com/ru/post/190342/


All Articles