Comment détecter quand une valeur @Input () change D'angle?

j'ai un composant parent ( Category Component ), un composant enfant ( videolist component ) et un ApiService.

j'ai la plus grande partie de cette fonctionnalité, c'est-à-dire que chaque composant peut accéder à l'api json et obtenir ses données pertinentes via des observables.

actuellement le composant de liste de vidéos obtient juste toutes les vidéos, je voudrais filtrer cela pour juste des vidéos dans une catégorie particulière, j'ai réalisé cela en passant le categoryId à l'enfant par l'intermédiaire de @Input() .

Category Component.html

<video-list *ngIf="category" [categoryId]="category.id"></video-list>

cela fonctionne et quand la catégorie parent CategoryComponent category change alors la valeur categoryId est transmise via @Input() mais je dois ensuite le détecter dans VideoListComponent et requérir à nouveau le tableau videos via APIService (avec le nouveau categoryId).

à AngularJS j'aurais fait un $watch sur la variable. Qu'est-ce que l' la meilleure façon de gérer cela?

221
demandé sur Lazar Ljubenović 2016-07-25 18:28:01

7 réponses

en fait, il y a deux façons de détecter et d'agir lorsqu'une entrée change dans le composant enfant en angular2+ :

  1. vous pouvez utiliser la ngOnChanges() méthode du cycle de vie comme également mentionné dans les réponses plus anciennes:

    @Input() categoryId: string;
    
    ngOnChanges(changes: SimpleChanges) {
    
        this.doSomething(changes.categoryId.currentValue);
        // You can also use categoryId.previousValue and 
        // categoryId.firstChange for comparing old and new values
    
    }
    

    liens de Documentation: ngOnChanges, 1519150920 "SimpleChanges, SimpleChange

    Exemple de démonstration: regardez ce plunker

  2. alternativement, vous pouvez également utiliser un setter de propriété d'entrée comme suit:

    private _categoryId: string;
    
    @Input() set categoryId(value: string) {
    
       this._categoryId = value;
       this.doSomething(this._categoryId);
    
    }
    
    get categoryId(): string {
    
        return this._categoryId;
    
    }
    

    lien de Documentation: regarder ici .

    Démo Exemple: Regardez ce plunker .

QUELLE APPROCHE DEVEZ-VOUS UTILISER?

si votre component a plusieurs entrées, alors, si vous utilisez ngOnChanges(), vous obtiendrez toutes les modifications pour toutes les entrées à la fois dans ngOnChanges(). En utilisant cette approche, vous pouvez également comparer les valeurs actuelles et antérieures de l'entrée qui a changé et prendre des mesures en conséquence.

cependant, si vous voulez faire quelque chose quand seulement une entrée particulière change (et vous ne vous souciez pas de la autres inputs), alors il pourrait être plus simple d'utiliser un setter de propriété input. Cependant, cette approche ne fournit pas une méthode intégrée pour comparer les valeurs précédentes et actuelles de l'entrée modifiée (ce que vous pouvez faire facilement avec la méthode ngOnChanges lifecycle).

EDIT 2017-07-25: ANGULAR CHANGE DETECTION MAY STILL NOT FIRE IN SOME CIRCUMSTANCES

normalement, la détection de changement de setter et ngOnChanges se déclenche chaque fois que le composant parent modifie les données qu'il transmet à l'enfant, pourvu que les données soient un type de données primitif JS(chaîne, Nombre, booléen) . Cependant, dans les scénarios suivants, il ne se déclenche pas et vous devez prendre des mesures supplémentaires afin de le faire fonctionner.

  1. si vous utilisez un objet ou un tableau imbriqué (au lieu d'un type de données primitives JS) pour passer des données de Parent à enfant, changez la détection (en utilisant setter ou ngchanges) pourrait ne pas tirer, comme également mentionné dans la réponse de l'utilisateur: muetzerich. Pour les solutions, regardez ici .

  2. si vous mutez des données en dehors du contexte angulaire (c.-à-d. externe), alors angular ne connaîtra pas les changements. Vous devrez peut-être utiliser ChangeDetectorRef ou NgZone dans votre component pour rendre angular conscient des changements externes et ainsi déclencher la détection de changement. Se référer à ce .

297
répondu Alan C. S. 2017-10-05 20:42:56

utilisez la méthode du cycle de vie ngOnChanges() dans votre composant.

ngOnChanges est appelé juste après que les propriétés liées aux données ont été les enfants sont contrôlés si au moins l'un d'eux a changé.

voici le Docs .

87
répondu muetzerich 2016-07-25 15:31:56

j'ai eu des erreurs dans la console ainsi que dans le compilateur et L'IDE en utilisant le type SimpleChanges dans la signature de la fonction. Pour éviter les erreurs, utilisez plutôt le mot-clé any dans la signature.

ngOnChanges(changes: any) {
    console.log(changes.myInput.currentValue);
}

EDIT:

comme Jon l'a souligné ci-dessous, vous pouvez utiliser la signature SimpleChanges lorsque vous utilisez la notation entre crochets plutôt que la notation par points.

ngOnChanges(changes: SimpleChanges) {
    console.log(changes['myInput'].currentValue);
}
24
répondu Darcy 2016-10-12 19:32:43

le pari le plus sûr est d'aller avec un service partagé à la place d'un paramètre @Input . De plus, le paramètre @Input ne détecte pas les changements dans le type d'objet imbriqué complexe.

un exemple simple de service est le suivant:

Service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SyncService {

    private thread_id = new Subject<number>();
    thread_id$ = this.thread_id.asObservable();

    set_thread_id(thread_id: number) {
        this.thread_id.next(thread_id);
    }

}

composant.ts

export class ConsumerComponent implements OnInit {

  constructor(
    public sync: SyncService
  ) {
     this.sync.thread_id$.subscribe(thread_id => {
          **Process Value Updates Here**
      }
    }

  selectChat(thread_id: number) {  <--- How to update values
    this.sync.set_thread_id(thread_id);
  }
}

vous pouvez utiliser une implémentation similaire dans d'autres composants et tous vos composants partageront les mêmes valeurs partagées.

5
répondu Sanket Berde 2017-11-30 11:24:50

je veux juste ajouter qu'il y a un autre crochet du cycle de vie appelé DoCheck qui est utile si la valeur @Input n'est pas une valeur primitive.

j'ai un tableau comme un Input donc cela ne déclenche pas l'événement OnChanges quand le contenu change (parce que la vérification que L'angle fait est 'simple' et pas profonde donc le tableau est toujours un tableau, même si le contenu sur le tableau a changé).

j'implémente alors quelques vérifier le code pour décider si je veux mettre à jour ma vue avec le tableau modifié.

1
répondu Stevo 2017-08-14 11:09:06

vous pouvez aussi avoir un observable qui déclenche des changements dans le composant parent(CategoryComponent) et faire ce que vous voulez faire dans l'abonnement dans le composant enfant. (videoListComponent)

service.ts 
public categoryChange$ : ReplaySubject<any> = new ReplaySubject(1);

-----------------
CategoryComponent.ts
public onCategoryChange(): void {
  service.categoryChange$.next();
}

-----------------
videoListComponent.ts
public ngOnInit(): void {
  service.categoryChange$.subscribe(() => {
   // do your logic
});
}
0
répondu Josf 2017-09-19 22:39:31