Was ist das Angular-Äquivalent zu einer AngularJS $ -Uhr?

213

In AngularJS konnten Sie Beobachter angeben, um Änderungen in Bereichsvariablen mithilfe der $watchFunktion von zu beobachten $scope. Was entspricht der Suche nach Variablenänderungen (z. B. in Komponentenvariablen) in Angular?

Erwin
quelle
Verwenden Sie get accessor in Typoskript.
jmvtrinidad
Überprüfen Sie diesen Artikel , der den Unterschied erklärt
Max Koretskyi

Antworten:

268

In Angular 2 erfolgt die Änderungserkennung automatisch ... $scope.$watch()und $scope.$digest()RIP

Leider ist der Abschnitt "Änderungserkennung" des Entwicklungshandbuchs noch nicht geschrieben (es gibt einen Platzhalter am unteren Rand der Seite " Architekturübersicht " im Abschnitt "Das andere Zeug").

Hier ist mein Verständnis der Funktionsweise der Änderungserkennung:

  • Zone.js "Affe patcht die Welt" - fängt alle asynchronen APIs im Browser ab (wenn Angular ausgeführt wird). Dies ist der Grund, warum wir setTimeout()in unseren Komponenten etwas verwenden können, anstatt so etwas wie $timeout... weil setTimeout()Affen gepatcht sind.
  • Angular erstellt und verwaltet einen Baum von "Änderungsdetektoren". Pro Komponente / Direktive gibt es einen solchen Änderungsdetektor (Klasse). (Sie können durch Injizieren auf dieses Objekt zugreifen ChangeDetectorRef.) Diese Änderungsdetektoren werden erstellt, wenn Angular Komponenten erstellt. Sie verfolgen den Status aller Ihrer Bindungen, um sie schmutzig zu überprüfen. Diese ähneln in gewissem Sinne der Automatik $watches(), die Angular 1 für {{}}Vorlagenbindungen einrichten würde .
    Im Gegensatz zu Angular 1 ist das Änderungserkennungsdiagramm ein gerichteter Baum und kann keine Zyklen aufweisen (dies macht Angular 2 viel leistungsfähiger, wie wir weiter unten sehen werden).
  • Wenn ein Ereignis ausgelöst wird (innerhalb der Winkelzone), wird der von uns geschriebene Code (der Rückruf des Ereignishandlers) ausgeführt. Es kann alle gewünschten Daten aktualisieren - das Modell / den Status der gemeinsam genutzten Anwendung und / oder den Ansichtsstatus der Komponente.
  • Danach wird aufgrund der hinzugefügten Hooks Zone.js der Änderungserkennungsalgorithmus von Angular ausgeführt. Standardmäßig (dh wenn Sie die onPushÄnderungserkennungsstrategie für keine Ihrer Komponenten verwenden) wird jede Komponente im Baum einmal (TTL = 1) ... von oben in der Reihenfolge der Tiefe zuerst untersucht. (Wenn Sie sich im Entwicklungsmodus befinden, wird die Änderungserkennung zweimal ausgeführt (TTL = 2). Weitere Informationen hierzu finden Sie in ApplicationRef.tick () .) Mit diesen Änderungsdetektorobjekten werden alle Bindungen schmutzig überprüft.
    • Lifecycle-Hooks werden als Teil der Änderungserkennung aufgerufen.
      Wenn die zu überwachenden Komponentendaten eine primitive Eingabeeigenschaft (String, Boolean, Number) sind, können Sie implementieren ngOnChanges(), um über Änderungen benachrichtigt zu werden.
      Wenn die Eingangs Eigenschaft ein Referenztyp (Objekt, ein Array, etc.), aber die Referenz hat sich nicht verändert (zB hinzugefügt Sie ein Element auf ein bestehendes Array), müssen Sie implementieren ngDoCheck()(siehe diese SO Antwort für mehr dazu).
      Sie sollten nur die Eigenschaften der Komponente und / oder Eigenschaften von untergeordneten Komponenten ändern (aufgrund der Implementierung eines einzelnen Tree Walks - dh des unidirektionalen Datenflusses). Hier ist ein Plunker , der das verletzt. Stateful Pipes können Sie auch hierher stolpern .
  • Für alle verbindlichen Änderungen, die gefunden werden, werden die Komponenten aktualisiert und anschließend das DOM aktualisiert. Die Änderungserkennung ist jetzt abgeschlossen.
  • Der Browser bemerkt die DOM-Änderungen und aktualisiert den Bildschirm.

Weitere Referenzen, um mehr zu erfahren:

Mark Rajcok
quelle
window.addEventListener () löst keine Erkennung aus, wenn Variablen geändert werden ... es macht mich verrückt, da ist nirgendwo etwas dran.
Albert James Teddy
@AlbertJamesTeddy finden Sie in der hostDokumentation "Host Listener" im DirectiveMetadata API-Dokument . Es wird erklärt, wie globale Ereignisse innerhalb der Winkelzone abgehört werden (sodass die Änderungserkennung wie gewünscht ausgelöst wird). Diese Antwort hat einen funktionierenden Plunker.
Mark Rajcok
Dieser Link wäre hilfreich ..
Refactor
@ MarkRajcok, ich habe mir erlaubt, einen Verweis auf meinen Artikel zur Änderungserkennung hinzuzufügen. Hoffe es macht dir nichts aus. Es erklärt sehr detailliert, was unter der Haube passiert.
Max Koretskyi
In Bezug auf das plunkr, das gegen die unidirektionale Datenflussregel verstößt, möchte ich hinzufügen, dass beim Ausführen des plunkr mit enableProdMode () in der übergeordneten Ansicht keine Aktualisierung angezeigt wird, da der Änderungsdetektor nur einmal ausgeführt wird.
Mister_L
93

Dieses Verhalten ist jetzt Teil des Komponentenlebenszyklus.

Eine Komponente kann die ngOnChanges-Methode in der OnChanges- Schnittstelle implementieren, um Zugriff auf Eingabeänderungen zu erhalten.

Beispiel:

import {Component, Input, OnChanges} from 'angular2/core';


@Component({
  selector: 'hero-comp',
  templateUrl: 'app/components/hero-comp/hero-comp.html',
  styleUrls: ['app/components/hero-comp/hero-comp.css'],
  providers: [],
  directives: [],

  pipes: [],
  inputs:['hero', 'real']
})
export class HeroComp implements OnChanges{
  @Input() hero:Hero;
  @Input() real:string;
  constructor() {
  }
  ngOnChanges(changes) {
      console.log(changes);
  }
}
toskv
quelle
77
Dies gilt nur für @Input (). Wenn Sie Änderungen der eigenen Daten Ihrer Komponente verfolgen möchten, funktioniert dies nicht
LanderV
4
Ich konnte keine Änderungen an einfachen Variablen (z. B. Boolescher Wert) erhalten. Es werden nur Objektänderungen erkannt.
Mtoloo
Warum muss im Dekorator der Komponente ein Array "Eingaben" hinzugefügt werden? Die Änderungserkennung funktioniert auch ohne diese Funktion.
Gil Epshtain
68

Wenn Sie zusätzlich zur automatischen bidirektionalen Bindung eine Funktion aufrufen möchten, wenn sich ein Wert ändert, können Sie die Syntax der bidirektionalen Bindungsverknüpfung auf die ausführlichere Version umstellen.

<input [(ngModel)]="yourVar"></input>

ist eine Abkürzung für

<input [ngModel]="yourVar" (ngModelChange)="yourVar=$event"></input>

(Siehe z. B. http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Sie könnten so etwas tun:

<input [(ngModel)]="yourVar" (ngModelChange)="changedExtraHandler($event)"></input>

Supermikko
quelle
Im letzten Beispiel wollten Sie [] um ngModel entfernen?
Eugene Kulabuhov
16

Sie können getter functionoder get accessorals Uhr auf Winkel 2 verwenden.

Siehe Demo hier .

import {Component} from 'angular2/core';

@Component({
  // Declare the tag name in index.html to where the component attaches
  selector: 'hello-world',

  // Location of the template for this component
  template: `
  <button (click)="OnPushArray1()">Push 1</button>
  <div>
    I'm array 1 {{ array1 | json }}
  </div>
  <button (click)="OnPushArray2()">Push 2</button>
  <div>
    I'm array 2 {{ array2 | json }}
  </div>
  I'm concatenated {{ concatenatedArray | json }}
  <div>
    I'm length of two arrays {{ arrayLength | json }}
  </div>`
})
export class HelloWorld {
    array1: any[] = [];
    array2: any[] = [];

    get concatenatedArray(): any[] {
      return this.array1.concat(this.array2);
    }

    get arrayLength(): number {
      return this.concatenatedArray.length;
    }

    OnPushArray1() {
        this.array1.push(this.array1.length);
    }

    OnPushArray2() {
        this.array2.push(this.array2.length);
    }
}
jmvtrinidad
quelle
12

Hier ist ein anderer Ansatz, bei dem Getter- und Setter-Funktionen für das Modell verwendet werden.

@Component({
  selector: 'input-language',
  template: `
  …
  <input 
    type="text" 
    placeholder="Language" 
    [(ngModel)]="query" 
  />
  `,
})
export class InputLanguageComponent {

  set query(value) {
    this._query = value;
    console.log('query set to :', value)
  }

  get query() {
    return this._query;
  }
}
wählen
quelle
4
Dieses Thema ist verrückt. Ich habe ein Objekt mit vielen Eigenschaften, die an eine komplexe Form gebunden sind. Ich möchte nicht (change)jedem einzelnen Handler hinzufügen . Ich möchte nicht get|setsjeder Eigenschaft in meinem Modell s hinzufügen . es wird nicht helfen, ein get|setfür hinzuzufügen this.object; ngOnChanges() erkennt nur Änderungen an @Inputs . Heiliges Manna! Was haben sie mit uns gemacht ??? Geben Sie uns eine Art Deep Watch zurück!
Cody
6

Wenn Sie es in [(yourVar)]beide Richtungen binden möchten, können Sie es verwenden , müssen es jedoch implementierenyourVarChange es in beide Ereignis und es jedes Mal aufrufen, wenn sich Ihre Variable ändert.

So etwas, um den Heldenwechsel zu verfolgen

@Output() heroChange = new EventEmitter();

und wenn sich dein Held umzieht, ruf an this.heroChange.emit(this.hero);

Die [(hero)]Bindung erledigt den Rest für Sie

siehe Beispiel hier:

http://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview

Mohy Eldeen
quelle
2

Dies beantwortet die Frage nicht direkt, aber ich bin bei verschiedenen Gelegenheiten auf diese Stapelüberlauffrage gestoßen, um etwas zu lösen, für das ich $ watch in angleJs verwenden würde. Am Ende habe ich einen anderen Ansatz als in den aktuellen Antworten beschrieben gewählt und möchte ihn teilen, falls jemand ihn nützlich findet.

Die Technik, mit der ich etwas Ähnliches erreiche, $watchbesteht darin, ein BehaviorSubject( mehr zum Thema hier ) in einem Angular-Dienst zu verwenden und meine Komponenten es abonnieren zu lassen, um die Änderungen zu erhalten (zu beobachten). Dies ähnelt a $watchin angleJs, erfordert jedoch etwas mehr Einrichtung und Verständnis.

In meiner Komponente:

export class HelloComponent {
  name: string;
  // inject our service, which holds the object we want to watch.
  constructor(private helloService: HelloService){
    // Here I am "watching" for changes by subscribing
    this.helloService.getGreeting().subscribe( greeting => {
      this.name = greeting.value;
    });
  }
}

In meinem Dienst

export class HelloService {
  private helloSubject = new BehaviorSubject<{value: string}>({value: 'hello'});
  constructor(){}
  // similar to using $watch, in order to get updates of our object 
  getGreeting(): Observable<{value:string}> {
    return this.helloSubject;
  }
  // Each time this method is called, each subscriber will receive the updated greeting.
  setGreeting(greeting: string) {
    this.helloSubject.next({value: greeting});
  }
}

Hier ist eine Demo zu Stackblitz

John
quelle