Ta dyskusja wymienia kilka strategii https://github.com/angular/angular/issues/6674#issuecomment-174699245
pytanie ma wiele konsekwencji. W niektórych przypadkach obserwowalne powinny być zarządzane poza ramami.
A. Można skanować wszystkie obserwable przed bootstrap
B. Bootstrap następnie zeskanować wszystkie obserwable przed przekazaniem obiektu do elementu górnego poziomu.
C. Możesz także zmienić metodę changeDetection w komponencie, aby wyzwalać zmiany wejść lub ręcznie wywoływać detektor zmian.
D. Jeśli używasz | async, to powinno być używane tylko na najwyższym poziomie, jeśli nie masz ochoty używać .subscribe, ale naprawdę powinieneś po prostu użyć .subscribe.
E. Jeśli używasz | async wszędzie, to co naprawdę robisz, odwraca kontrolę nad renderingiem do obserwowalnych, co oznacza, że wróciliśmy do Angular1 dni kaskadowych zmian, więc albo musisz zrobić C, D, B lub A
ChangeDetectionStrategy nie wydaje się działać w tej chwili. Najprościej ustawić komponent jako Wolnostojący.
Możemy również użyć haka do trybu życia ngOnInit w celu usunięcia komponentu z drzewa detekcji zmian. Będziesz musiał uruchomić this.ref.detach(); gdzie ref jest wstrzykiwany poprzez ChangeDetectorRef
ngOnInit() {
this.ref.detach();
}
makeYourChanges() {
this.ref.reattach(); // attach back to change detector tree
this.data.value = Math.random() + ''; // make changes
this.ref.detectChanges(); // check as dirty
this.ref.detach(); // remove from tree
// zone.js triggers changes
}
ChangeDetectorRef
Można również nie należą zone.js i ręcznie kontrolować wszystkie zmiany. Możesz także wstrzyknąć NgZone, aby uruchomić operację poza strefą.js, więc nie będzie ona określać kątowo, aby wyzwalać chane. Na przykład,
// this example might need a refactor to work with rxjs 5
export class Timeflies {
pos = 'absolute';
color = 'red';
letters: LetterConfig[];
constructor(
private service: Message,
private el: ElementRef,
private zone: NgZone) {
}
ngOnInit() {
// initial mapping (before mouse moves)
this.letters = this.service.message.map(
(val, idx) => ({
text: val,
top: 100,
left: (idx * 20 + 50),
index: idx
})
);
this.zone.runOutsideAngular(() => {
Observable
.fromEvent(this.el.nativeElement, 'mousemove')
.map((e: MouseEvent) => {
//var offset = getOffset(this.el);
// subtract offset of the element
var o = this.el.nativeElement.getBoundingClientRect();
return {
offsetX: e.clientX - o.left,
offsetY: e.clientY - o.top
};
})
.flatMap(delta => {
return Observable
.fromArray(this.letters
.map((val, index) => ({
letter: val.text,
delta,
index
})));
})
.flatMap(letterConfig => {
return Observable
.timer((letterConfig.index + 1) * 100)
.map(() => ({
text: letterConfig.letter,
top: letterConfig.delta.offsetY,
left: letterConfig.delta.offsetX + letterConfig.index * 20 + 20,
index: letterConfig.index
}));
})
.subscribe(letterConfig => {
// to render the letters, put them back into app zone
this.zone.run(() => this.letters[letterConfig.index] = letterConfig);
});
});//zone
}
}
Dzięki Mark! Elvis załatwił sprawę! Ale nie rozumiem jeszcze przepływu renderowania. Szablon jest renderowany za każdym razem, gdy następuje zmiana we właściwościach komponentu? –
@ TonyAlexanderHild, podczas wykrywania zmiany kąta (która działa po każdym zdarzeniu), domyślnie wszystkie powiązania widoku/szablonu są brudne, co oznacza, że są sprawdzane pod kątem zmian. Gdy dane zwracają się z serwera, jest to zdarzenie, więc przełącz się na wykrywanie zmian. 'model.Nazwa' jest brudny sprawdzony i stwierdzono, że został zmieniony, więc Angular aktualizuje DOM. Po Angular zwraca kontrolę do przeglądarki, widzi zmiany DOM i aktualizuje to, co widzimy na ekranie. –
Dzięki @MarkRajcok! Teraz jest jasne. –