web-dev-qa-db-ja.com

Angular 6 @Viewchildは遅延読み込みでは機能しません

エラーを発生させる私のコードは、未定義のプロパティタイトルを読み取れません。

親コンポーネント

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

そして子コンポーネントです。

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

     public title = "hi"
     constructor(){}

    }

routing.module.tsは

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

そして、エラーは

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

コンポーネントの作成に関連して'template'または 'templateUrl'が欠落していると思います

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 [〜#〜]このスレッドに関するユーザーの説明に従って

参照用に Stackblitz Demo を追加しました(コンソールを確認してください)

親コンポーネントの下にレンダリングされたChildComponentにアクセスする場合は、<router-outlet>これを行うには、ルーターアウトレットの(アクティブ化)サポートされるプロパティを使用します。

ルーターアウトレットは、新しいコンポーネントがインスタンス化されるたびに、アクティブ化イベントを発行します

Angular Docs

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' 
    }

}

結果は、アクセスしたページによって異なります親の子の下

Child1Componentにアクセスすると、そのインスタンスを取得しますChild1Component {title: "hi"}

Child2Componentにアクセスすると、そのインスタンスを取得しますChild2Component {name: "Angular"}

これらの結果は、ParentComponentのonActivate(event)コンソールに反映され、アクセスできます

11
KShewengger

それはそれが機能するはずの方法ではありません。 ChildComponentでのみParentComponentを取得できます[〜#〜]のみ[〜#〜]ParentComponentテンプレートに<app-child></app-child>タグがある場合。

このようなもの:

...

<app-child></app-child>

...

ただし、子ルーティングを使用しているため、ChildComponentParentComponentrouter-outletにロードされるため、ViewChildを使用してアクセスすることはできません。

PS:ngAfterViewInitはインスタンス化しても安全であると見なすことができるため、ViewChild内でのみアクセスできます。ビューが読み込まれた後:

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

}

これは、両方のケースでのシナリオを示す、refの Working Sample StackBlitz です。

PS:ルーティングでChildComponentParentComponentプロパティを取得するには、ルーティングを使用するか、 SharedServiceまたは、ルートのChildPropertyをQueryParamとして渡し、ActivatedRouteを使用してParentComponentでそれを読み取る必要があります。

更新:

ルートクエリパラメータを使用したデータの共有:

これはあまり意味がありませんが、ChildComponentには、titleプロパティをqueryParamとして渡して、ユーザーをChildComponentにルーティングするリンクを含めることができます。 。このようなもの:

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

そして、あなたのParentComponentでは、次のようにActivatedRouteを使用してそれにアクセスできます。

...
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']);
    });
    ...
  }

  ...

}

SharedServiceを使用する

SharedServiceを作成し、privateBehaviorSubjectを使用してObservableメソッドを呼び出すことで、asObservableとして公開されます。その値は、更新されたsetChildProperty値でnextメソッドを本質的に呼び出すmethod(childProperty)を公開することによって設定できます。

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

}

次に、それをParentComponentChildComponentの両方に注入できます。

ChildComponentに値を設定します。

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

}

そして、ParentComponentで値を取得します。

...
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

parent.component.html追加したテンプレート<child></child> 鬼ごっこ。

2
Morema