Qual è l'equivalente angolare di un orologio AngularJS $?


217

In AngularJS eri in grado di specificare gli osservatori per osservare i cambiamenti nelle variabili di ambito utilizzando la $watchfunzione di $scope. Qual è l'equivalente di osservare i cambiamenti delle variabili (ad esempio, nelle variabili dei componenti) in Angular?


Usa get accessor in dattiloscritto.
jmvtrinidad

controlla questo articolo che spiega la differenza
Max Koretskyi

Risposte:


275

In Angular 2, il rilevamento delle modifiche è automatico ... $scope.$watch()e $scope.$digest()RIP

Sfortunatamente, la sezione Rilevamento modifiche della guida allo sviluppo non è ancora stata scritta (c'è un segnaposto nella parte inferiore della pagina Panoramica dell'architettura , nella sezione "Le altre cose").

Ecco la mia comprensione di come funziona il rilevamento delle modifiche:

  • Zone.js "monkey patch the world" - intercetta tutte le API asincrone nel browser (quando Angular è in esecuzione). Questo è il motivo per cui possiamo usare setTimeout()all'interno dei nostri componenti piuttosto che qualcosa di simile $timeout... perché setTimeout()è patch di scimmia.
  • Angular costruisce e mantiene un albero di "rilevatori di cambiamento". Esiste un tale rilevatore di modifiche (classe) per componente / direttiva. (È possibile accedere a questo oggetto iniettando ChangeDetectorRef.) Questi rilevatori di modifiche vengono creati quando Angular crea componenti. Tengono traccia dello stato di tutti i tuoi attacchi, per il controllo sporco. Questi sono, in un certo senso, simili all'automatico $watches()che Angular 1 imposterebbe per i {{}}binding di template.
    A differenza di Angular 1, il grafico di rilevamento delle modifiche è un albero diretto e non può avere cicli (questo rende Angular 2 molto più performante, come vedremo di seguito).
  • Quando viene generato un evento (all'interno della zona angolare), viene eseguito il codice che abbiamo scritto (il callback del gestore di eventi). Può aggiornare tutti i dati che desidera: il modello / stato dell'applicazione condivisa e / o lo stato di visualizzazione del componente.
  • Dopodiché, a causa degli hook aggiunti Zone.js, esegue l'algoritmo di rilevamento delle modifiche di Angular. Per impostazione predefinita (ovvero, se non stai utilizzando la onPushstrategia di rilevamento delle modifiche su nessuno dei tuoi componenti), ogni componente dell'albero viene esaminato una volta (TTL = 1) ... dall'alto, in ordine profondo. (Bene, se sei in modalità sviluppatore, il rilevamento delle modifiche viene eseguito due volte (TTL = 2). Vedi ApplicationRef.tick () per ulteriori informazioni su questo.) Esegue il controllo sporco su tutti i tuoi collegamenti, utilizzando quegli oggetti rilevatore di modifiche.
    • Gli hook del ciclo di vita vengono chiamati come parte del rilevamento delle modifiche.
      Se i dati del componente che si desidera controllare sono una proprietà di input primitiva (String, boolean, number), è possibile implementare ngOnChanges()per ricevere una notifica delle modifiche.
      Se la proprietà input è un tipo di riferimento (oggetto, array, ecc.), Ma il riferimento non è cambiato (ad esempio, hai aggiunto un elemento a un array esistente), dovrai implementarlo ngDoCheck()(vedi questa risposta SO per ulteriori informazioni su questo).
      È necessario modificare solo le proprietà del componente e / o le proprietà dei componenti discendenti (a causa dell'implementazione del percorso ad albero singolo, ovvero il flusso di dati unidirezionale). Ecco un plunker che lo viola. Anche le pipe stateful possono farti inciampare qui.
  • Per qualsiasi modifica di associazione rilevata, i componenti vengono aggiornati e quindi il DOM viene aggiornato. Il rilevamento delle modifiche è ora terminato.
  • Il browser nota che il DOM cambia e aggiorna lo schermo.

Altri riferimenti per saperne di più:


window.addEventListener () non attiva il rilevamento quando le variabili vengono modificate ... mi fa impazzire, non c'è niente da nessuna parte.
Albert James Teddy

@AlbertJamesTeddy, consulta la hostdocumentazione "Host Listeners" nel documento dell'API DirectiveMetadata . Spiega come ascoltare gli eventi globali dall'interno della zona angolare (quindi il rilevamento delle modifiche verrà attivato come desiderato). Questa risposta ha un plunker funzionante.
Mark Rajcok

questo collegamento sarebbe utile ..
refactoring

@MarkRajcok, mi sono preso la libertà di aggiungere un riferimento al mio articolo sul rilevamento delle modifiche. Spero non ti dispiaccia. Spiega in grande dettaglio cosa succede sotto il cofano.
Max Koretskyi

Per quanto riguarda il plunkr che viola la regola del flusso di dati unidirezionale, vorrei aggiungere che se esegui il plunkr con enableProdMode (), non vedrai alcun aggiornamento nella vista genitore, perché il rilevatore di modifiche viene eseguito solo una volta.
Mister_L

94

Questo comportamento fa ora parte del ciclo di vita del componente.

Un componente può implementare il metodo ngOnChanges nell'interfaccia OnChanges per ottenere l'accesso alle modifiche di input.

Esempio:

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

80
questo è vero solo per @Input (). se desideri tenere traccia delle modifiche ai dati del tuo componente, non funzionerà
LanderV

4
Non sono riuscito a ottenere modifiche di variabili semplici (booleane per esempio). Vengono rilevate solo le modifiche agli oggetti.
mtoloo

perché è necessario aggiungere un array "input" nel decoratore del componente? il rilevamento delle modifiche funzionerà anche senza questo.
Gil Epshtain,

69

Se, oltre all'associazione automatica a due vie, si desidera chiamare una funzione quando un valore cambia, è possibile interrompere la sintassi del collegamento a due vie per la versione più dettagliata.

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

è una scorciatoia per

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

(vedi ad esempio http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Potresti fare qualcosa del genere:

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


Nell'ultimo esempio volevi rimuovere [] attorno a ngModel?
Eugene Kulabuhov

16

Puoi usare getter functiono get accessorper agire come orologio su angolare 2.

Guarda la demo qui .

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

12

Ecco un altro approccio che utilizza le funzioni getter e setter per il modello.

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

5
Questo argomento è folle. Ho un oggetto con molte proprietà legate a una forma complessa. Non voglio aggiungere (change)gestori su ognuno di essi; Non voglio aggiungere messaggi di posta get|setselettronica a ogni proprietà nel mio modello; non aiuta aggiungere un get|setper this.object; ngOnChanges() rileva solo le modifiche a @Inputs . Santa manna! Cosa ci hanno fatto ??? Restituiscici una sorta di osservazione profonda!
Cody

8

Se vuoi renderlo vincolante a 2 vie, puoi usare [(yourVar)], ma devi implementare l' yourVarChangeevento e chiamarlo ogni volta che la tua variabile cambia.

Qualcosa di simile per monitorare il cambiamento dell'eroe

@Output() heroChange = new EventEmitter();

e poi quando il tuo eroe viene cambiato, chiama this.heroChange.emit(this.hero);

la [(hero)]rilegatura farà il resto per te

vedi esempio qui:

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


5

Questo non risponde direttamente alla domanda, ma in diverse occasioni sono arrivato a questa domanda di Stack Overflow per risolvere qualcosa che avrei usato $ watch per in angularJs. Ho finito per utilizzare un approccio diverso da quello descritto nelle risposte attuali e voglio condividerlo nel caso qualcuno lo trovi utile.

La tecnica che utilizzo per ottenere qualcosa di simile $watchè usare un BehaviorSubject( più sull'argomento qui ) in un servizio Angular e lasciare che i miei componenti si iscrivano per ottenere (guardare) le modifiche. Questo è simile a un $watchin angularJs, ma richiede un po 'più di configurazione e comprensione.

Nel mio componente:

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

Al mio servizio

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

Ecco una demo su Stackblitz


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.