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?
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:
setTimeout()
dans nos composants plutôt que quelque chose comme $timeout
..., car setTimeout()
est patché.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 {{}}
.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. ngOnChanges()
pour être averti des modifications.ngDoCheck()
( voir this SO answer pour plus d'informations).Autres références pour en savoir plus:
onPush
.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);
}
}
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>
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);
}
}
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;
}
}
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:
Essayez ceci lorsque votre application continue toujours à exiger $parse
, $eval
, $watch
comme un comportement similaire à Angular.
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