W tym scenariuszu wyświetlam listę uczniów (tablicę) do widoku z ngFor
:
<li *ngFor="#student of students">{{student.name}}</li>
To wspaniałe, że aktualizuje się za każdym razem, gdy dodam innego ucznia do listy.
Jednak, gdy daję się pipe
do filter
o imieniu studenta,
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
Nie aktualizuje listy, dopóki nie wpiszę czegoś w filtrującym polu nazwiska ucznia.
Oto link do plnkr .
Hello_world.html
<h1>Students:</h1>
<label for="newStudentName"></label>
<input type="text" name="newStudentName" placeholder="newStudentName" #newStudentElem>
<button (click)="addNewStudent(newStudentElem.value)">Add New Student</button>
<br>
<input type="text" placeholder="Search" #queryElem (keyup)="0">
<ul>
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
</ul>
sort_by_name_pipe.ts
import {Pipe} from 'angular2/core';
@Pipe({
name: 'sortByName'
})
export class SortByNamePipe {
transform(value, [queryString]) {
// console.log(value, queryString);
return value.filter((student) => new RegExp(queryString).test(student.name))
// return value;
}
}
pure:false
w swojej rurze ichangeDetection: ChangeDetectionStrategy.OnPush
komponencie..test()
funkcji filtrującej. Dzieje się tak, ponieważ jeśli użytkownik wprowadzi ciąg zawierający znaki specjalne, takie jak:*
lub+
itp., Twój kod się zepsuje. Myślę, że powinieneś użyć.includes()
lub wyjść z ciągu zapytania z funkcją niestandardową.pure:false
i ustawienie stanu potoku rozwiąże problem. Modyfikowanie ChangeDetectionStrategy nie jest konieczne.Odpowiedzi:
Aby w pełni zrozumieć problem i możliwe rozwiązania, musimy omówić wykrywanie zmian kątowych - dla rur i komponentów.
Wykrywanie zmian rur
Rury bezpaństwowe / czyste
Domyślnie potoki są bezstanowe / czyste. Potoki bezstanowe / czyste po prostu przekształcają dane wejściowe w dane wyjściowe. Nic nie pamiętają, więc nie mają żadnych właściwości - tylko
transform()
metoda. Angular może zatem zoptymalizować traktowanie rur bezstanowych / czystych: jeśli ich dane wejściowe się nie zmieniają, rury nie muszą być wykonywane podczas cyklu wykrywania zmian. W przypadku rur takich jak{{power | exponentialStrength: factor}}
,power
ifactor
są wejściami.W przypadku tego pytania
"#student of students | sortByName:queryElem.value"
,students
iqueryElem.value
to wejścia, a przewódsortByName
jest bezstanowy / czystego.students
jest tablicą (odniesieniem).students
nie zmienia się - dlatego potok bezstanowy / czysty nie jest wykonywany.queryElem.value
zmienia się, dlatego wykonywany jest potok bezstanowy / czysty.Jednym ze sposobów rozwiązania problemu z tablicą jest zmiana odwołania do tablicy za każdym razem, gdy dodawany jest uczeń, tj. Tworzenie nowej tablicy za każdym razem, gdy dodawany jest uczeń. Moglibyśmy to zrobić za pomocą
concat()
:Chociaż to działa, nasz
addNewStudent()
metoda nie powinna być implementowana w określony sposób tylko dlatego, że używamy potoku. Chcemypush()
dodać do naszej tablicy.Stateful Pipes
Rury stanowe mają stan - zwykle mają właściwości, a nie tylko
transform()
metodę. Mogą wymagać oceny, nawet jeśli ich dane wejściowe nie uległy zmianie. Kiedy określimy, że potok jest stanowy / nieczysty -pure: false
- wtedy za każdym razem, gdy system wykrywania zmian Angulara sprawdza komponent pod kątem zmian i ten komponent używa potoku stanowego, sprawdza wyjście potoku, czy jego wejście uległo zmianie, czy nie.Brzmi to tak, jak chcemy, nawet jeśli jest mniej wydajne, ponieważ chcemy, aby potok był wykonywany, nawet jeśli
students
odniesienie nie uległo zmianie. Jeśli po prostu sprawimy, że potok będzie stanowy, otrzymamy błąd:Zgodnie z odpowiedzią @ drewmoore , „ten błąd występuje tylko w trybie programisty (który jest domyślnie włączony od wersji beta-0). Jeśli zadzwonisz
enableProdMode()
podczas ładowania aplikacji, błąd nie zostanie zgłoszony”. Dokumenty dotycząceApplicationRef.tick()
stanu:W naszym scenariuszu uważam, że błąd jest fałszywy / wprowadzający w błąd. Mamy potok stanowy i wyjście może się zmieniać za każdym razem, gdy jest wywoływane - może mieć efekty uboczne i to jest w porządku. NgFor jest oceniane po potoku, więc powinno działać poprawnie.
Jednak tak naprawdę nie możemy programować po wyrzuceniu tego błędu, więc jednym obejściem jest dodanie właściwości tablicy (tj. Stan) do implementacji potoku i zawsze zwracanie tej tablicy. Zobacz odpowiedź @ pixelbits na to rozwiązanie.
Możemy jednak być bardziej wydajni i, jak zobaczymy, nie będziemy potrzebować właściwości tablicy w implementacji potoku i nie będziemy potrzebować obejścia dla wykrywania podwójnej zmiany.
Wykrywanie zmian komponentów
Domyślnie przy każdym zdarzeniu przeglądarki wykrywanie zmian kątowych przechodzi przez każdy komponent, aby sprawdzić, czy się zmienił - sprawdzane są dane wejściowe i szablony (a może inne rzeczy?).
Jeśli wiemy, że składnik zależy tylko od jego właściwości wejściowych (i zdarzeń szablonu), a właściwości wejściowe są niezmienne, możemy zastosować znacznie wydajniejszą
onPush
strategię wykrywania zmian. Dzięki tej strategii, zamiast sprawdzania każdego zdarzenia przeglądarki, komponent jest sprawdzany tylko wtedy, gdy zmieniają się dane wejściowe i gdy wyzwalają się zdarzenia szablonu. I najwyraźniej nie otrzymujemy tegoExpression ... has changed after it was checked
błędu przy tym ustawieniu. Dzieje się tak, ponieważonPush
składnik nie jest ponownie sprawdzany, dopóki nie zostanie ponownie „zaznaczony” (ChangeDetectorRef.markForCheck()
). Dlatego powiązania szablonów i stanowe dane wyjściowe potoku są wykonywane / oceniane tylko raz. Potoki bezstanowe / czyste nadal nie są wykonywane, chyba że zmienią się ich dane wejściowe. Więc nadal potrzebujemy tu stanowej potoku.Oto rozwiązanie zasugerowane przez @EricMartinez: potok stanowy z
onPush
wykrywaniem zmian. Zobacz odpowiedź @ caffinatedmonkey dla tego rozwiązania.Zauważ, że w tym rozwiązaniu
transform()
metoda nie musi za każdym razem zwracać tej samej tablicy. Uważam to jednak za trochę dziwne: potok stanowy bez stanu. Myśląc o tym więcej ... potok stanowy prawdopodobnie powinien zawsze zwracać tę samą tablicę. W przeciwnym razie mógłby być używany tylko zonPush
komponentami w trybie deweloperskim.Po tym wszystkim, myślę, że podoba mi się kombinacja odpowiedzi @ Erica i @ pixelbits: potok stanowy, który zwraca to samo odniesienie do tablicy, z
onPush
wykrywaniem zmian, jeśli komponent na to pozwala. Ponieważ potok stanowy zwraca to samo odwołanie do tablicy, potok może być nadal używany z komponentami, które nie są skonfigurowane zonPush
.Plunker
Prawdopodobnie stanie się to idiomem Angulara 2: jeśli tablica zasila potok, a tablica może się zmienić (elementy w tablicy, a nie odwołanie do tablicy), musimy użyć potoku stanowego.
źródło
onPush
wykrywania zmian plnkr.co/edit/gRl0Pt9oBO6038kCXZPk?p=previewEric Martinez zauważył w komentarzach, dodając
pure: false
doPipe
dekoratora ichangeDetection: ChangeDetectionStrategy.OnPush
doComponent
dekoratora będzie rozwiązać problem. Oto pracujący plunkr. Zmiana naChangeDetectionStrategy.Always
również działa. Dlatego.Zgodnie z przewodnikiem angular2 na rurach :
Jeśli chodzi o
ChangeDetectionStrategy
, domyślnie wszystkie powiązania są sprawdzane w każdym cyklu. Gdypure: false
rura jest dodany, to uważa, metody wykrywania zmiany zmienia się odCheckAlways
sięCheckOnce
ze względu na wydajność. W przypadkuOnPush
powiązania dla składnika są sprawdzane tylko w przypadku zmiany właściwości wejściowej lub wyzwolenia zdarzenia. Aby uzyskać więcej informacji o wykrywaczach zmian, ważnej częściangular2
, zapoznaj się z poniższymi linkami:źródło
pure: false
potoku myślę, że metoda wykrywania zmian zmienia się z CheckAlways na CheckOnce” - to nie zgadza się z tym, co cytowałeś z dokumentacji. Moje rozumienie jest następujące: dane wejściowe potoku bezstanowego są domyślnie sprawdzane w każdym cyklu. W przypadku zmianytransform()
wywoływana jest metoda potoku w celu (ponownego) wygenerowania wyniku.transform()
Metoda stanowego potoku jest wywoływana w każdym cyklu, a jej wyjście jest sprawdzane pod kątem zmian. Zobacz też stackoverflow.com/a/34477111/215945 .OnPush
(tj. Komponent jest oznaczony jako „niezmienny”), stanowy potok wyjściowy nie musi się „stabilizować” - tj. Wydaje się, żetransform()
metoda jest uruchamiana tylko raz (prawdopodobnie wszystkie wykrywanie zmian dla komponentu jest uruchamiane tylko raz). Porównaj to z odpowiedzią @ pixelbits, w którejtransform()
metoda jest wywoływana wielokrotnie i musi się ustabilizować (zgodnie z inną odpowiedzią pixelbits ), stąd potrzeba użycia tego samego odniesienia do tablicy.transform
jest wywoływany tylko wtedy, gdy nowe dane są przesyłane, a nie wiele razy, aż dane wyjściowe się ustabilizują, prawda?Demo Plunkr
Nie musisz zmieniać ChangeDetectionStrategy. Wdrożenie stanowego potoku wystarczy, aby wszystko działało.
To jest potok stanowy (żadne inne zmiany nie zostały wprowadzone):
źródło
Expression 'students | sortByName:queryElem.value in HelloWorld@7:6' has changed after it was checked. Previous value: '[object Object],[object Object],[object Object]'. Current value: '[object Object],[object Object],[object Object]' in [students | sortByName:queryElem.value in HelloWorld@7:6]
Z dokumentacji kątowej
Czyste i nieczyste rury
Istnieją dwie kategorie rur: czyste i nieczyste. Rury są domyślnie czyste. Każda fajka, którą do tej pory widziałeś, była czysta. Czyścisz rurę, ustawiając jej czystą flagę na fałsz. Możesz uczynić FlyingHeroesPipe nieczystym w następujący sposób:
@Pipe({ name: 'flyingHeroesImpure', pure: false })
Zanim to zrobisz, zrozum różnicę między czystym a nieczystym, zaczynając od czystej fajki.
Czyste rury Angular wykonuje czystą potokę tylko wtedy, gdy wykryje czystą zmianę wartości wejściowej. Czysta zmiana to albo zmiana pierwotnej wartości wejściowej (ciąg, liczba, wartość logiczna, symbol), albo zmieniona referencja do obiektu (data, tablica, funkcja, obiekt).
Angular ignoruje zmiany w obiektach (złożonych). Nie wywoła czystego potoku, jeśli zmienisz miesiąc wejściowy, dodasz do tablicy wejściowej lub zaktualizujesz właściwość obiektu wejściowego.
Może się to wydawać restrykcyjne, ale jest też szybkie. Sprawdzenie odniesienia do obiektu jest szybkie - znacznie szybsze niż dokładne sprawdzenie różnic - dzięki czemu Angular może szybko określić, czy może pominąć zarówno wykonanie potoku, jak i aktualizację widoku.
Z tego powodu czysta potok jest preferowana, gdy możesz żyć ze strategią wykrywania zmian. Jeśli nie możesz, możesz użyć nieczystej rury.
źródło
Zamiast robić czyste: fałszywe. Możesz głęboko skopiować i zamienić wartość w komponencie na this.students = Object. assign ([], NEW_ARRAY); gdzie NEW_ARRAY to zmodyfikowana tablica.
Działa dla kątowej 6 i powinien działać również z innymi wersjami kątowymi.
źródło
Obejście problemu: ręcznie zaimportuj potok do konstruktora i wywołaj metodę transformacji przy użyciu tego potoku
Właściwie nie potrzebujesz nawet fajki
źródło
Dodaj do potoku dodatkowy parametr i zmień go zaraz po zmianie tablicy, a nawet przy czystym potoku lista zostanie odświeżona
niech pozycja przedmiotów | pipe: param
źródło
W tym przypadku użyłem mojego potoku w pliku ts do filtrowania danych. Jest to znacznie lepsze dla wydajności niż używanie czystych rur. Użyj w ts w ten sposób:
źródło
tworzenie zanieczyszczonych rur jest kosztowne w wydajności. więc nie twórz nieczystych potoków, raczej zmień odniesienie do zmiennej danych, tworząc kopię danych przy zmianie danych i ponownie przypisuj odniesienie do kopii w oryginalnej zmiennej danych.
źródło