web-dev-qa-db-fra.com

Angular 6 @Viewchild ne fonctionne pas avec le chargement paresseux

Voici mon code qui donne une erreur ne peut pas lire le titre de la propriété non défini.

Composant parent

import { Child } from './child.component';
@Component({
  selector: 'parent',
})
export class ParentComponet implements OnInit, AfterViewInit {
 constructor(){}

  @ViewChild(Child) child: Child;

  ngAfterViewInit(){
    console.log("check data", this.child.title)
  }
}

Et composant enfant est.

@Component({
      selector: 'child',
    })
    export class ChildComponet {

     public title = "hi"
     constructor(){}

    }

routing.module.ts est comme

{
        path: "",
        component: ParentComponent,
        children: [
            {
                path: '/child',
                component: ChildComponent
            }
        ]
}

Et donne l'erreur est

ERROR TypeError: Cannot read property 'title' of undefined(…)
8
Ricky

Je pense qu'il vous manque 'template' ou 'templateUrl' pour la création d'un composant

ParentComponent

import { ChildComponent } from './child.component';    // {ChildComponent} not {Child} as we are referencing it to the exported class of ChildComponent

@Component({
   selector: 'parent',
   template: `<child></child>`
})
export class ParentComponet implements OnInit, AfterViewInit {...}

ChildComponent

@Component({
  selector: 'child',
  template: `<h1>{{ title }}</h1>`
})
export class ChildComponent {...}       // Be sure to spell it right as yours were ChildComponet - missing 'n'

UPDATE selon la clarification de l'utilisateur sur ce fil

Avait ajouté un Stackblitz Demo pour votre référence (Vérifiez la console)}

Si vous souhaitez accéder au ChildComponent rendu sous le <router-outlet> du composant parent, vous pouvez le faire en utilisant la propriété prise en charge (activate) de router-outlet:

Une prise de routeur émettra un événement d'activation chaque fois qu'un nouveau composant est instancié 

Documents angulaires

Modèle de ParentComponent

@Component({
   selector: 'parent',
   template: `<router-outlet (activate)="onActivate($event)"></router-outlet>`
})
export class ParentComponent {

    onActivate(event): void {
        console.log(event);         // Sample Output when you visit ChildComponent url
                                    // ChildComponent {title: "hi"}

        console.log(event.title);   // 'hi' 
    }

}

Le résultat sera différent en fonction de la page visitée sous les enfants de vos parents

Si vous visitez Child1Component, vous obtiendrez son instance Child1Component {title: "hi"}

Si vous visitez Child2Component, vous obtiendrez son instance Child2Component {name: "Angular"}

Ces résultats seront ensuite répercutés sur la console onActivate (événement) de votre ParentComponent pour que vous puissiez accéder à

11
KShewengger

Ce n'est pas comme ça que ça doit fonctionner. Vous ne pourrez obtenir que ChildComponent dans votre ParentComponentUNIQUEMENT si vous avez la balise <app-child></app-child> dans votre modèle ParentComponent.

Quelque chose comme ça:

...

<app-child></app-child>

...

Mais puisque vous utilisez le routage enfant et que la ChildComponent se chargera sur le router-outlet de votre ParentComponent, vous n'y aurez pas accès à l'aide de ViewChild.

PS: Vous n'y aurez accès qu'à l'intérieur de ngAfterViewInit car ViewChild ne peut être considéré comme sûr d'avoir instancié qu'après le chargement de la vue:

import { Component, OnInit, ViewChild } from '@angular/core';
import { ChildComponent } from '../child/child.component';
...

@Component({...})
export class ParentComponent implements OnInit {

  @ViewChild(ChildComponent) childComponent: ChildComponent;

  ...

  ngAfterViewInit() {
    console.log(this.childComponent);
  }

}

Voici un exemple de travail StackBlitz pour votre référence qui illustre votre scénario dans les deux cas.

PS: Pour obtenir les propriétés ChildComponent dans votre ParentComponent, avec Routing, vous devrez soit utiliser un SharedService, soit passer la propriété ChildProperty de la route en tant que QueryParam et la lire dans votre ParentComponent à l'aide de la commande ActivatedRoute

METTRE À JOUR:

Partage de données à l'aide des paramètres de requête d'itinéraire:

Bien que cela n’aura pas beaucoup de sens, mais dans votre ChildComponent, vous pouvez avoir un lien qui acheminera l’utilisateur vers le ChildComponent avec la propriété title passée en tant que queryParam. Quelque chose comme ça:

<a 
  [routerLink]="['/child']" 
  [queryParams]="{title: title}">
  Go To Child Route With Query Params
</a>

Et dans votre ParentComponent, y avoir accès en utilisant ActivatedRoute comme ceci:

...
import { ActivatedRoute } from '@angular/router';
...

@Component({...})
export class ParentComponent implements OnInit {

  ...

  constructor(
    private route: ActivatedRoute,
    ...
  ) { }

  ngOnInit() {
    this.route.queryParams.subscribe(queryParams => {
      console.log('queryParams[`title`]', queryParams['title']);
    });
    ...
  }

  ...

}

Utiliser une SharedService

Créez simplement une SharedService avec une privateBehaviorSubject qui serait exposée en tant que Observable en appelant la méthode asObservable. Sa valeur peut être définie en exposant une méthode (setChildProperty) qui appellera essentiellement la méthode next avec la valeur childProperty mise à jour:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable()
export class SharedService {

  private childProperty: BehaviorSubject<string> = new BehaviorSubject<string>(null);
  childProperty$: Observable<string> = this.childProperty.asObservable();

  constructor() { }

  setChildProperty(childProperty) {
    this.childProperty.next(childProperty);
  }

}

Vous pouvez ensuite l'injecter à la fois dans votre ParentComponent et dans votre ChildComponent:

Dans ChildComponent, définissez la valeur:

import { Component, OnInit } from '@angular/core';
import { SharedService } from '../shared.service';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

  public title = "hi"

  constructor(private sharedService: SharedService) { }

  ngOnInit() {
    this.sharedService.setChildProperty(this.title);
  }

}

Et dans votre ParentComponent, obtenez la valeur:

...
import { SharedService } from '../shared.service';

@Component({...})
export class ParentComponent implements OnInit {

  ...

  constructor(
    ...,
    private sharedService: SharedService
  ) { }

  ngOnInit() {
    ...
    this.sharedService.childProperty$.subscribe(
      childProperty => console.log('Got the Child Property from the Shared Service as: ', childProperty)
    );
  }

  ...

}
3
SiddAjmera

Assurez-vous que vous avez ajouté la balise parent.component.html dans votre modèle <child></child>.

2
Morema