Mam usługę, powiedz:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
I chciałbym użyć foo
do sterowania listą renderowaną w HTML:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
Aby sterownik mógł wykryć, kiedy aService.foo
jest aktualizowany, połączyłem ten wzorzec, w którym dodałem usługę do kontrolera, $scope
a następnie użyłem $scope.$watch()
:
function FooCtrl($scope, aService) {
$scope.aService = aService;
$scope.foo = aService.foo;
$scope.$watch('aService.foo', function (newVal, oldVal, scope) {
if(newVal) {
scope.foo = newVal;
}
});
}
Wydaje mi się to długotrwałe i powtarzam to w każdym kontrolerze, który korzysta ze zmiennych usługi. Czy jest lepszy sposób na obejrzenie wspólnych zmiennych?
angularjs
watch
angular-services
berto
źródło
źródło
aService.foo
w znacznikach HTML? (jak to: plnkr.co/edit/aNrw5Wo4Q0IxR2loipl5?p=preview )Odpowiedzi:
Zawsze możesz użyć starego dobrego wzorca obserwatora, jeśli chcesz uniknąć tyranii i kosztów ogólnych
$watch
.W serwisie:
I w kontrolerze:
źródło
$destory
zdarzenia w zakresie i dodaj metodę wyrejestrowania doaService
$watch
Wzorzec vs obserwator to po prostu wybór odpytywania lub odpychania i zasadniczo jest to kwestia wydajności, więc używaj go, gdy liczy się wydajność. Używam wzorca obserwatora, kiedy inaczej musiałbym „głęboko” obserwować złożone obiekty. Dołączam całe usługi do zakresu $ zamiast oglądać wartości pojedynczej usługi. Unikam zegarka Angulara jak diabeł, tego wystarczająco dużo dzieje się w dyrektywach i rodzimym wiązaniu danych kątowych.W takim scenariuszu, w którym wiele / nieznanych obiektów może być zainteresowanych zmianami, skorzystaj
$rootScope.$broadcast
ze zmienianego elementu.Zamiast tworzyć własny rejestr słuchaczy (które muszą zostać wyczyszczone przy różnych zniszczeniach $), powinieneś mieć możliwość skorzystania
$broadcast
z danej usługi.Nadal musisz kodować
$on
programy obsługi w każdym detektorze, ale wzorzec jest oddzielony od wielu wywołań do,$digest
co pozwala uniknąć ryzyka długotrwałych obserwatorów.W ten sposób również słuchacze mogą przychodzić i wychodzić z DOM i / lub różnych zakresów potomnych bez zmiany sposobu działania usługi.
** aktualizacja: przykłady **
Nadawanie byłoby najbardziej sensowne w „globalnych” usługach, które mogą wpływać na niezliczone inne rzeczy w Twojej aplikacji. Dobrym przykładem jest usługa użytkownika, w której może się zdarzyć wiele zdarzeń, takich jak logowanie, wylogowanie, aktualizacja, bezczynność itp. Uważam, że to tutaj nadawanie ma sens, ponieważ każdy zasięg może nasłuchiwać zdarzenia, bez nawet wstrzyknięcie usługi i nie trzeba oceniać żadnych wyrażeń ani wyników pamięci podręcznej w celu sprawdzenia zmian. Po prostu odpala i zapomina (więc upewnij się, że jest to powiadomienie o pożarze i zapomnieniu, a nie coś, co wymaga działania)
Powyższa usługa nadałaby komunikat do każdego zakresu, gdy funkcja save () zakończyła się i dane były prawidłowe. Alternatywnie, jeśli jest to zasób $ lub przesłanie ajax, przenieś wywołanie rozgłoszeniowe do wywołania zwrotnego, aby uruchamiało się, gdy serwer odpowiedział. Emisje pasują szczególnie do tego wzorca, ponieważ każdy słuchacz po prostu czeka na wydarzenie bez konieczności sprawdzania zasięgu przy każdym $ streszczeniu. Słuchacz wyglądałby następująco:
Jedną z korzyści tego jest eliminacja wielu zegarków. Jeśli łączysz pola lub wyprowadzasz wartości jak w powyższym przykładzie, będziesz musiał obserwować zarówno imię, jak i nazwisko. Oglądanie funkcji getUser () działałoby tylko wtedy, gdy obiekt użytkownika został zastąpiony podczas aktualizacji, nie uruchamiałby się, gdyby obiekt użytkownika został po prostu zaktualizowany. W takim przypadku musisz zrobić dokładny zegarek, który jest bardziej intensywny.
$ broadcast wysyła wiadomość z zakresu, do którego została wywołana, do dowolnych zakresów potomnych. Więc wywołanie go z $ rootScope będzie uruchamiane na każdym zasięgu. Jeśli na przykład miałbyś nadawać z zakresu kontrolera, uruchamiałby się tylko w zakresach dziedziczących z zakresu kontrolera. $ emit idzie w przeciwnym kierunku i zachowuje się podobnie jak zdarzenie DOM, ponieważ bąbelki w górę łańcucha zasięgu.
Należy pamiętać, że istnieją scenariusze, w których $ broadcast ma wiele sensu, i są scenariusze, w których $ watch jest lepszą opcją - szczególnie jeśli jest w zakresie izolowanym z bardzo specyficznym wyrażeniem watch.
źródło
Używam podobnego podejścia jak @dtheodot, ale używam obietnicy kątowej zamiast przekazywać wywołania zwrotne
Następnie, gdziekolwiek, użyj
myService.setFoo(foo)
metody aktualizacjifoo
w serwisie. W kontrolerze możesz użyć go jako:Pierwsze dwa argumenty
then
to wywołania zwrotne sukcesu i błędu, trzeci to powiadomienie zwrotne.Referencje dla $ q.
źródło
$scope.$watch
na zmiennej usługi nie działało (zakres, na który patrzyłem, to modal, który odziedziczył po nim$rootScope
) - zadziałało. Fajna sztuczka, dziękuję za udostępnienie!Bez zegarków i wywołań zwrotnych obserwatora ( http://jsfiddle.net/zymotik/853wvv7s/ ):
JavaScript:
HTML
Ten JavaScript działa, gdy przekazujemy obiekt z usługi, a nie wartość. Gdy obiekt JavaScript jest zwracany z usługi, Angular dodaje zegarki do wszystkich swoich właściwości.
Zauważ też, że używam „var self = this”, ponieważ muszę zachować odniesienie do oryginalnego obiektu, gdy wykonuje się limit czasu $, w przeciwnym razie „this” będzie odnosić się do obiektu okna.
źródło
$scope.count = service.count
nie działa.$scope.data = service.data
<p>Count: {{ data.count }}</p>
Natknąłem się na to pytanie, szukając czegoś podobnego, ale myślę, że zasługuje na dokładne wyjaśnienie tego, co się dzieje, a także kilka dodatkowych rozwiązań.
Gdy wyrażenie HTML, takie jak użyte, jest obecne w kodzie HTML, Angular automatycznie konfiguruje
$watch
for$scope.foo
i będzie aktualizować HTML przy każdej$scope.foo
zmianie.Niewypowiedzianym problemem jest to, że jedna z dwóch rzeczy wpływa na to
aService.foo
, że zmiany nie są wykrywane. Te dwie możliwości to:aService.foo
ustawia się za każdym razem do nowej tablicy, powodując, że odwołanie do niej jest nieaktualne.aService.foo
jest aktualizowany w taki sposób, że$digest
cykl nie jest uruchamiany podczas aktualizacji.Problem 1: Nieaktualne referencje
Biorąc pod uwagę pierwszą możliwość, zakładając, że
$digest
stosuje się a, jeśliaService.foo
zawsze była to ta sama tablica, zestaw automatycznie$watch
wykryłby zmiany, jak pokazano w poniższym fragmencie kodu.Rozwiązanie 1-a: Upewnij się, że tablica lub obiekt jest tym samym obiektem przy każdej aktualizacji
Pokaż fragment kodu
Jak widać, NG-repeat rzekomo przypisane do
aService.foo
nie aktualizuje kiedyaService.foo
zmiany, ale ng-repeat dołączone doaService2.foo
robi . Jest tak, ponieważ nasze odniesienie doaService.foo
jest nieaktualne, ale nasze odniesienie doaService2.foo
nie jest. Utworzyliśmy odwołanie do początkowej tablicy$scope.foo = aService.foo;
, która została następnie odrzucona przez usługę przy następnej aktualizacji, co oznacza, że$scope.foo
nie odnosi się już do tablicy, której chcieliśmy.Jednakże, chociaż istnieje kilka sposobów, aby upewnić się, że początkowe odniesienie jest utrzymywane w takcie, czasami może być konieczna zmiana obiektu lub tablicy. A co jeśli właściwość usługi odwołuje się do prymitywu takiego jak
String
lubNumber
? W takich przypadkach nie możemy po prostu polegać na referencji. Więc co można zrobić?Kilka podanych wcześniej odpowiedzi już daje pewne rozwiązania tego problemu. Jednak osobiście opowiadam się za zastosowaniem prostej metody sugerowanej przez Jina i thetallweeks w komentarzach:
Rozwiązanie 1-b: Dołącz usługę do zakresu i odwołanie
{service}.{property}
w kodzie HTML.Czyli po prostu zrób to:
HTML:
JS:
Pokaż fragment kodu
W ten sposób
$watch
zostanie rozwiązanyaService.foo
każdy$digest
, który uzyska poprawnie zaktualizowaną wartość.Jest to rodzaj tego, co próbujesz zrobić ze swoim obejściem, ale w znacznie mniejszym stopniu. Dodałeś niepotrzebne
$watch
w kontrolerze, który jawnie nakładafoo
się za$scope
każdym razem, gdy się zmienia. Nie potrzebujesz tego dodatkowego,$watch
gdy dołączaszaService
zamiastaService.foo
do$scope
i przypisujesz się bezpośrednio doaService.foo
znaczników.To wszystko dobrze i dobrze, jeśli zastosuje się
$digest
cykl. W powyższych przykładach użyłem$interval
usługi Angulara do aktualizacji tablic, która automatycznie uruchamia$digest
pętlę po każdej aktualizacji. Ale co, jeśli zmienne serwisowe (z jakiegokolwiek powodu) nie są aktualizowane w „świecie Angular”. Innymi słowy, czy nie mamy$digest
cyklu aktywowanego automatycznie przy każdej zmianie właściwości usługi?Problem 2: Brak
$digest
Wiele rozwiązań tutaj rozwiązuje ten problem, ale zgadzam się z Code Whisperer :
Dlatego wolałbym nadal używać
aService.foo
odwołania w znacznikach HTML, jak pokazano w drugim przykładzie powyżej, i nie musiałbym rejestrować dodatkowego wywołania zwrotnego w kontrolerze.Rozwiązanie 2: Użyj setera i gettera z
$rootScope.$apply()
Byłem zaskoczony, że nikt jeszcze nie zasugerował użycia setera i gettera . Ta funkcja została wprowadzona w ECMAScript5 i dlatego istnieje już od lat. Oczywiście oznacza to, że jeśli z jakiegoś powodu musisz obsługiwać naprawdę stare przeglądarki, ta metoda nie będzie działać, ale wydaje mi się, że JavaScript i gettery są znacznie słabo wykorzystywane. W tym konkretnym przypadku mogą być bardzo przydatne:
Pokaż fragment kodu
Tutaj dodałem „prywatny” zmienną w funkcji serwisowej:
realFoo
. Ten plik jest aktualizowany i pobierany przy użyciu odpowiednio funkcjiget foo()
iset foo()
naservice
obiekcie.Zwróć uwagę na użycie
$rootScope.$apply()
funkcji set. Zapewnia to, że Angular będzie świadomy wszelkich zmian wservice.foo
. Jeśli pojawią się błędy „inprog”, zobacz tę przydatną stronę referencyjną lub jeśli używasz Angular> = 1.3, możesz po prostu użyć$rootScope.$applyAsync()
.Uważaj na to, jeśli
aService.foo
jest bardzo często aktualizowany, ponieważ może to znacząco wpłynąć na wydajność. Jeśli wydajność byłaby problemem, możesz ustawić wzorzec obserwatora podobny do innych odpowiedzi tutaj za pomocą setera.źródło
services
nie dla właściwości należących do samej usługi.O ile mogę powiedzieć, nie musisz robić czegoś tak skomplikowanego jak to. Przypisałeś już foo z usługi do swojego zakresu, a ponieważ foo jest tablicą (i z kolei jest obiektem przypisywanym przez referencję!). Wszystko, co musisz zrobić, to coś takiego:
Jeśli jakaś inna zmienna w tym samym Ctrl jest zależna od zmiany Foo, to tak, potrzebujesz obserwować Foo i dokonać zmian w tej zmiennej. Ale pod warunkiem, że jest to proste oglądanie referencji, nie jest konieczne. Mam nadzieję że to pomoże.
źródło
$watch
pracować z prymitywem. Zamiast tego, definiuje metodę na usługę, która zwraca wartość prymitywną:somePrimitive() = function() { return somePrimitive }
I przypisana właściwość $ zakres do tej metody:$scope.somePrimitive = aService.somePrimitive;
. Następnie użyłem metody zakresu w HTML:<span>{{somePrimitive()}}</span>
$scope.foo = aService.foo
nie aktualizuje automatycznie zmiennej zasięgu.Możesz wstawić usługę do $ rootScope i oglądać:
W twoim kontrolerze:
Inną opcją jest użycie zewnętrznej biblioteki, takiej jak: https://github.com/melanke/Watch.JS
Współpracuje z: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS, Rhino 1.7+
Możesz obserwować zmiany jednego, wielu lub wszystkich atrybutów obiektu.
Przykład:
źródło
Możesz obejrzeć zmiany w samej fabryce, a następnie opublikować zmianę
Następnie w kontrolerze:
W ten sposób umieścisz cały powiązany kod fabryczny w jego opisie, możesz polegać tylko na transmisji z zewnątrz
źródło
== AKTUALIZACJA ==
Bardzo proste teraz w zegarku $.
Pióro tutaj .
HTML:
JavaScript:
źródło
Opierając się na odpowiedzi dtheodora, możesz użyć czegoś podobnego do poniższego, aby upewnić się, że nie zapomnisz wyrejestrować połączenia zwrotnego ... Niektórzy mogą jednak sprzeciwić się przekazaniu
$scope
usługi.Array.remove to metoda rozszerzenia, która wygląda następująco:
źródło
Oto moje ogólne podejście.
W twoim kontrolerze
źródło
Dla takich jak ja, którzy szukają prostego rozwiązania, robi to prawie dokładnie to, czego oczekujesz od używania normalnego $ watch w kontrolerach. Jedyną różnicą jest to, że ocenia ciąg w kontekście javascript, a nie w określonym zakresie. Będziesz musiał wprowadzić $ rootScope do swojej usługi, chociaż służy on tylko do prawidłowego podłączenia do cykli trawienia.
źródło
w obliczu bardzo podobnego problemu obejrzałem funkcję w zakresie i kazałem tej funkcji zwrócić zmienną usługi. Stworzyłem skrzypce js . kod znajdziesz poniżej.
źródło
Przyszedłem do tego pytania, ale okazało się, że mój problem polega na tym, że korzystałem z setInterval, kiedy powinienem był używać dostawcy kątowego $ interwału. Dotyczy to również setTimeout (zamiast tego użyj $ timeout). Wiem, że to nie jest odpowiedź na pytanie PO, ale może pomóc niektórym, ponieważ pomogło mi.
źródło
setTimeout
dowolnej innej funkcji innej niż Angular, ale nie zapomnij zawinąć kodu w wywołanie zwrotne$scope.$apply()
.Znalazłem naprawdę świetne rozwiązanie w drugim wątku z podobnym problemem, ale zupełnie innym podejściem. Źródło: AngularJS: $ watch wewnątrz dyrektywy nie działa po zmianie wartości $ rootScope
Zasadniczo rozwiązanie mówi NIE używać,
$watch
ponieważ jest to bardzo ciężkie rozwiązanie. Zamiast tego proponują użycie$emit
i$on
.Moim problemem było obejrzenie zmiennej w moim serwisie i zareagowanie zgodnie z dyrektywą . I powyższą metodą jest to bardzo proste!
Przykład mojego modułu / usługi:
Więc zasadniczo oglądać Moje strony
user
- zawsze, gdy jest ustawiony na nową wartość I stanu.$emit
user:change
Teraz w moim przypadku zastosowałem w dyrektywie :
Teraz w dyrektywie słucham
$rootScope
i na temat danej zmiany - odpowiednio reaguję. Bardzo łatwe i eleganckie!źródło
// service: (tutaj nic specjalnego)
// ctrl:
źródło
Trochę brzydkie, ale dodałem rejestrację zmiennych zakresu do mojej usługi dla przełącznika:
A następnie w kontrolerze:
źródło
this
zamiast tego wracasz z tej usługiself
?return this;
wyjść poza konstruktorów ;-)Spójrz na tego plunkera :: to najprostszy przykład, jaki mogłem wymyślić
http://jsfiddle.net/HEdJF/
źródło
Widziałem tutaj okropne wzorce obserwatorów, które powodują wycieki pamięci w dużych aplikacjach.
Mogę się trochę spóźnić, ale to takie proste.
Funkcja zegarka obserwuje zmiany referencyjne (typy pierwotne), jeśli chcesz obejrzeć coś w rodzaju wypychania tablicy, po prostu użyj:
someArray.push(someObj); someArray = someArray.splice(0);
Spowoduje to zaktualizowanie referencji i aktualizację zegarka z dowolnego miejsca. W tym metoda pobierania usług. Wszystko, co jest prymitywne, zostanie zaktualizowane automatycznie.
źródło
Jestem spóźniony do tej części, ale znalazłem lepszy sposób na zrobienie tego niż odpowiedź zamieszczona powyżej. Zamiast przypisywać zmienną do przechowywania wartości zmiennej usługi, utworzyłem funkcję dołączoną do zakresu, która zwraca zmienną usługi.
kontroler
Myślę, że zrobi to, co chcesz. Dzięki tej implementacji mój kontroler stale sprawdza wartość mojej usługi. Szczerze mówiąc, jest to o wiele prostsze niż wybrana odpowiedź.
źródło
Napisałem dwie proste usługi narzędziowe, które pomagają mi śledzić zmiany właściwości usługi.
Jeśli chcesz pominąć długie wyjaśnienie, możesz przejść cieśniną do jsfiddle
Ta usługa jest używana w kontrolerze w następujący sposób: Załóżmy, że masz usługę „testService” o właściwościach „prop1”, „prop2”, „prop3”. Chcesz obejrzeć i przypisać do zakresu „prop1” i „prop2”. Dzięki usłudze zegarka będzie to wyglądać tak:
Wywołałbym go na końcu mojego kodu asynchronicznego, aby uruchomić pętlę $ digest. Tak:
Wszystko to razem będzie wyglądać tak (możesz to uruchomić lub otworzyć skrzypce ):
źródło