Przekazywanie bieżącego zakresu do usługi AngularJS

106

Czy przekazywanie „prądu” $scopedo usługi AngularJS jest prawidłowe ?

Jestem w sytuacji, w której mam usługę $, wiedząc, że jest używana tylko przez jeden kontroler, i chciałbym mieć odniesienie do zakresu kontrolera w samych metodach $ service.

Czy jest to filozoficznie poprawne?

A może lepiej byłoby transmitować zdarzenia do $ rootScope, a następnie kazać kontrolerowi ich słuchać?

SC
źródło
1
Czy możesz powiedzieć nam dokładniej, co próbujesz zrobić? Być może rozszerzenie zakresu na usługę wcale nie jest konieczne?
ganaraj
Cóż, to nie jest takie trudne. Po prostu chciałbym mieć dostęp do $scopewłaściwości i dzwonić w $scope.$applyrazie potrzeby.
SC
Dodatkowo, powiedz, że chcę zastosować zmiany, które pochodzą z usługi $, do zakresu $. Mam nadzieję, że teraz jest to jaśniejsze.
SC
8
Proponuję umieścić właściwości $ scope, do których ma mieć dostęp usługa, w samej usłudze (zamiast mieć je w kontrolerze). Usługi są lepszymi miejscami do przechowywania modeli / danych niż administratorzy.
Mark Rajcok
@MarkRajcock Ja też próbuję zrozumieć ten problem. Obecnie właśnie dzwonię do usługi i dołączam podane dane do administratora $scope... w jaki sposób administrator miałby bezpośrednio uzyskać dostęp do danych w serwisie i przekazać je do widoku bez robienia tego?
drjimmie1976

Odpowiedzi:

67

Aby powiadomić kontroler, gdy dzieje się coś asynchronicznego, użyj obietnic Angular .

Aby sprowokować $apply, nie potrzebujesz zakresu, możesz wywołać $rootScope.$apply, ponieważ nie ma różnicy wywoływanie go w określonym zakresie lub w katalogu głównym.

Odnośnie odczytu zmiennych byłoby lepiej, gdybyś otrzymał parametry. Ale możesz również odczytać to z zakresu jako parametr obiektu, ale użyłbym parametru, który sprawiłby, że interfejs usługi byłby znacznie bardziej przejrzysty.

Caio Cunha
źródło
Myślę, że jest to odpowiedź, która lepiej rozwiązuje moje wątpliwości początkujących w AngularJS.
SC
@Caio Cunha Czy mógłbyś wyjaśnić, dlaczego przekazanie zakresu nie jest dobrym pomysłem? Mam dokładnie ten problem, chcę dodać trochę rzeczy $scopeprzez wywołanie usługi przy użyciu executeSql()funkcji asynchronicznej . Patrząc na 3 opcje (1) użyj wywołania zwrotnego w funkcji async, a następnie wywołaj $scope.$apply... to działa, ale jest brzydkie (2) przejdź $scopedo funkcji asynchronicznej, a następnie wywołaj theScope.$apply()... to również działa (3) użyj obietnicy. ..nie próbowałem tego jeszcze. Dlaczego obietnica jest najlepszym sposobem? Dzięki!
drjimmie1976
15

Powiedziałbym, że jeśli twoja funkcjonalność jest specyficzna tylko dla jednego kontrolera, nie potrzebujesz usługi.

Zadaniem kontrolerów jest manipulowanie określonym modelem, podczas gdy usługa powinna zajmować się zadaniami globalnymi. Wolałbym raczej trzymać się tego paradygmatu, zamiast mieszać rzeczy.

Tak mówią doktorzy

Usługa

Usługi Angular to pojedyncze usługi, które wykonują określone zadania wspólne dla aplikacji internetowych

Kontroler

W Angular kontroler jest funkcją JavaScript (typ / klasa), która jest używana do rozszerzania wystąpień zakresu kątowego, z wyłączeniem zakresu głównego.

PS: Oprócz tego, jeśli chcesz przetrawić, możesz również wstrzyknąć $ rootScope w ramach swojej usługi.

F Lekschas
źródło
2
Dzięki. Świetna odpowiedź. Zagłębiając się w moją sytuację, chodzi o to, że używam usługi, ponieważ chcę singletona. Jest tylko jeden kontroler korzystający z usługi, ale ten kontroler może zostać utworzony kilka razy podczas cyklu życia aplikacji, więc naprawdę chcę, aby usługa była zawsze w tym samym stanie.
SC
W każdym razie wyjaśnienie dotyczące dzwonienia $applylub $digestdo $ rootScope ma dla mnie całkowicie sens.
SC
1
Nadal trzymałbym go oddzielnie w usłudze do testowania.
bluehallu
Jeśli funkcjonalność jest specyficzna dla jednej instancji kontrolera, możesz pominąć implementację usługi, ale poza tym nie, ponieważ poświęcasz możliwość współdzielenia stanu między tymi instancjami. Co więcej, tylko dlatego, że obecnie istnieje jeden typ kontrolera, nie oznacza to, że nie będzie innego kontrolera, który jutro będzie mógł wykorzystać tę funkcjonalność. Dodatkowo usługa pozwala uniknąć nadymania kontrolera. Nie chodzi więc o to, czy istnieje pojedynczy kontroler, ale o to, jaka jest funkcjonalność, o którą chodzi.
Nick
9

Tak. Możesz przekazać $ scope do usługi podczas jej inicjalizacji. W konstruktorze usługi możesz przypisać zakres do czegoś takiego jak this._scope, a następnie odwołać się do zakresu w ramach usługi!

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;

});
user12121234
źródło
1
+1 chciałbym jednak zobaczyć sposób na automatyczne rozpoznanie każdego kontrolera za $scopekażdym razem, gdy dany kontroler wstrzykuje usługę - aby nie musieć wywoływać metody w usłudze i ręcznie $scopedo niej przechodzić .
Cody
2
@Cody Nie polecam tego, ponieważ jest to sprzeczne z Dependency Injection
Coldstar
Zgoda, +1 za zestrzelenie mnie! Zapewne rzeźnicy też DIP - powiedziałbym, ryzykując zbędność.
Cody
Mieszasz tutaj usługi z fabrykami. To rozwiązanie wykorzystuje fabrykę, która różni się od usługi. Główna różnica polega na tym, że usługa zwraca (pojedynczy) obiekt. Natomiast fabryka zwraca funkcję, której instancję można utworzyć ( new MyFunction()). Pytanie dotyczyło usługi, w której dzwonienie newnie wchodzi w grę.
Karvapallo
@Karvapallo Słuszna uwaga. Jako kontrapunkt uważam, że usługi kątowe odnoszą się do fabryki, usługi i dostawcy (ng-wat)?
user12121234
6

Osobiście uważam, że przejście $scopedo usługi jest złym pomysłem , ponieważ tworzy coś w rodzaju cyrkularnego odniesienia: kontroler jest zależny od usługi, a usługa zależy od zakresu kontrolera.

Oprócz tego, że są zagmatwane pod względem relacji, rzeczy takie jak ten w końcu przeszkadzają odśmiecaczowi.

Preferowanym podejściem jest umieszczenie obiektu domeny w zakresie kontrolera i przekazanie go do usługi. W ten sposób usługa działa niezależnie od tego, czy jest używana wewnątrz kontrolera, czy może w przyszłości w innej usłudze.

Na przykład, jeśli usługa ma wypychać i zdejmować elementy z tablicy errors, mój kod będzie wyglądał następująco:

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

Usługa współdziała wtedy ze sterownikiem, działając na errors. Oczywiście muszę być ostrożny, aby nigdy nie wymazać całego odniesienia do tablicy, ale pod koniec dnia jest to ogólny problem JS.

Nigdy nie chciałbym używać transmisji $applyi / lub podobnych rzeczy, ponieważ imho dobre praktyki OO zawsze będą ważniejsze od jakiejkolwiek magii Angulara.

Marco Faustinelli
źródło
1
Czy ten kod różni się od tego ?:$scope.errors = []; $scope.myService = new MyService($scope.errors);
Soldeplata Saketos
@SoldeplataSaketos - Tak, to prawda. errorsżyje niezależnie od $scope. O to właśnie chodzi w tej odpowiedzi. Pls sprawdź link, który podałem w tekście. Twoje zdrowie.
Marco Faustinelli
1
Jeśli dobrze rozumiem twój kod, $scope.errorswskazuje var errors, a błędy zmiennej wydają mi się zbędne, ponieważ to tylko kolejny wskaźnik. Jeden Podobna sytuacja mogę myśleć i że jest to rażąco zbędny jest ten kawałek kodu: const errors = errors2 = errors3 = []; $scope.errors = errors;. Czy zgadzasz się, że tylko podany przez Ciebie fragment kodu wydaje var errors = []się zbędny?
Soldeplata Saketos
Nie, nie jest. Powtarzam się słowo po słowie: errorsżyje niezależnie od $scope. Musisz zrozumieć, czym jest obiekt domeny, a także czym jest varprzypisanie. Jeśli podany przeze mnie link nie wystarczy, dostępnych jest wiele innych materiałów.
Marco Faustinelli
Cóż, będzie to zależało wyłącznie od implementacji funkcji MyService(errors). W moim rozumieniu usługa powinna generować tablicę rejestracyjną na podstawie parametru (czyli w tym przypadku wskaźnika). Dla mnie to zły wzorzec, ponieważ usługi są singletonami pod kątem. Jeśli implementacja usługi jest dobrze zaprogramowana, powinna wygenerować tablicę w zmiennej wewnętrznej (aby nadal była singletonem). Dlatego nie ma sensu inicjowanie zmiennej poza usługą.
Soldeplata Saketos