web-dev-qa-db-fra.com

Quel est le Angular équivalent à une montre AngularJS $?

Dans AngularJS, vous avez été en mesure de spécifier des observateurs pour observer les modifications des variables de périmètre à l’aide de la fonction $watch de $scope. Quelle est l'équivalent de surveiller les modifications de variables (dans, par exemple, les variables de composant) dans Angular?

201
Erwin

Dans Angular 2, la détection des modifications est automatique ... $scope.$watch() et $scope.$digest() R.I.P.

Malheureusement, la section Détection de changement du guide de développement n’est pas encore écrite (il existe un espace réservé au bas de la page Aperçu de l’architecture , dans la section "Autres éléments").

Voici ce que je comprends du fonctionnement de la détection des changements:

  • Zone.js "monkey corrige le monde" - il intercepte toutes les API asynchrones du navigateur (lorsque Angular s'exécute). C'est pourquoi nous pouvons utiliser setTimeout() dans nos composants plutôt que quelque chose comme $timeout..., car setTimeout() est patché.
  • Angular construit et maintient un arbre de "détecteurs de changement". Il existe un tel détecteur de changement (classe) par composant/directive. (Vous pouvez accéder à cet objet en injectant ChangeDetectorRef .) Ces détecteurs de changement sont créés lorsque Angular crée des composants. Ils gardent une trace de l'état de toutes vos liaisons, pour vérification. Celles-ci sont, dans un sens, similaires à la $watches() automatique que Angular 1 devrait configurer pour les liaisons de modèle {{}}.
    Contrairement à Angular 1, le graphe de détection de changement est un arbre dirigé et ne peut pas avoir de cycles (cela rend Angular 2 beaucoup plus performant, comme nous le verrons plus loin).
  • Lorsqu'un événement est déclenché (dans la zone Angular,), le code que nous avons écrit (le rappel du gestionnaire d'événements) est exécuté. Il peut mettre à jour toutes les données souhaitées - le modèle/l'état d'application partagée et/ou l'état d'affichage du composant.
  • Après cela, en raison des crochets ajoutés à Zone.js, il exécute ensuite l'algorithme de détection de changement d'Angular. Par défaut (c’est-à-dire si vous n’utilisez pas la stratégie de détection de changement onPush sur l’un de vos composants), chaque composant de l’arborescence est examiné une fois (TTL = 1) ... du haut vers le haut, en premier ordre. (Eh bien, si vous êtes en mode dev, la détection des modifications est exécutée deux fois (TTL = 2). Voir ApplicationRef.tick () pour plus d'informations.) Il effectue une vérification à blanc de toutes vos liaisons, en utilisant ces objets détecteurs de changement.
    • Les hooks de cycle de vie sont appelés dans le cadre de la détection des modifications.
      Si les données du composant que vous souhaitez surveiller sont une propriété d'entrée primitive (String, boolean, number), vous pouvez implémenter ngOnChanges() pour être averti des modifications.
      Si la propriété d'entrée est un type de référence (objet, tableau, etc.), mais que la référence n'a pas changé (par exemple, vous avez ajouté un élément à un tableau existant), vous devez implémenter ngDoCheck() ( voir this SO answer pour plus d'informations).
      Vous ne devez modifier que les propriétés du composant et/ou les propriétés des composants descendants (en raison de la mise en œuvre d’une marche à pied unique - c’est-à-dire d’un flux de données unidirectionnel). Voici n plunker qui viole cela. Les pipes stateful peuvent aussi vous tromper ici.
  • Pour toute modification de liaison trouvée, les composants sont mis à jour, puis le DOM est mis à jour. La détection de changement est maintenant terminée.
  • Le navigateur remarque les modifications du DOM et met à jour l'écran.

Autres références pour en savoir plus:

262
Mark Rajcok

Ce comportement fait maintenant partie du cycle de vie du composant.

Un composant peut implémenter la méthode ngOnChanges dans l'interface OnChanges pour accéder aux modifications d'entrée.

Exemple:

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

Si, en plus de la liaison bidirectionnelle automatique, vous souhaitez appeler une fonction lorsqu'une valeur est modifiée, vous pouvez modifier la syntaxe du raccourci de liaison bidirectionnelle en une version plus détaillée.

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

est un raccourci pour

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

(voir par exemple http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Vous pouvez faire quelque chose comme ça:

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

64
supermikko

Vous pouvez utiliser getter function ou get accessor pour faire office de surveillance sur angular 2.

Voir la démo ici .

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

Voici une autre approche utilisant les fonctions getter et setter pour le modèle.

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

Si vous voulez faire une liaison bidirectionnelle, vous pouvez utiliser [(yourVar)], mais vous devez implémenter l'événement yourVarChange et l'appeler à chaque changement de variable.

Quelque chose comme ça pour suivre le changement de héros

@Output() heroChange = new EventEmitter();

et ensuite, quand votre héros sera changé, appelez this.heroChange.emit(this.hero);

la reliure [(hero)] fera le reste pour vous

voir exemple ici:

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

5
Mohy Eldeen

Essayez ceci lorsque votre application continue toujours à exiger $parse, $eval, $watch comme un comportement similaire à Angular.

https://github.com/vinayk406/angular-expression-parser

3
Vinay K

Cela ne répond pas directement à la question, mais à plusieurs reprises, je me suis posé cette question de Stack Overflow afin de résoudre un problème que j'utiliserais avec $ watch for dans angularJs. J'ai fini par utiliser une approche différente de celle décrite dans les réponses actuelles et je souhaite la partager au cas où quelqu'un le jugerait utile.

La technique que j'utilise pour obtenir quelque chose de similaire $watch consiste à utiliser un BehaviorSubject ( plus d'informations sur le sujet ici ) dans un service Angular, et laisser mon des composants s'y abonnent pour obtenir les changements. Ceci est similaire à un $watch dans angularJs, mais nécessite davantage de configuration et de compréhension.

Dans ma composante:

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

À mon service

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

Voici une démo sur Stackblitz

1
John