Bileşen şablonundaki bir öğeyi nasıl seçebilirim?


516

Bileşen şablonunda tanımlanan bir öğeyi nasıl ele geçireceğini bilen var mı? Polimer $ve ile gerçekten kolaylaştırır $$.

Ben sadece Angular'da nasıl gidileceğini merak ediyordum.

Öğreticiden örnek alın:

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

Sınıf tanımındaki tutma pveya inputöğenin referansını nasıl yakalayabilirim ?

Yanıtlar:


937

Oradan enjekte etmek ElementRefve kullanmak querySelectorveya bunlara benzer bir şey yapmak yerine, görünümdeki öğelere doğrudan erişmek için açıklayıcı bir yol kullanılabilir:

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

eleman

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

StackBlitz örneği

  • @ViewChild () , yönerge veya bileşen türünü parametre olarak veya şablon değişkeninin adını (dize) destekler.
  • @ViewChildren () aynı zamanda bir ad listesini virgülle ayrılmış liste olarak destekler (şu anda boşluğa izin verilmez @ViewChildren('var1,var2,var3')).
  • @ContentChild () ve @ContentChildren () aynı şeyi yapar, ancak açık DOM'da ( <ng-content>yansıtılan öğeler).

torunları

@ContentChildren() torunları da sorgulamaya izin veren tek kişi

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

{descendants: true}varsayılan olmalı ancak 2.0.0 finalinde değil ve bir hata olarak kabul
edildi Bu, 2.0.1'de düzeltildi

okumak

Bir bileşen ve yönergeler varsa, readparametre hangi örneğin döndürüleceğini belirtmeye izin verir.

Örneğin ViewContainerRef, varsayılan olarak dinamik olarak oluşturulan bileşenler için gerekenElementRef

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

değişiklikleri abone ol

Görünüm alt öğeleri yalnızca ngAfterViewInit()çağrıldığında ve içerik alt öğeleri yalnızca ngAfterContentInit()çağrıldığında ayarlanmış olsa da, sorgu sonucundaki değişikliklere abone olmak istiyorsanız,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));
}

doğrudan DOM erişimi

yalnızca DOM öğelerini sorgulayabilir, ancak bileşenleri veya yönerge örneklerini sorgulayamaz:

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

keyfi olarak yansıtılan içerik edinin

Bkz. Aktarılan içeriğe erişme


12
Açısal ekipler ElementRef'i kullanmamaları konusunda tavsiyede bulundu, bu daha iyi bir çözüm.
Onurlu Chow

7
Aslında bir inputde ElementRef, ancak ana bilgisayardan sorgulamak yerine, aslında istediğiniz öğeye başvuru alırsınız ElementRef.
Günter Zöchbauer

32
Aslında kullanmak ElementRefgayet iyi. Ayrıca kullanarak ElementRef.nativeElementile Renderergayet iyi. Ne önerilmez özelliklerini erişen bir ElementRef.nativeElement.xxxdoğrudan.
Günter Zöchbauer

2
@Natanael Bunun açık bir şekilde belgelenip belgelenmediğini veya nerede olduğunu bilmiyorum, ancak doğrudan DOM erişiminden kaçınılması gereken konularda veya diğer tartışmalarda (ayrıca Açısal ekip üyelerinden) düzenli olarak bahsediliyor. Doğrudan DOM'ye erişme (özelliklere ve yöntemlere erişmenin ne ElementRef.nativeElement)olduğu, Angulars sunucu tarafı oluşturma ve WebWorker özelliğini kullanmanızı engeller (yaklaşan çevrimdışı şablon derleyicisini de bozup kırmadığını bilmiyorum - ama sanmıyorum).
Günter Zöchbauer

10
Okuma bölümünde yukarıda belirtildiği gibi , ViewChild ile bir öğe için nativeElement almak istiyorsanız, aşağıdakileri yapmanız gerekir: @ViewChild('myObj', { read: ElementRef }) myObj: ElementRef;
jsgoupil

203

ElementRefBileşeninizin yapıcısına enjekte ederek DOM öğesini tanıtabilirsiniz:

constructor(myElement: ElementRef) { ... }

Dokümanlar: https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html


1
@Brocco cevabınızı güncelleyebilir misiniz? ElementRefGittiğinden beri güncel bir çözüm görmek istiyorum .
Jefftopia

23
ElementRefkullanılabilir (tekrar?).
Günter Zöchbauer

10
link DOM'a doğrudan erişim gerektiğinde bu API'yi son çare olarak kullanın . Bunun yerine Angular tarafından sağlanan şablonlama ve veri bağlama özelliklerini kullanın. Alternatif olarak, yerel öğelere doğrudan erişim desteklenmediğinde bile güvenle kullanılabilen API sağlayan Renderer'a göz atabilirsiniz. Doğrudan DOM erişimine güvenmek, uygulamanız ve oluşturma katmanları arasında sıkı bir bağlantı kurarak ikisini ayırmayı ve uygulamanızı bir web çalışanına dağıtmayı imkansız hale getirir.
sandeep talabathula

@sandeeptalabathula Bir üçüncü taraf kütüphanesinden kayan tarih seçici bileşeni eklemek için bir öğe bulmak için daha iyi bir seçenek nedir? Bunun orijinal soru olmadığının farkındayım, ancak DOM'daki öğeleri bulmanın tüm senaryolarda kötü olduğunu anlıyorsunuz ...
John

13
@john Ah .. tamam. Bunu deneyebilir this.element.nativeElement.querySelector('#someElementId')ve ElementRef'i bu şekilde yapıcıya aktarabilirsiniz .. private element: ElementRef, Import lib ...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) {
    // ...
  }
}

En son sürümle çalışacak şekilde güncellenen örnek

Yerel öğe hakkında daha fazla ayrıntı için burada


20

Açısal 4+ : Öğeyerenderer.selectRootElement erişmek için bir CSS seçiciyle kullanın.

Başlangıçta bir e-posta girişi gösteren bir form var. E-posta girildikten sonra form, projelerine ilişkin bilgi eklemeye devam etmeleri için genişletilir. Ancak, varolan bir istemci değilse , form proje bilgileri bölümünün üstünde bir adres bölümü içerir.

Şu andan itibaren, veri giriş kısmı bileşenlere ayrılmamıştır, bu nedenle bölümler * ngIf direktifleriyle yönetilmektedir. Mevcut bir istemci ise proje notları alanına veya yeni ise ilk ad alanına odaklanmam gerekir.

Çözümleri başarı ile denedim. Ancak, bu cevaptaki Güncelleme 3 bana nihai çözümün yarısını verdi. Diğer yarısı MatteoNY'nin bu konudaki tepkisinden geldi . Sonuç şudur:

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

Yaptığım tek şey bir öğeye odaklanmak olduğundan, değişiklik algılama ile kendimi endişelendirmeme gerek yok, bu yüzden aslında renderer.selectRootElementAngular dışında çağrı yapabilirim . Yeni bölümlerin işlenmesi için zaman vermem gerektiğinden, öğe bölümü denenmeden önce oluşturma iş parçacıklarının zamana yetişmesine izin vermek için öğe bölümü zaman aşımına uğradı. Tüm bunlar kurulduktan sonra, basit CSS seçicileri kullanarak elemanı çağırabilirim.

Bu örneğin öncelikle odak olayıyla ilgilendiğini biliyorum, ancak bunun başka bağlamlarda kullanılamaması benim için zor.


Renderer sınıfı Açısal 4.3.0'dan beri DEPRECATED. angular.io/api/core/Renderer
Jamie

15

Bileşen örneğini bir *ngIfveya içinde yakalamaya çalışan kişiler *ngSwitchCaseiçin bu numarayı takip edebilirsiniz.

Bir inityönerge oluşturun .

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

Bileşeninizi aşağıdaki gibi bir adla dışa aktarın myComponent

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

ElementRefAND MyComponentörneğini almak için bu şablonu kullanın

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

Bu kodu TypeScript'te kullan

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

12

ViewChilddekoratörü şu şekilde içe aktar @angular/core:

HTML Kodu:

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

TS Kodu:

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

class TemplateFormComponent {

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

artık sınıftaki herhangi bir öğeye erişmek için 'myForm' nesnesini kullanabilirsiniz.

Source


Ancak, bileşen sınıfındaki şablon öğelerine neredeyse erişmeniz gerekmediğini, sadece açısal mantığı doğru bir şekilde anlamanız gerektiğini fark etmelisiniz.
Hany

3
Hiçbirini kullanma, tür 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
Lütfen bu koda biraz açıklama yapın. Sadece açıklama yapmadan kod dökümü kesinlikle önerilmez.
rayryeng

5
Bu işe yaramaz: @ViewChild ek açıklamaları aracılığıyla ayarlanan öznitelikler yalnızca ngAfterViewInit yaşam döngüsü olayından sonra kullanılabilir. Yapıcıdaki değere erişmek, inputTxtbu durumda tanımlanmamış bir değer verir .
David

Ang 7 kullanarak, bu kusursuz çalıştı.
MizAkita

5

Not : Bu açısal 6 için geçerli ve yukarıdaki gelmezElementRefolduElementRef<T>ileTtürünü belirtennativeElement.

ElementRefTüm cevapların önerdiği gibi kullanıyorsanız , derhal ElementRefbenzeyen korkunç bir tür bildirime sahip bir sorunla karşılaşacağınızı eklemek isterim.

export declare class ElementRef {
  nativeElement: any;
}

Bu, nativeElement öğesinin bir olduğu bir tarayıcı ortamında aptalca bir durumdur HTMLElement.

Bu sorunu çözmek için aşağıdaki tekniği kullanabilirsiniz

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

interface ElementRef {
  nativeElement: HTMLElement;
}

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

1
Bu yaşadığım bir sorunu açıklıyor. Bu işe yaramaz çünkü başka itembir ElementRef'e ayarlasanız bile bir ElementRef olması gerekir let item:ElementRef, item2:ElementRef; item = item2; // no can do.. Çok kafa karıştırıcı. Ama bu iyi: işaretlediğiniz let item:ElementRef, item2:ElementRef; item = item2.nativeElementuygulama nedeniyle.
oooyaya

1
Aslında ilk örneğiniz let item: ElementRef, item2: ElementRef; item = item2kesin atama analizi nedeniyle başarısız oluyor. İkinciniz aynı sebeplerden dolayı başarısız olur, ancak item2tartışılan nedenlerle başlatılırsa (veya declare letburada kullanabileceğimiz atama için yararlı bir hızlı kontrol olarak) her ikisi de başarılı olur . Ne olursa olsun, anyböyle bir herkese açık API'da görmek gerçekten utanç verici .
Aluan Haddad

2

bir sonraki kardeşi almak için bunu kullanın

event.source._elementRef.nativeElement.nextElementSibling

1

Listeden hedef elemanın seçilmesi. Aynı elemanlar listesinden belirli bir eleman seçmek kolaydır.

bileşen kodu:

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 Kodu:

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

css kodu:

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

0

Hızlı kullanım için minimum örnek:

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. İlgili DOM öğesine bir şablon referans değişkeni koyun. Bizim örneğimizde bu #inputElüzerinde <input>etiketinin.
  2. Bileşen sınıfımızda DOM öğesini @ViewChild dekoratör aracılığıyla enjekte edin
  3. ngAfterViewInitYaşam döngüsü kancasındaki elemana erişin .

Not:

DOM öğelerini değiştirmek istiyorsanız, öğelere doğrudan erişmek yerine Renderer2 API'sını kullanın. DOM'a doğrudan erişime izin vermek uygulamanızı XSS ​​saldırılarına karşı daha savunmasız hale getirebilir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.