コンポーネントテンプレートの要素を選択するにはどうすればよいですか?


516

コンポーネントテンプレートで定義された要素を取得する方法を知っている人はいますか?Polymerは、$とを使用して非常に簡単になり$$ます。

Angularでそれをどうやってやろうかと思っていたところです。

チュートリアルの例を見てみましょう:

import {Component} from '@angular/core';

@Component({
    selector:'display',
    template:`
     <input #myname (input)="updateName(myname.value)"/>
     <p>My name : {{myName}}</p>
     `   
})
export class DisplayComponent {
    myName: string = "Aman";
    updateName(input: String) {
        this.myName = input;
    }
}

クラス定義内からpまたはinput要素の参照をキャッチまたは取得するにはどうすればよいですか?

回答:


938

そこからインジェクションElementRefして使用しquerySelectorたり、類似のものを使用する代わりに、宣言的な方法を使用して、ビュー内の要素に直接アクセスできます。

<input #myname>
@ViewChild('myname') input; 

素子

ngAfterViewInit() {
  console.log(this.input.nativeElement.value);
}

StackBlitzの例

  • @ViewChild()は、パラメータとしてディレクティブまたはコンポーネントタイプ、またはテンプレート変数の名前(文字列)をサポートします。
  • @ViewChildren()は、コンマ区切りのリストとして名前のリストもサポートしています(現在はスペースは使用できません@ViewChildren('var1,var2,var3'))。
  • @ContentChild()@ContentChildren()は同じことを行いますが、軽量DOM(<ng-content>投影された要素)で行います。

子孫

@ContentChildren() 子孫を照会することもできる唯一のもの

@ContentChildren(SomeTypeOrVarName, {descendants: true}) someField; 

{descendants: true}デフォルトにする必要がありますが、2.0.0 finalではなく、バグと見なされます。
これは2.0.1で修正されました

読んだ

コンポーネントとディレクティブがある場合、readパラメーターを使用して、返すインスタンスを指定できます。

たとえばViewContainerRef、デフォルトではなく動的に作成されたコンポーネントに必要ですElementRef

@ViewChild('myname', { read: ViewContainerRef }) target;

変更をサブスクライブする

ビューの子はngAfterViewInit()が呼び出されたときにのみ設定され、コンテンツの子はngAfterContentInit()が呼び出されたときにのみ設定されますが、クエリ結果の変更をサブスクライブする場合は、ngOnInit()

https://github.com/angular/angular/issues/9689#issuecomment-229247134

@ViewChildren(SomeType) viewChildren;
@ContentChildren(SomeType) contentChildren;

ngOnInit() {
  this.viewChildren.changes.subscribe(changes => console.log(changes));
  this.contentChildren.changes.subscribe(changes => console.log(changes));
}

直接DOMアクセス

DOM要素のみをクエリできますが、コンポーネントやディレクティブインスタンスはクエリできません。

export class MyComponent {
  constructor(private elRef:ElementRef) {}
  ngAfterViewInit() {
    var div = this.elRef.nativeElement.querySelector('div');
    console.log(div);
  }

  // for transcluded content
  ngAfterContentInit() {
    var div = this.elRef.nativeElement.querySelector('div');
    console.log(div);
  }
}

任意の投影コンテンツを取得する

トランスクリプションされたコンテンツへのアクセスを参照してください


12
角度のあるチームはElementRefの使用を推奨していません。これがより良いソリューションです。
栄誉あるチョウ

7
実際にinputもありElementRefますが、あなたの代わりにホストからそれを問い合わせるので、あなたが実際にしたい要素への参照を取得しますElementRef
ギュンターZöchbauer

32
実際に使用しても問題ElementRefありません。ElementRef.nativeElementと一緒に使ってRendererも結構です。どのような推奨されないことの性質アクセスしているElementRef.nativeElement.xxx直接の。
ギュンターZöchbauer

2
@Natanaelこれが明示的に文書化されているかどうか、またはどこにあるかはわかりませんが、問題や他のディスカッション(Angularチームメンバーによる)で定期的に言及されており、直接DOMアクセスを避ける必要があります。何のプロパティにアクセスし、メソッドのである(直接DOMへのアクセスElementRef.nativeElement)が、私はないと思う) -であり、Angularsサーバ側のレンダリングとWebWorker機能を使用できないように、あなたを(それはまた、今後のオフラインテンプレートコンパイラを破った場合、私は知らない。
ギュンターツェヒバウアー

10
で述べたように、読み取りのセクションでは、ViewChildを持つ要素のためnativeElementを取得したい場合は、次の操作を行う必要があります: @ViewChild('myObj', { read: ElementRef }) myObj: ElementRef;
jsgoupil

203

ElementRefコンポーネントのコンストラクタに注入することで、DOM要素へのハンドルを取得できます。

constructor(myElement: ElementRef) { ... }

ドキュメント:https : //angular.io/docs/ts/latest/api/core/index/ElementRef-class.html


1
@Brocco回答を更新できますか?ElementRefがなくなったので、現在の解決策を見たいのですが。
ジェフピア2015年

23
ElementRef利用可能です(再び?)。
ギュンターZöchbauer

10
link このAPIは、DOMへの直接アクセスが必要な最後の手段として使用します。代わりに、Angularが提供するテンプレートおよびデータバインディングを使用してください。あるいは、ネイティブ要素への直接アクセスがサポートされていない場合でも安全に使用できるAPIを提供するレンダラーを見てください。直接DOMアクセスに依存すると、アプリケーションとレンダリングレイヤーの間に密結合が生じ、2つを分離してアプリケーションをWebワーカーにデプロイすることができなくなります。
Sandeep talabathula 2017

@sandeeptalabathulaサードパーティのライブラリから浮動日付ピッカーコンポーネントをアタッチする要素を見つけるためのより良いオプションは何ですか?これは元々の問題ではなかったことは承知していますが、DOM内の要素を見つけるのはすべてのシナリオで悪いことだと
John

13
@john Ah ..大丈夫。これを試してみて、this.element.nativeElement.querySelector('#someElementId')ElementRefを次のようにコンストラクタに渡すことができます。libを private element: ElementRef, インポート...import { ElementRef } from '@angular/core';
sandeep talabathula

52
import { Component, ElementRef, OnInit } from '@angular/core';

@Component({
  selector:'display',
  template:`
   <input (input)="updateName($event.target.value)">
   <p> My name : {{ myName }}</p>
  `
})
class DisplayComponent implements OnInit {
  constructor(public element: ElementRef) {
    this.element.nativeElement // <- your direct element reference 
  }
  ngOnInit() {
    var el = this.element.nativeElement;
    console.log(el);
  }
  updateName(value) {
    // ...
  }
}

最新バージョンで動作するように更新された例

ネイティブ要素の詳細については、こちら


20

Angular 4+renderer.selectRootElementCSSセレクターと併用して要素にアクセスします。

最初にメール入力を表示するフォームがあります。メールアドレスを入力すると、フォームが拡張され、プロジェクトに関する情報を引き続き追加できるようになります。ただし、既存のクライアントでない場合、フォームにはプロジェクト情報セクションの上に住所セクションが含まれます。

現在、データ入力部分はコンポーネントに分割されていないため、セクションは* ngIfディレクティブで管理されます。既存のクライアントの場合はプロジェクトのメモフィールドにフォーカスし、新しい場合は名フィールドにフォーカスを設定する必要があります。

解決策を試しましたが成功しませんでした。しかし、この回答のUpdate 3 は、最終的な解決策の半分を私に与えてくれました。残りの半分は、このスレッドでのMatteoNYの応答によるものです。結果はこれです:

import { NgZone, Renderer } from '@angular/core';

constructor(private ngZone: NgZone, private renderer: Renderer) {}

setFocus(selector: string): void {
    this.ngZone.runOutsideAngular(() => {
        setTimeout(() => {
            this.renderer.selectRootElement(selector).focus();
        }, 0);
    });
}

submitEmail(email: string): void {
    // Verify existence of customer
    ...
    if (this.newCustomer) {
        this.setFocus('#firstname');
    } else {
        this.setFocus('#description');
    }
}

私がしているのは要素にフォーカスを設定することだけなので、変更の検出について心配する必要がないので、実際renderer.selectRootElementにAngularの外部で呼び出しを実行できます。新しいセクションにレンダリングする時間を与える必要があるため、要素セクションはタイムアウトにラップされ、要素の選択が試行される前にレンダリングスレッドが追いつくことができます。すべての設定が完了したら、基本的なCSSセレクターを使用して要素を呼び出すだけです。

この例は主にフォーカスイベントを扱っていることは知っていますが、これを他のコンテキストで使用できないことは私には難しいことです。


クラスRendererは、Angular 4.3.0以降非推奨です。angular.io/api/core/Renderer
ジェイミー

15

*ngIfまたはの内部でコンポーネントインスタンスを取得しようとしている人*ngSwitchCaseは、このトリックに従うことができます。

initディレクティブを作成します。

import {
    Directive,
    EventEmitter,
    Output,
    OnInit,
    ElementRef
} from '@angular/core';

@Directive({
    selector: '[init]'
})
export class InitDirective implements OnInit {
    constructor(private ref: ElementRef) {}

    @Output() init: EventEmitter<ElementRef> = new EventEmitter<ElementRef>();

    ngOnInit() {
        this.init.emit(this.ref);
    }
}

次のような名前でコンポーネントをエクスポートします myComponent

@Component({
    selector: 'wm-my-component',
    templateUrl: 'my-component.component.html',
    styleUrls: ['my-component.component.css'],
    exportAs: 'myComponent'
})
export class MyComponent { ... }

このテンプレートを使用してElementRefAND MyComponentインスタンスを取得します

<div [ngSwitch]="type">
    <wm-my-component
           #myComponent="myComponent"
           *ngSwitchCase="Type.MyType"
           (init)="init($event, myComponent)">
    </wm-my-component>
</div>

TypeScriptでこのコードを使用する

init(myComponentRef: ElementRef, myComponent: MyComponent) {
}

12

ViewChildからデコレータをインポートする@angular/coreように、ます。

HTMLコード:

<form #f="ngForm"> 
  ... 
  ... 
</form>

TSコード:

import { ViewChild } from '@angular/core';

class TemplateFormComponent {

  @ViewChild('f') myForm: any;
    .
    .
    .
}

これで、「myForm」オブジェクトを使用して、クラス内の任意の要素にアクセスできます。

Source


ただし、コンポーネントクラスのテンプレート要素にアクセスする必要はほとんどないことに注意してください。角度ロジックを正しく理解する必要があるだけです。
Hany

3
使用しないでください。タイプはElementRefです
Johannes

10
 */
import {Component,ViewChild} from '@angular/core' /*Import View Child*/

@Component({
    selector:'display'
    template:`

     <input #myname (input) = "updateName(myname.value)"/>
     <p> My name : {{myName}}</p>

    `
})
export class DisplayComponent{
  @ViewChild('myname')inputTxt:ElementRef; /*create a view child*/

   myName: string;

    updateName: Function;
    constructor(){

        this.myName = "Aman";
        this.updateName = function(input: String){

            this.inputTxt.nativeElement.value=this.myName; 

            /*assign to it the value*/
        };
    }
}

10
このコードにいくつかの説明を提供してください。説明なしの単純なコードダンプはお勧めしません。
rayryeng 2017年

5
これは機能しません。@ ViewChildアノテーションを介して設定された属性は、ngAfterViewInitライフサイクルイベントの後でのみ使用できます。その場合、コンストラクターで値にアクセスすると、未定義の値が生成されますinputTxt
デビッド

ang 7を使用すると、これは完璧に機能しました。
MizAkita

5

:これは角度6に適用され、上記のようにしないElementRefとなったElementRef<T>Tのタイプを示しますnativeElement

ElementRefすべての回答で推奨されているようにを使用している場合ElementRef、次のようなひどい型宣言がある問題がすぐに発生することを付け加えておきます

export declare class ElementRef {
  nativeElement: any;
}

これは、nativeElementがであるブラウザ環境では愚かHTMLElementです。

これを回避するには、次のテクニックを使用できます

import {Inject, ElementRef as ErrorProneElementRef} from '@angular/core';

interface ElementRef {
  nativeElement: HTMLElement;
}

@Component({...}) export class MyComponent {
  constructor(@Inject(ErrorProneElementRef) readonly elementRef: ElementRef) { }
}

1
これは私が抱えていた問題を説明しています。item別のElementRefに設定している場合でも、ElementRefである必要があると表示される ため、これは機能しませんlet item:ElementRef, item2:ElementRef; item = item2; // no can do.。非常に紛らわしいです。しかし、これは問題あり let item:ElementRef, item2:ElementRef; item = item2.nativeElementません。あなたが指摘した実装のために。
oooyaya 2017年

1
実際の最初の例let item: ElementRef, item2: ElementRef; item = item2は、明確な割り当て分析のために失敗します。2番目は同じ理由で失敗しますが、item2説明した理由で初期化されている場合は両方とも成功します(またはdeclare letここで使用できる割り当て可能性の便利なクイックチェックとして)。とにかく、anyこのようなパブリックAPIで見るのは本当に残念です。
Aluan Haddad

2

次の兄弟をすぐに取得するには、これを使用します

event.source._elementRef.nativeElement.nextElementSibling

1

リストからターゲット要素を選択します。同じ要素のリストから特定の要素を選択するのは簡単です。

コンポーネントコード:

export class AppComponent {
  title = 'app';

  listEvents = [
    {'name':'item1', 'class': ''}, {'name':'item2', 'class': ''},
    {'name':'item3', 'class': ''}, {'name':'item4', 'class': ''}
  ];

  selectElement(item: string, value: number) {
    console.log("item="+item+" value="+value);
    if(this.listEvents[value].class == "") {
      this.listEvents[value].class='selected';
    } else {
      this.listEvents[value].class= '';
    }
  }
}

HTMLコード:

<ul *ngFor="let event of listEvents; let i = index">
   <li  (click)="selectElement(event.name, i)" [class]="event.class">
  {{ event.name }}
</li>

CSSコード:

.selected {
  color: red;
  background:blue;
}

0

簡単な使用法の最小の例:

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

@Component({
  selector: 'my-app',
  template:
  `
  <input #inputEl value="hithere">
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('inputEl') inputEl:ElementRef; 

  ngAfterViewInit() {
    console.log(this.inputEl);
  }
}
  1. 目的のDOM要素にテンプレート参照変数を配置します。この例では、これはある#inputEl<input>タグ。
  2. コンポーネントクラスで、@ ViewChildデコレータを介してDOM要素を挿入します。
  3. ngAfterViewInitライフサイクルフックの要素にアクセスします。

注意:

DOM要素を操作する場合は、要素に直接アクセスする代わりに、Renderer2 APIを使用します。DOMへの直接アクセスを許可すると、アプリケーションがXSS攻撃に対してより脆弱になる可能性があります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.