2015-01-11 19 views
6

Ho un router di utente definito qualcosa di simile (tagliati per semplicità):AngularJS UI Router utilizza la dipendenza risolto in fabbrica/servizio

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

Questo stato someState sta usando una fabbrica/servizio che dipende da che model risolvere. E 'definita una cosa del genere, e AngularJS genera sconosciuta fornitore: modelProvider < - modello < - someservice errore qui:

angular 
    .module('someModule') 
    .factory('someService', someService); 

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

Tuttavia, utilizzando la stessa model determinazione all'interno del controllore della questo stato funziona bene:

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

Così sto capendo che UI Router sta ritardando il dI del SomeController fino a quando la volontà che sta accadendo, che permette AngularJS di non gettare un errore. Tuttavia, come mai lo stesso ritardo non sta accadendo quando si mette quella risoluzione come dipendenza da someService? Le risoluzioni funzionano solo sui controller? E se questo è il caso, come posso usare una risoluzione all'interno di una fabbrica/servizio?

+0

quello che stai chiedendo è tutto come una catena di dipendenza molto circolare. Il servizio non dovrebbe mai dipendere dalla risoluzione, ma è possibile legare i dati al servizio durante la risoluzione – charlietfl

risposta

14

Si risolve solo i lavori sui controller?

Sì, risolve solo i lavori sui controller.

E se questo è il caso, come posso utilizzare una risoluzione all'interno di una fabbrica/servizio?

Ricordate che le fabbriche e servizi tornano Singleton oggetti, vale a dire la prima volta che una fabbrica viene iniettato in un controller, esegue alcun codice di istanza si fornisce e crea un oggetto, e quindi eventuali tempi successivi che fabbrica viene creata un'istanza , lo stesso oggetto viene restituito.

In altre parole:

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

SomeFactory.now sarà il tempo corrente la prima volta la fabbrica viene iniettato in un controllore, ma non aggiornamento consumato successiva.

Come tale, il concetto di risoluzione per una fabbrica non ha molto senso. Se si desidera avere un servizio che faccia qualcosa in modo dinamico (che è ovviamente molto comune), è necessario inserire la logica all'interno delle funzioni sul singleton.

Ad esempio, nell'esempio di codice che hai fornito, la tua fabbrica dipendeva da un modello.Un approccio sarebbe quello di iniettare il modello nel controller usando il metodo di risoluzione che hai già impostato, quindi esporre un metodo sul singleton che accetta un modello e fa quello che devi fare, ad esempio:

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

In alternativa, se non è necessario il valore risolto nel controller, non inserirlo direttamente in una risoluzione, inserire invece la logica di risoluzione in un metodo sul singleton del servizio e chiamare tale metodo all'interno della risoluzione, passando il risultato al controller.

È difficile essere più dettagliati con le conversazioni astratte, quindi se hai bisogno di ulteriori indicatori, fornisci un caso d'uso specifico.

+0

Grazie, questo ha senso e ho avuto quel momento "lampadina" il secondo mi hai ricordato che fabbriche/servizi sono singleton . Non sono sicuro di come mi sia perso questa connessione prima. – Amberite

1

Non è possibile utilizzare i valori risolti nei servizi o nelle fabbriche, solo nel controller che appartiene allo stesso stato dei valori risolti. I servizi e le fabbriche sono singleton e i controller sono stati istanziati di recente per (in questo caso) uno stato o qualsiasi altro luogo in cui viene utilizzato ng-controller.

L'istanziazione avviene con il servizio $ controller che è in grado di iniettare oggetti che appartengono solo a quel controller. I servizi e le fabbriche non hanno questa capacità.

1

Si dovrebbe rivedere la angular docs su iniezione di dipendenza:

  • Componenti come i servizi, le direttive, i filtri e le animazioni sono definiti da un metodo factory iniettabile o funzione di costruzione. Questi componenti possono essere iniettati con componenti "servizio" e "valore" come dipendenze.
  • I controllori sono definiti da una funzione di costruzione, che può essere iniettata con uno qualsiasi dei componenti "servizio" e "valore" come dipendenze, ma possono anche essere forniti con dipendenze speciali. Vedi Controllori sotto per un elenco di queste dipendenze speciali.
  • Il metodo di esecuzione accetta una funzione, che può essere iniettata con componenti "service", "value" e "constant" come dipendenze. Si noti che non è possibile iniettare "provider" in blocchi di esecuzione.
  • Il metodo config accetta una funzione, che può essere iniettata con componenti "provider" e "costante" come dipendenze. Si noti che non è possibile iniettare componenti "servizio" o "valore" nella configurazione.

Quindi ogni tipo di componente angolare ha il proprio elenco di componenti accettabili da iniettare. Dato che i servizi sono singleton, non avrebbe alcun senso iniettare un valore come parte di una risoluzione. Se nella tua pagina c'erano due posti separati che utilizzavano un servizio con diverse risoluzioni, il risultato sarebbe indeterminato. Non avrebbe più senso che iniettare $scope nel tuo servizio. Ha senso per i controller perché il controller è responsabile della stessa area della pagina che è stata risolta.

Se il tuo someService deve utilizzare i dati nel modello, dovrebbe avere una funzione che prende i dati come parametro e il controller deve passarli.

3

Giusto per aggiungere la risposta di Ed Hinchliffe, c'è un'altra cosa che potresti provare. Come altre persone hanno menzionato, fabbriche e servizi sono singleton. Tuttavia, c'è una differenza fondamentale tra servizi e fabbriche che possiamo usare per questo, i servizi forniscono un'istanza della funzione di servizio (new ServiceFunction()), mentre una fabbrica fornisce il valore che viene restituito invocando il riferimento alla funzione passato al modulo. Queste informazioni è ulteriormente spiegato su questo thread StackOverflow:

service-vs-provider-vs-factory

Quindi, in pratica questo significa che siamo in grado di creare una funzione sulla fabbrica, aggiungere proprietà al suo prototipo, e quindi creare un'istanza di esso sul controller passando gli argomenti che ci piacciono Ecco un esempio molto semplice:

Supponiamo di avere il nostro modulo angolare su una variabile globale app.Prima creiamo stato Ui-Router con un attributo risolvere:

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; 
     }] 
    } 
    }); 
}); 

Per rappresentare un'azione asincrona stiamo usando il servizio $q e $timeout fornito dal nucleo angolare per restituire una promessa sull'oggetto RESLOVE, che viene risolto dopo tre secondi.

Ora abbiamo bisogno di creare la nostra fabbrica con il metodo spiegato prima:

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; 
}); 

Qui abbiamo creato la fabbrica Greeter come costruttore. Ora possiamo creare un'istanza di questa fabbrica che passa gli argomenti che ci piacciono. In questo esempio, è necessario fornire al costruttore un valore repeat. Se vogliamo iniettare un valore asincrono alla fabbrica, approfittando della resolve proprietà di un ui-stato, possiamo farlo sul controller:

app.controller('ExampleCtrl', function ($scope, Greeter, repeatValue) { 
    $scope.repeatValue = repeatValue; 

    var greeter = new Greeter(repeatValue); 

    $scope.greetingsArray = greeter.repeatedHi(); 
}); 

Il $scope.greetingsArray sarà riempito con repeatValue di "Hi " stringhe.

Spero di essere stato chiaro, spero che sia d'aiuto.

+0

Grazie per l'ottimo post. In precedenza pensavo di trasformare la fabbrica in un'istanza istanziata dal controller, anche se non ero sicuro che si trattasse di un anti-pattern. – Amberite

Problemi correlati