Passaggio dell'ambito corrente a un servizio AngularJS


106

È corretto passare la "corrente" $scopea un servizio AngularJS?

Mi trovo nella situazione in cui ho un $ service sapendo che è consumato da un solo controller e mi piacerebbe avere un riferimento all'ambito del controller nei metodi $ service stessi.

È filosoficamente corretto?

O farei meglio a trasmettere eventi a $ rootScope e poi farli ascoltare dal mio controller?


1
Puoi forse farci sapere in modo più specifico cosa stai tentando di fare? Forse spingere l'ambito sul servizio non è affatto necessario?
ganaraj

Beh, non è così difficile. Semplicemente, vorrei poter accedere alle $scopeproprietà e chiamare $scope.$applyquando necessario.
SC

Inoltre, si supponga di voler applicare le modifiche provenienti da $ service a $ scope. Spero sia più chiaro ora.
SC

8
Ti suggerisco di inserire le proprietà $ scope a cui vuoi che il tuo servizio acceda nel servizio stesso (invece di averle nel controller). I servizi sono posti migliori per archiviare modelli / dati rispetto ai controller.
Mark Rajcok

@MarkRajcock Sto cercando di capire anche questo problema. Al momento sto solo chiamando un servizio e allegando i dati forniti a quelli del controller $scope... come farebbe il controller ad accedere direttamente ai dati nel servizio e passarli alla visualizzazione senza farlo?
drjimmie1976

Risposte:


67

Per far sapere al controller quando accade qualcosa di asincrono, usa le promesse angolari .

Per provocare il $apply, non è necessario l'ambito, è possibile chiamare $rootScope.$apply, poiché non c'è differenza chiamandolo in uno scopo specifico o nella radice.

Per quanto riguarda la lettura della variabile, sarebbe meglio se ricevessi i parametri. Ma potresti anche leggerlo da uno scope come parametro oggetto, ma io andrei con il parametro, che renderebbe la tua interfaccia di servizio molto più chiara.


Penso che questa sia la risposta che risolve meglio i miei dubbi sui principianti di AngularJS.
SC

@Caio Cunha Potresti espandere il motivo per cui non è una buona idea passare un ambito? Sto riscontrando esattamente questo problema, voglio aggiungere alcune cose $scopetramite una chiamata a un servizio utilizzando un asyncexecuteSql() funzione . Esaminando 3 opzioni (1) usa un callback sulla funzione async, quindi chiama $scope.$apply... funziona, ma è brutto (2) passa $scopealla funzione async, quindi chiama theScope.$apply()... anche questo funziona (3) usa una promessa. ..non ancora provato. Perché una promessa è il modo migliore? Grazie!
drjimmie1976

15

Direi che se la tua funzionalità è specifica per un solo controller, non hai bisogno di un servizio.

Il compito del controllore consiste nel manipolare il modello specifico mentre un servizio dovrebbe occuparsi di compiti globali. Preferisco attenermi a questo paradigma invece di mescolare le cose.

Questo è ciò che dicono i documenti

Servizio

I servizi angolari sono singleton che svolgono attività specifiche comuni alle app web

controllore

In Angular, un controller è una funzione JavaScript (tipo / classe) utilizzata per aumentare le istanze di angular Scope, escluso l'ambito radice.

PS: A parte questo, se hai bisogno di digerire puoi anche iniettare $ rootScope all'interno del tuo servizio.


2
Grazie. Ottima risposta. Entrando in profondità nella mia situazione, il punto è che sto usando Service perché voglio un singleton. C'è solo un controller che utilizza il servizio, ma questo controller potrebbe essere istanziato più volte durante il ciclo di vita dell'app, quindi voglio davvero che il servizio sia sempre nello stesso stato.
SC

Comunque il chiarimento sulla chiamata $applyo $digestsu $ rootScope ha perfettamente senso per me.
SC

1
Lo terrei comunque separato in un servizio per il test.
bluehallu

Se la funzionalità è specifica per un'istanza di un controller, è possibile saltare l'implementazione di un servizio, ma altrimenti no poiché si sacrifica la possibilità di condividere lo stato tra quelle istanze. Inoltre, solo perché oggi esiste un solo tipo di controller, non significa che non ci sarà un controller diverso in grado di sfruttare la funzionalità domani. Inoltre, un servizio può evitare di gonfiare il controller. Quindi non è tanto una questione se esiste un singolo controller, ma quale sia la funzionalità in questione.
Nick

9

Sì. È possibile passare $ scope al servizio quando lo si inizializza. Nel costruttore del servizio puoi assegnare l'ambito a qualcosa di simile a this._scope e quindi fare riferimento all'ambito all'interno del servizio!

angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {

    $scope.someVar = 4;

    $scope.blahService = new blahService($scope);

});

angular.module('blah').factory('blahService', function() {

    //constructor
    function blahService(scope) {
        this._scope = scope;

        this._someFunction()
    }

    //wherever you'd reference the scope
    blahService.prototype._someFunction = function() {

        this._scope['someVar'] = 5;

    }

    return blahService;

});

1
+1, tuttavia, vorrei vedere un modo per conoscere automaticamente ogni controller ogni $scopevolta che quel dato controller inietta il servizio, in modo da non dover chiamare un metodo sul servizio e passarlo manualmente $scope.
Cody

2
@Cody non lo consiglio in quanto è in contraddizione con Dependency Injection
Coldstar

D'accordo, +1 per avermi sparato! Probabilmente anche i macellai DIP - direi, a rischio di essere ridondanti.
Cody

Stai mescolando servizi con fabbriche qui. Questa soluzione utilizza una fabbrica, che differisce da un servizio. La differenza principale è che un servizio restituisce un oggetto (singleton). Mentre una factory restituisce una funzione, che può essere istanziata ( new MyFunction()). La domanda riguardava un servizio, dove la chiamata newnon è un'opzione.
Karvapallo

@Karvapallo Buon punto. Come contro punto credo che i servizi angolari si riferiscano a una fabbrica, un servizio e un fornitore (ng-wat)?
user12121234

6

Personalmente credo che passare $scopea un servizio sia a cattiva idea , perché crea una sorta di riferimento circolare: il controller dipende dal servizio e il servizio dipende dall'ambito del controller.

Oltre a creare confusione in termini di relazioni, cose come questa finiscono per intralciare il netturbino.

Il mio approccio preferito è inserire un oggetto di dominio nell'ambito del controller e passarlo al servizio. In questo modo il servizio funziona indipendentemente dal fatto che venga utilizzato all'interno di un controller o forse all'interno di un altro servizio in futuro.

Ad esempio, se il servizio dovrebbe eseguire il push e il pop di elementi da un array errors, il mio codice sarà:

var errors = [];
$scope.errors = errors;
$scope.myService = new MyService(errors);

Il servizio interagisce quindi con il controllore operando su errors. Ovviamente devo essere cauto nel non cancellare mai l'intero riferimento all'array, ma alla fine della giornata questa è una preoccupazione generale di JS.

Non vorrei mai usare trasmissioni $applye / o cose simili, perché le buone pratiche di OO trionferanno sempre su qualsiasi magia angolare.


1
Questo codice ha qualche differenza con questo ?:$scope.errors = []; $scope.myService = new MyService($scope.errors);
Soldeplata Saketos

@SoldeplataSaketos - Sì, lo fa. errorsvive indipendentemente da $scope. Questo è il punto centrale di questa risposta. Pls controlla il link che ho fornito nel testo. Saluti.
Marco Faustinelli

1
Se capisco correttamente il tuo codice, $scope.errorssta puntando var errorse la variabile errors mi sembra ridondante, poiché è solo un altro puntatore. Una situazione simile mi viene in mente e che è palesemente ridondanti è questo pezzo di codice: const errors = errors2 = errors3 = []; $scope.errors = errors;. Sei d'accordo che con solo il pezzo di codice che hai fornito sembra che var errors = []sia ridondante?
Soldeplata Saketos

No non lo è. Mi ripeto parola per parola: errorsvive indipendentemente da $scope. È necessario capire cos'è un oggetto di dominio e cos'è varun'assegnazione. Se il link che ho fornito non è sufficiente, c'è molto altro materiale disponibile.
Marco Faustinelli

Bene, ciò dipenderà esclusivamente dall'implementazione della funzione MyService(errors). A quanto mi risulta, il servizio dovrebbe generare un array di registrazione basato sul parametro (che in questo caso è un puntatore). Per me questo è un cattivo schema, poiché i servizi sono singleton in angolare. Se l'implementazione del servizio è ben programmata, dovrebbe generare l'array in una variabile interna (per continuare ad essere un singleton). Pertanto, non ha senso inizializzare la variabile al di fuori del servizio.
Soldeplata Saketos
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.