Co to jest Angular równoważny zegarkowi AngularJS $?

213

W AngularJS można było określić obserwatorów, którzy będą obserwować zmiany zmiennych zakresu za pomocą $watchfunkcji $scope. Jaki jest odpowiednik obserwowania zmian zmiennych (na przykład zmiennych składowych) w Angular?

Erwin
źródło
Użyj get accessor w maszynopisie.
jmvtrinidad
sprawdź ten artykuł, który wyjaśnia różnicę
Max Koretskyi

Odpowiedzi:

268

W Angular 2 wykrywanie zmian odbywa się automatycznie ... $scope.$watch()i $scope.$digest()RIP

Niestety sekcja Wykrywanie zmian w przewodniku dla deweloperów nie została jeszcze napisana (w dolnej części strony Przegląd architektury znajduje się symbol zastępczy , w sekcji „Inne rzeczy”).

Oto moje rozumienie działania detekcji zmian:

  • Zone.js „małpa łata świat” - przechwytuje wszystkie asynchroniczne interfejsy API w przeglądarce (gdy Angular działa). To dlatego możemy używać setTimeout()wewnątrz naszych komponentów zamiast czegoś takiego jak $timeout... ponieważ setTimeout()jest załatany małpą.
  • Angular buduje i utrzymuje drzewo „detektorów zmian”. Istnieje jeden taki detektor zmian (klasa) na komponent / dyrektywę. (Możesz uzyskać dostęp do tego obiektu przez wstrzyknięcie ChangeDetectorRef.) Te detektory zmian są tworzone, gdy Angular tworzy komponenty. Śledzą stan wszystkich twoich powiązań, w celu brudnego sprawdzania. W pewnym sensie są one podobne do automatycznych $watches()ustawień Angulara 1 dla {{}}powiązań szablonów.
    W przeciwieństwie do Angulara 1, wykres wykrywania zmian jest ukierunkowanym drzewem i nie może mieć cykli (to sprawia, że ​​Angular 2 jest o wiele bardziej wydajny, jak zobaczymy poniżej).
  • Po uruchomieniu zdarzenia (w strefie Angular) uruchamiany jest kod, który napisaliśmy (wywołanie zwrotne programu obsługi zdarzeń). Może aktualizować dowolne dane - współdzielony model / stan aplikacji i / lub stan widoku komponentu.
  • Następnie, z powodu dodanych hooków Zone.js, następnie uruchamia algorytm wykrywania zmian Angulara. Domyślnie (tj. Jeśli nie używasz onPushstrategii wykrywania zmian na żadnym ze swoich komponentów), każdy element w drzewie jest sprawdzany raz (TTL = 1) ... od góry, w kolejności od głębokości do pierwszej. (Cóż, jeśli jesteś w trybie deweloperskim, wykrywanie zmian działa dwa razy (TTL = 2). Więcej informacji na ten temat znajdziesz w ApplicationRef.tick () .) Wykonuje sprawdzanie wszystkich powiązań przy użyciu tych obiektów wykrywacza zmian.
    • Haki cyklu życia są wywoływane w ramach wykrywania zmian.
      Jeśli dane komponentu, które chcesz oglądać, są podstawową właściwością wejściową (ciąg, wartość logiczna, liczba), możesz zaimplementować, ngOnChanges()aby otrzymywać powiadomienia o zmianach.
      Jeżeli nieruchomość jest rodzajem wejścia odniesienia (obiekt, tablica itd.), Ale odwołanie nie uległ zmianie (np dodano element do istniejącej tablicy), trzeba wdrożyć ngDoCheck()(patrz to tak odpowiedź na dłużej na to).
      Powinieneś zmienić tylko właściwości komponentu i / lub właściwości komponentów potomnych (ze względu na implementację pojedynczego przejścia drzewa - tj. Jednokierunkowy przepływ danych). Oto plunker, który to narusza. Potężne rury mogą cię tutaj potknąć .
  • W przypadku wykrytych zmian powiązań komponenty są aktualizowane, a następnie DOM jest aktualizowany. Wykrywanie zmian jest teraz zakończone.
  • Przeglądarka zauważa zmiany DOM i aktualizuje ekran.

Inne odniesienia, aby dowiedzieć się więcej:

Mark Rajcok
źródło
window.addEventListener () nie wyzwala wykrywania, gdy zmienne są zmieniane ... doprowadza mnie do szaleństwa, nigdzie nie ma na tym nic.
Albert James Teddy
@AlbertJamesTeddy, zapoznaj się z hostdokumentacją „Host nasłuchiwania” w dokumentacji interfejsu API dyrektywy Metadata . Wyjaśnia, jak słuchać globalnych wydarzeń z wewnątrz strefy kątowej (więc wykrywanie zmian będzie uruchamiane zgodnie z potrzebami). Ta odpowiedź ma działającego plunkera.
Mark Rajcok
ten link byłby pomocny ..
refaktor
@MarkRajcok, mogłem dodać odniesienie do mojego artykułu na temat wykrywania zmian. Mam nadzieję, że nie masz nic przeciwko. Bardzo szczegółowo wyjaśnia, co dzieje się pod maską.
Max Koretskyi
Jeśli chodzi o plunkr, który narusza zasadę jednokierunkowego przepływu danych, chciałbym dodać, że jeśli uruchomisz plunkr za pomocą enableProdMode (), nie zobaczysz żadnej aktualizacji w widoku nadrzędnym, ponieważ detektor zmian działa tylko raz.
Mister_L
93

To zachowanie jest teraz częścią cyklu życia komponentu.

Komponent może zaimplementować metodę ngOnChanges w interfejsie OnChanges , aby uzyskać dostęp do zmian danych wejściowych.

Przykład:

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
źródło
77
dotyczy to tylko @Input (). jeśli chcesz śledzić zmiany własnych danych komponentu, to nie zadziała
LanderV
4
Nie mogłem uzyskać zmian prostych zmiennych (na przykład boolean). Wykryto tylko zmiany obiektów.
mtoloo
dlaczego trzeba dodać tablicę „danych wejściowych” w dekoratorze komponentu? wykrywanie zmian również będzie działać bez tego.
Gil Epshtain
68

Jeśli oprócz automatycznego wiązania dwukierunkowego chcesz wywołać funkcję, gdy zmienia się wartość, możesz przerwać składnię skrótu wiązania dwustronnego do wersji bardziej szczegółowej.

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

jest skrótem od

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

(patrz np. http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Możesz zrobić coś takiego:

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

supermikko
źródło
W ostatnim przykładzie chciałeś usunąć [] wokół ngModel?
Eugene Kulabuhov
16

Możesz używać getter functionlub get accessordziałać jako zegarek na kanciastym 2.

Zobacz demo tutaj .

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
źródło
12

Oto inne podejście wykorzystujące funkcje gettera i settera dla modelu.

@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;
  }
}
Wybierz
źródło
4
Ten temat jest szalony. Mam obiekt o wielu właściwościach związanych ze złożoną formą. Nie chcę dodawać (change)programów obsługi do każdego z nich; Nie chcę dodawać get|setss do każdej właściwości w moim modelu; to nie pomoże, aby dodać get|setdo this.object; ngOnChanges() wykrywa tylko zmiany w @Inputs . Święta manna! Co oni nam zrobili ??? Oddaj nam jakiś rodzaj głębokiej obserwacji!
Cody
6

Jeśli chcesz uczynić to wiązaniem dwukierunkowym, możesz użyć [(yourVar)], ale musisz zaimplementować yourVarChangezdarzenie i wywołać je za każdym razem, gdy zmieniasz zmienną.

Coś takiego do śledzenia zmiany bohatera

@Output() heroChange = new EventEmitter();

a potem, gdy twój bohater się zmieni, zadzwoń this.heroChange.emit(this.hero);

[(hero)]wiążące zrobi resztę za Ciebie

patrz przykład tutaj:

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

Mohy Eldeen
źródło
2

To nie odpowiada bezpośrednio na pytanie, ale przy różnych okazjach padałem na to pytanie dotyczące przepełnienia stosu, aby rozwiązać coś, czego użyłbym $ watch w angularJs. Skończyło się na innym podejściu niż opisano w bieżących odpowiedziach i chcę się nim podzielić, na wypadek, gdyby ktoś uznał to za przydatne.

Techniką, której używam, aby osiągnąć coś podobnego, $watchjest użycie BehaviorSubject( więcej na ten temat tutaj ) w usłudze Angular i pozwolenie moim komponentom na subskrypcję, aby uzyskać (obserwować) zmiany. Jest to podobne do $watchw angularJs, ale wymaga nieco więcej konfiguracji i zrozumienia.

W moim komponencie:

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;
    });
  }
}

W mojej służbie

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});
  }
}

Oto demo na Stackblitz

Jan
źródło