web-dev-qa-db-ja.com

Angular 2 @ViewChildは未定義を返します

関連する投稿やドキュメントをいくつか見てきましたが、@ ViewChildから期待どおりの動作が得られないようです。

最終的には、divのスクロール位置を設定しようとしています。この要素はコンポーネントではなく、私のHTMLの通常のdivです。

これを実現するために、@ ViewChildを使用して必要なDOM要素を取得し、そのスクロール値を設定しようとしています。 (余談ですが、@ ViewChild(またはjQuery)を使用せずにこれを達成するためのより良い方法を知っている場合、回答は大歓迎です!)

現時点では、@ ViewChildは未定義のみを返します。いくつかのダミーチェックを実行します。-AfterViewInitで要素にアクセスしています-この要素には* ngIfや* ngForなどの他のディレクティブはありません。

コントローラーは次のとおりです。

import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';

@Component({
    selector: 'portfolio-page',
    templateUrl: './portfolio-page.component.html',
    styleUrls: ['./portfolio-page.component.scss']
})

export class PortfolioPageComponent implements AfterViewInit{
    @ViewChild('gallery-container') galleryContainer: ElementRef;

    ngAfterViewInit(){
        console.log('My element: ' + this.galleryContainer);
    }
}

そしてテンプレート:

<div id='gallery-container' class='gallery-image-container'>
    <div class='gallery-padding'></div>
    <img class='gallery-image' src='{{ coverPhotoVm }}' />
    <img class='gallery-image' src='{{ imagepath }}' *ngFor='let imagepath of imagesVm' />
</div>

私の出力は単純です:私の要素:未定義。

ご覧のとおり、私は現在IDで要素にアクセスしようとしていますが、クラス名も試しています。 ViewChildセレクタークエリが何を期待しているかについて、誰でも詳細を提供できますか?

また、ハッシュ '#'が@ViewChildが使用するセレクターID識別子として使用される例も見ました--これにより、#gallery-containerでテンプレート解析エラーが発生します。

ここで間違っている可能性のあるものは考えられません。すべての助けに感謝します、ありがとう!

完全なコードはこちらから入手できます: https://github.com/aconfee/KimbyArting/tree/master/client/KimbyArting/components/portfolio-page

29
Adam

代わりにテンプレートでrefを使用してみてください。

<div id='gallery-container' #galleryContainer class='gallery-image-container'>
    <div class='gallery-padding'></div>
    <img class='gallery-image' src='{{ coverPhotoVm }}' />
    <img class='gallery-image' src='{{ imagepath }}' *ngFor='let imagepath of imagesVm' />
</div>

引数として参照名を使用します。

@ViewChild('galleryContainer') galleryContainer: ElementRef;

編集

このように宣言されたビューの子は、ビューが初期化された後にのみ利用可能であることを忘れていました。これが最初に発生するのは、ngAfterViewInitAfterViewInitインターフェイスをインポートして実装する)です。

参照名にダッシュを含めることはできません。ダッシュを使用しないと機能しません

50
Aviad P.

コンポーネントにアクセスしたときにコンポーネントがまだ初期化されていない場合、子コンポーネントが未定義であるというエラーが表示されることがあります。

ただし、AfterViewInitで子コンポーネントにアクセスした場合でも、@ ViewChildがnullを返すことがありました。この問題は、* ngIfまたは他のディレクティブによって発生する可能性があります。

解決策は、@ ViewChildの代わりに@ViewChildrenを使用し、コンポーネントの準備ができたときに実行される変更サブスクリプションをサブスクライブすることです。

たとえば、親コンポーネントParentComponentで、子コンポーネントMyComponentにアクセスする場合。

import { Component, ViewChildren, AfterViewInit, QueryList } from '@angular/core';
import { MyComponent } from './mycomponent.component';

export class ParentComponent implements AfterViewInit
{
  //other code emitted for clarity

  @ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>;

  public ngAfterViewInit(): void
  {
    this.childrenComponent.changes.subscribe((comps: QueryList<MyComponent>) =>
    {
      // Now you can access the child component
    });
  }
}
15
Marco Barbero

変更のサブスクライブ

@ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>

setTimeout()およびnotifyOnChanges()および慎重なnullチェックと組み合わせて、動作を確認しました。

他のアプローチでは信頼性の低い結果が生成され、テストが困難です。

1
Martin Stetina

同様の問題がありました。あなたとは異なり、私の@ViewChildは有効なElementRefを返しましたが、そのnativeElementにアクセスしようとするとundefinedでした。

ビューid#contentではなく#content = "ngModel"のように設定して解決しました。

<textarea type = "text"
    id = "content"
    name = "content"
    #content
    [(ngModel)] = "article.content"
    required></textarea>
0
Yamashiro Rion

今日も同じ問題がありました。奇妙なことにsetTimeoutを使用するとうまくいきました。

試してみる:

ngAfterViewInit(){
    setTimeout(_ => console.log('My element: ' + this.galleryContainer), 0);
}
0
Andre Elrico