AngularJS UI Router using resolved dependency in factory / service

I have a UI router that defines something like this (cropped for simplicity):

$stateProvider .state('someState', { resolve: { model: ['modelService', 'info', function (modelService, info) { return modelService.get(info.id).$promise; }] }, controller: 'SomeController' }); 

In this state, someState uses a factory / service that is dependent on this model solution. He defined something like this, and AngularJS throws an error Unknown provider: modelProvider <- someService :

 angular .module('someModule') .factory('someService', someService); someService.$inject = ['model']; function someService(model) { ... } 

However, using the same model resolving inside this state controller works fine :

 SomeController.$inject = ['model']; function SomeController(model) { ... } 

So, I understand that the UI router delays the SomeController DI until it is resolved, which allows AngularJS not to throw an error. However, how does the same delay not occur when you install this solution as a dependency on someService ? Does it allow work only on controllers? And if so, how can I use the solution inside the factory / service?

+6
source share
4 answers

Does it allow to work only on controllers?

Yes, only work with controllers.

And if so, how can I use the solution inside the factory / service?

Remember that factories and services return monophonic objects, that is, the first time factory is entered into the controller, it launches any instance code that you provide and creates the object, and then all subsequent moments when the factory is created, the same object is returned.

In other words:

 angular.module('someModule') .factory( 'SomeFactory' , function () { // this code only runs once object = {} object.now = Date.now(); return object ); 

SomeFactory.now will be the current time the first time the factory is entered into the controller, but not the next time it is used.

Thus, the concept of a factory solution does not really make sense. If you want to have a service that does something dynamically (which is obviously very common), you need to put the logic inside the functions on a singleton.

For example, in the code sample that you specified, your factory depended on the model. One approach would be to inject the model into the controller using the resolution method that you already configured, and then set the method on a singlet that accepts the model and does what you need to do, for example:

 angular.module('someModule') .factory( 'SomeFactory', function () { return { doSomethingWithModel: function (model) { $http.post('wherever', model); } }); .controller('SomeController', function (SomeFactory, model) { SomeFactory.doSomethingWithModel(model); }); 

Alternatively, if you do not need an allowed value in the controller at all, do not put it directly in the solution, instead put the logic of the solution in the method of a single call to the service and call this method inside the resolution, passing the result to the controller.

It is difficult to be more detailed with an abstract conversation, so if you need further pointers, specify a specific use case.

+14
source

To add an answer to Ed Hinchliff, there is one more thing you could try. As other people have noted, factories and services are solitary. However, there is one key difference between the services and factories we can use to do this: the services provide an instance of the service function ( new ServiceFunction() ), and the factory is the value returned by calling the function reference passed to the module. This information is explained in this stackoverflow:

service-vs-provider-vs-factory

So basically this means that we can create a function in the factory, add properties to its prototype, and then create an instance of this on the controller, passing the arguments we need. Here is a very simple example:

Suppose we have our angular module for the app global variable. First, we create a Ui-Router state with a permission attribute:

 app.config(function ($stateProvider) { $stateProvider .state('example', { url: '/example', templateUrl: 'app/example/example.html', controller: 'ExampleCtrl', resolve: { repeatValue: ['$q', '$timeout', function($q, $timeout){ var deferred = $q.defer(); $timeout(function(){ deferred.resolve( parseInt(Math.random() * 100) ); }, 3000); return deferred.promise; }] } }); }); 

To represent an asynchronous action, we use the $q and $timeout service provided by the angular core to return a promise to the reslove object, which resolves after three seconds.

Now we need to create our factory using the method described earlier:

 app.factory('Greeter', function () { // Function var Greeter = function(repeat){ this.repeat = repeat; }; // Prototype Greeter.prototype.repeatedHi = function() { var array = []; console.log(this); for (var i = 0; i < this.repeat; i++){ array.push('Hi'); } return array; }; // Public API here return Greeter; }); 

Here we created the Greeter factory constructor. Now we can create an instance of the factory by passing the arguments we need. In this example, we need to give the constructor a value of repeat . If we want to enter an asynchronous value in the factory using the resolve ui-state property, we can do this on the controller:

 app.controller('ExampleCtrl', function ($scope, Greeter, repeatValue) { $scope.repeatValue = repeatValue; var greeter = new Greeter(repeatValue); $scope.greetingsArray = greeter.repeatedHi(); }); 

$scope.greetingsArray will populate the repeatValue string "Hi".

Hope I said clearly, hope this helps.

+3
source

You cannot use allowed values ​​in services or factories, only in the controller, which belongs to the same state as the allowed values. Services and factories are singletones, and controllers are re-created for (in this case) state or elsewhere where ng-controller is used.

An instance is created using the $ controller service, which is capable of injecting objects that belong only to this controller. Services and factories do not have this capability.

+1
source

You should look at angular docs when dependency injection:

  • Components, such as services, directives, filters, and animations, are defined by the factory injection method or constructor. As dependencies, these components can be added by the "service" and "value" components.
  • Controllers are defined by a constructor function, which can be introduced by any of the service and value components as dependencies, but they can also be provided with special dependencies. See “Controllers” below for a list of these special dependencies.
  • The run method accepts a function that can be entered as dependencies with the parameters "service", "value" and "constant". Please note that you cannot enter "providers" in startup blocks.
  • The config method accepts a function that can be introduced using the "providers" and "constant" components as dependencies. Please note that you cannot enter service or value components in a configuration.

Thus, each type of angular component has its own list of acceptable input components. Since services are single point, it really does not make sense to enter a value as part of the solution. If your page had two separate locations that used the service with different permissions, the result would be undefined. That would make no sense than introducing $scope into your service. This makes sense for the controllers, because the controller is responsible for the same area of ​​the allowed page.

If your someService should use the data in the model, it should have a function that takes the data as a parameter, and your controller should pass it.

+1
source

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


All Articles