Crochet du cycle de vie angulaire 2 Après que tous les enfants sont initialisés?

je suis à la recherche d'un concept pour mettre en œuvre le cas suivant:

j'ai une composante de recherche de parent qui a certains composants Comme Voir les enfants / Contenu Les enfants pour afficher les facettes et les résultats de recherche. Je veux maintenant déclencher une recherche lorsque l'application a terminé le chargement de sorte que l'utilisateur ne voit pas une page vide.

mon problème maintenant est que je ne peux pas trouver un crochet de cycle de vie qui correspond à mes besoins. Les facettes / résultats de recherche s'abonner aux résultats de recherche dans leur ngoninit respectif. Donc j'ai besoin d'un crochet qui s'appelle après tous les composants enfants avaient terminé l'initialisation.

j'ai essayé les crochets sur le composant parent

  • ngAfterContentInit: ce qui est appelé avant ngOnInit est appelée sur les enfants de moins de!--14-->
  • ngAfterViewInit: celui-ci fonctionne mais après le retour des résultats de la recherche la vue des enfants est mise à jour ce qui conduit à une erreur puisque les actions qui manipulent la vue ne sont pas permises dans ngAfterViewInit

une idée de comment aborder ce problème? Il me semble que je ne saisis pas quelque chose de fondamental ici.

Cheers

Tom

24
demandé sur Jviaches 2016-08-04 12:16:16
la source

6 ответов

j'ai fini par utiliser le crochet ngAfterViewInit () de la façon suivante:

ngAfterViewInit(){
  //stuff that doesn't do view changes
  setTimeout(_=> this.methodThatKicksOffAnotherRoundOfChanges());
}

ceci devrait être sûr à utiliser comparé à d'autres invocations de setTimeout où un temps réel est défini, puisqu'il devrait commencer instantanément (et affecter juste le comportement de threading / contexte)

32
répondu Tom 2016-08-09 12:21:33
la source

faites que les composants enfant émettent un événement sur leur Init et écoutez-le là où vous en avez besoin

2
répondu Vale Steve 2016-08-04 13:56:51
la source

si vous avez aussi des enfants content (transcluses en utilisant <ng-content>)ngAfterContentInit() c'est le droit du cycle de vie de rappel.

https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#aftercontent

ngAfterContentInit() {
  doSomething();
}
2
répondu Günter Zöchbauer 2016-08-09 12:27:30
la source

si vous avez besoin de votre composant Parent pour attendre un comportement asynchrone personnalisé pour plusieurs composantes de l'enfant alors voici une bonne solution que j'ai concocté.

tout composant enfant qui initialise de façon asynchrone étendre ceci:

import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';

export class AsynchronouslyInitialisedComponent {
  loadedState: Subject<boolean> = new Subject<boolean>();
  loadedState$ = this.loadedState.asObservable();
  constructor() {
  }
  protected componentLoaded() {
    this.loadedState.next(true);
  }
}

Exemple de composant Enfant:

import {Component} from '@angular/core';
import {AsynchronouslyInitialisedComponent} from './../base_component';

@Component({
  moduleId: module.id,
  selector: 'child'
})
export class Child extends AsynchronouslyInitialisedComponent {
  constructor() {
    super();
  }
  ngOnInit() {
    //Some async behavoir:
    setTimeout(() => {
      this.componentLoaded();
    }, 10000);
  }
}

maintenant, tout ce que votre composant Parent doit faire est le suivant:

import {Component, ViewEncapsulation, ViewChild};
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/zip';

@Component({
  moduleId: module.id,
  selector: 'parent'
})
export class Parent { 

  @ViewChild('child') child; //Just use <child #child></child> in your template

  constructor() {
  }
  ngOnInit() {
    Observable
    .zip(this.child.loadedState$, this.otherChild.loadedState$) //Add as many as you want here... 
    .subscribe(pair => {
      console.log('All child components loaded');
    });
  }
}
1
répondu Baconbeastnz 2018-01-12 20:56:05
la source

L'OP semble penser qu'il ne répond pas à ses besoins, ou n'est pas Élégant, mais il semble que la réponse de Vale Steve est l'approche la plus logique ici.

dans mon cas, j'ai dû attendre 2 composants enfant pour initialiser, qui contrôlent les panneaux gauche et droite de mon application. J'ai deux enfants composants de déclarer leur initialisation d'un service partagé:

  constructor(
      public _navigate: RouteNavigationService // service shared between all child components and parent
  ){}

  ngOnInit() {
    this._navigate.setChildInit(this.panel);
  }

service partagé (RouteNavigationService):

  private _leftChildInit  = new Subject();
  private _rightChildInit = new Subject();

  leftChildInit$          = this._leftChildInit.asObservable();
  rightChildInit$         = this._rightChildInit.asObservable();

  setChildInit(panel){
    switch(panel){
      case 'leftPanel':
        console.log('left panel initialized!');
        this._leftChildInit.next(true);
        break;
      case 'rightPanel':
        console.log('right panel initialized!');
        this._rightChildInit.next(true);
        break;
    }
  }

puis dans mon composant parent j'utilise le zip méthode pour combiner plusieurs Observables ensemble (vous pouvez ajouter des composants enfants supplémentaires ici aussi) et attendre qu'ils finissent tous:

  childInitSub: Subscription;

  constructor(
      public _navigate: RouteNavigationService
  ) {
    this.childInitSub = Observable.zip( // WAIT FOR BOTH LEFT & RIGHT PANELS' ngOnInit() TO FIRE
        this._navigate.leftChildInit$,
        this._navigate.rightChildInit$).subscribe(data =>
        // Both child components have initialized... let's go!
    );
  }

L'OP unis dans un commentaire

"je n'aime pas cela, car à chaque fois que j'ajoute un nouveau composant, j'ai besoin de attendre un événement onInit supplémentaire et avoir à l'implémenter"

mais de façon réaliste tout ce que vous avez à faire est de faire un autre Subject à votre service pour la nouvelle composant enfant ngOnInit, et ajouter une ligne supplémentaire dans la méthode zip du composant parent.

notez que la réponse acceptée ici, en utilisant ngAfterViewInit () n'est pas tout à fait la même chose que ci-dessus. Dans mon cas, à l'aide de ngAfterViewInit() produisait un ExpressionChangedAfterItHasBeenCheckederror ce qui dépasse la portée de cette question, mais sert à démontrer que cette approche ne fait pas réellement la même chose.

Dans contrast, la méthode ci-dessus est littéralement seulement déclenché comme résultat direct de tous les composants de votre enfant' ngOnInit() les événements ayant déclenché.

1
répondu Inigo 2018-01-26 14:44:36
la source

en mode dev (par défaut) le mécanisme de détection de changement effectue une vérification supplémentaire de l'arbre des composants pour voir que vous ne manipulez pas L'UI dans les crochets. C'est pourquoi vous recevez des erreurs.

si vous êtes sûr que vos modifications de L'interface utilisateur dans ngAfterViewInit sont valides, invoquez enablprodmode() juste avant bootstrap(). Cela indique à L'angle, "ne faites pas le second passage pendant la détection de changement".

-1
répondu Yakov Fain 2016-08-04 14:10:05
la source

Autres questions sur