Làm thế nào tôi có thể chọn một yếu tố trong một mẫu thành phần?


516

Có ai biết làm thế nào để giữ một phần tử được xác định trong một mẫu thành phần không? Polyme làm cho nó thực sự dễ dàng với $$$.

Tôi chỉ tự hỏi làm thế nào để đi về nó trong Angular.

Lấy ví dụ từ hướng dẫ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;
    }
}

Làm cách nào để tôi bắt giữ hoặc lấy tham chiếu của phần tử phoặc inputphần tử từ trong định nghĩa lớp?

Câu trả lời:


938

Thay vì tiêm ElementRefvà sử dụng querySelectorhoặc tương tự từ đó, một cách khai báo có thể được sử dụng thay thế để truy cập trực tiếp các phần tử trong chế độ xem:

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

thành phần

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

Ví dụ về StackBlitz

  • @ViewChild () hỗ trợ chỉ thị hoặc loại thành phần làm tham số hoặc tên (chuỗi) của biến mẫu.
  • @ViewChildren () cũng hỗ trợ danh sách các tên dưới dạng danh sách được phân tách bằng dấu phẩy (hiện không có khoảng trắng được phép @ViewChildren('var1,var2,var3')).
  • @ContentChild ()@ContentChildren () làm tương tự nhưng trong DOM ánh sáng ( <ng-content>các phần tử được chiếu).

hậu duệ

@ContentChildren() là người duy nhất cho phép truy vấn con cháu

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

{descendants: true}phải là mặc định nhưng không phải là bản 2.0.0 cuối cùng và nó được coi là một lỗi
Điều này đã được sửa trong 2.0.1

đọc

Nếu có một thành phần và chỉ thị, readtham số cho phép chỉ định trường hợp nào sẽ được trả về.

Ví dụ ViewContainerRef, được yêu cầu bởi các thành phần được tạo động thay vì mặc địnhElementRef

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

đăng ký thay đổi

Mặc dù các khung nhìn con chỉ được đặt khi ngAfterViewInit()được gọi và nội dung con chỉ được đặt khi ngAfterContentInit()được gọi, nếu bạn muốn đăng ký thay đổi kết quả truy vấn, nên thực hiện trongngOnInit()

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

truy cập DOM trực tiếp

chỉ có thể truy vấn các phần tử DOM, nhưng không thể truy cập các thành phần hoặc thể hiện lệnh:

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

có được nội dung dự kiến ​​tùy ý

Xem nội dung truy cập


12
Các đội góc cạnh khuyên không nên sử dụng ElementRef, đây là giải pháp tốt hơn.
Chow đáng kính

7
Trên thực tế inputcũng là một ElementRef, nhưng bạn có được tham chiếu đến phần tử bạn thực sự muốn, thay vì truy vấn nó từ máy chủ lưu trữ ElementRef.
Günter Zöchbauer 30/03/2016

32
Thực tế sử dụng ElementReflà tốt. Cũng sử dụng ElementRef.nativeElementvới Rendererlà tốt. Điều không khuyến khích là truy cập các thuộc tính ElementRef.nativeElement.xxxtrực tiếp.
Günter Zöchbauer

2
@Natanael Tôi không biết liệu điều này được ghi lại rõ ràng hay không nhưng nó được đề cập thường xuyên trong các vấn đề hoặc các cuộc thảo luận khác (cũng từ các thành viên nhóm Angular) nên tránh truy cập DOM trực tiếp. Truy cập vào DOM trực tiếp (đó là những gì truy cập vào các thuộc tính và phương pháp ElementRef.nativeElement)là, ngăn chặn bạn từ việc sử dụng Angulars vẽ phía máy chủ và tính năng WebWorker (Tôi không biết nếu nó cũng phá vỡ ẩn mẫu biên dịch sắp tới - nhưng tôi đoán không).
Günter Zöchbauer

10
Như đã đề cập ở trên trong phần đọc , nếu bạn muốn lấy phần tử gốc cho một phần tử với ViewChild, bạn phải làm như sau: @ViewChild('myObj', { read: ElementRef }) myObj: ElementRef;
jsgoupil

203

Bạn có thể xử lý phần tử DOM thông qua việc ElementRefđưa nó vào hàm tạo của thành phần:

constructor(myElement: ElementRef) { ... }

Tài liệu: https://angular.io/docs/ts/latest/api/core/index/EuityRef- class.html


1
@Brocco bạn có thể cập nhật câu trả lời của bạn? Tôi muốn thấy một giải pháp hiện tại kể từ khi ElementRefkhông còn nữa.
Jefftopia

23
ElementRefcó sẵn (một lần nữa?).
Günter Zöchbauer

10
link Sử dụng API này như là phương sách cuối cùng khi cần truy cập trực tiếp vào DOM. Thay vào đó, sử dụng templating và ràng buộc dữ liệu được cung cấp bởi Angular. Ngoài ra, bạn hãy xem Trình kết xuất cung cấp API có thể được sử dụng một cách an toàn ngay cả khi không truy cập trực tiếp vào các phần tử gốc. Dựa vào truy cập DOM trực tiếp tạo ra sự kết hợp chặt chẽ giữa ứng dụng của bạn và các lớp kết xuất, điều này sẽ khiến không thể tách hai và triển khai ứng dụng của bạn thành một nhân viên web.
Sandeep Talabathula

@sandeeptalabathula Tùy chọn nào tốt hơn để tìm một phần tử để đính kèm thành phần bộ chọn ngày trôi nổi từ thư viện của bên thứ ba? Tôi biết rằng đây không phải là câu hỏi ban đầu, nhưng bạn hiểu rằng việc tìm kiếm các yếu tố trong DOM là không tốt trong tất cả các tình huống ...
John

13
@john À .. được rồi. Bạn có thể thử điều này - this.element.nativeElement.querySelector('#someElementId')và chuyển ElementRef cho nhà xây dựng như thế này .. private element: ElementRef, Nhập 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) {
    // ...
  }
}

Ví dụ được cập nhật để hoạt động với phiên bản mới nhất

Để biết thêm chi tiết về yếu tố bản địa, tại đây


20

Angular 4+ : Sử dụng renderer.selectRootElementvới bộ chọn CSS để truy cập phần tử.

Tôi đã có một hình thức ban đầu hiển thị một đầu vào email. Sau khi email được nhập, biểu mẫu sẽ được mở rộng để cho phép họ tiếp tục thêm thông tin liên quan đến dự án của họ. Tuy nhiên, nếu họ không phải là khách hàng hiện tại, biểu mẫu sẽ bao gồm một phần địa chỉ phía trên phần thông tin dự án.

Cho đến nay, phần nhập dữ liệu chưa được chia thành các thành phần, vì vậy các phần được quản lý bằng chỉ thị * ng If. Tôi cần đặt trọng tâm vào trường ghi chú dự án nếu họ là khách hàng hiện tại hoặc trường tên đầu tiên nếu họ là người mới.

Tôi đã thử các giải pháp không thành công. Tuy nhiên, Cập nhật 3 trong câu trả lời này đã cho tôi một nửa giải pháp cuối cùng. Nửa còn lại đến từ phản ứng của MatteoNY trong chủ đề này . Kết quả là thế này:

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

Vì điều duy nhất tôi đang làm là tập trung vào một yếu tố, tôi không cần quan tâm đến việc phát hiện thay đổi, vì vậy tôi thực sự có thể thực hiện cuộc gọi đến renderer.selectRootElement bên ngoài Angular. Vì tôi cần cho thời gian của các phần mới để kết xuất, phần tử được gói trong thời gian chờ để cho phép các chủ đề kết xuất bắt kịp thời gian trước khi thử lựa chọn phần tử. Khi tất cả đã được thiết lập, tôi chỉ có thể gọi phần tử bằng các bộ chọn CSS cơ bản.

Tôi biết ví dụ này chủ yếu liên quan đến sự kiện trọng tâm, nhưng thật khó cho tôi rằng điều này không thể được sử dụng trong các bối cảnh khác.


Trình kết xuất lớp được ĐỔI kể từ Angular 4.3.0. angular.io/api/core/Renderer
Jamie

15

Đối với những người đang cố lấy ví dụ thành phần bên trong một *ngIfhoặc *ngSwitchCase, bạn có thể làm theo thủ thuật này.

Tạo một initchỉ thị.

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

Xuất thành phần của bạn với một tên như myComponent

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

Sử dụng mẫu này để lấy ví dụ ElementRefANDMyComponent

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

Sử dụng mã này trong TypeScript

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

12

nhập ViewChildtrang trí từ@angular/core , như vậy:

Mã HTML:

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

Mã TS:

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

class TemplateFormComponent {

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

bây giờ bạn có thể sử dụng đối tượng 'myForm' để truy cập bất kỳ phần tử nào trong nó trong lớp.

Source


Nhưng bạn nên lưu ý rằng bạn hầu như không cần truy cập các phần tử mẫu trong lớp thành phần, bạn chỉ cần hiểu chính xác logic góc.
Hany

3
Dont sử dụng bất kỳ, loại là 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
Vui lòng cung cấp một số giải thích cho mã này. Đơn giản là mã bán phá giá mà không có lời giải thích là rất nản lòng.
rayryeng

5
Điều này sẽ không hoạt động: các thuộc tính được đặt qua chú thích @ViewChild sẽ chỉ khả dụng sau sự kiện vòng đời ngAfterViewInit. Truy cập giá trị trong hàm tạo sẽ mang lại giá trị không xác định inputTxttrong trường hợp đó.
David M.

Sử dụng ang 7, điều này làm việc hoàn hảo.
MizAkita

5

Lưu ý : Điều này không áp dụng đối với góc 6 trở lên nhưElementRefđã trở thànhElementRef<T>vớiTbiểu thị các loạinativeElement .

Tôi muốn nói thêm rằng nếu bạn đang sử dụng ElementRef, theo khuyến nghị của tất cả các câu trả lời, thì bạn sẽ gặp ngay vấn đề ElementRefcó khai báo kiểu khủng khiếp trông giống như

export declare class ElementRef {
  nativeElement: any;
}

Điều này thật ngu ngốc trong một môi trường trình duyệt trong đó localEuity là một HTMLElement .

Để khắc phục điều này, bạn có thể sử dụng các kỹ thuật sau đây

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

interface ElementRef {
  nativeElement: HTMLElement;
}

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

1
Điều này giải thích một vấn đề tôi đang gặp phải. Điều này không hoạt động vì nó sẽ itemcần phải là ElementRef, mặc dù bạn đang đặt nó thành ElementRef khác : let item:ElementRef, item2:ElementRef; item = item2; // no can do.. Rất bối rối. Nhưng điều này là tốt: let item:ElementRef, item2:ElementRef; item = item2.nativeElementbởi vì việc thực hiện bạn đã chỉ ra.
oooyaya

1
Trên thực tế ví dụ đầu tiên của bạn let item: ElementRef, item2: ElementRef; item = item2thất bại vì phân tích phân công xác định. Lần thứ hai của bạn thất bại vì những lý do tương tự nhưng cả hai đều thành công nếu item2được khởi tạo vì những lý do đã thảo luận (hoặc như một kiểm tra nhanh hữu ích cho khả năng chuyển nhượng mà chúng ta có thể sử dụng declare letở đây). Bất kể, thực sự xấu hổ khi thấy anytrên một API công khai như thế này.
Aluan Haddad

2

để có được anh chị em tiếp theo, hãy sử dụng cái này

event.source._elementRef.nativeElement.nextElementSibling

1

Chọn phần tử mục tiêu từ danh sách. Thật dễ dàng để chọn phần tử cụ thể từ danh sách các phần tử tương tự.

mã thành phần:

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

Mã HTML:

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

mã css:

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

0

Ví dụ tối ưu để sử dụng nhanh chóng:

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. Đặt một biến tham chiếu mẫu trên thành phần DOM quan tâm. Trong ví dụ của chúng tôi, đây là #inputEltrên <input>thẻ.
  2. Trong lớp thành phần của chúng ta, chèn phần tử DOM thông qua trình trang trí @ViewChild
  3. Truy cập phần tử trong ngAfterViewInitmóc vòng đời.

Ghi chú:

Nếu bạn muốn thao tác các phần tử DOM, hãy sử dụng API Renderer2 thay vì truy cập trực tiếp vào các phần tử. Cho phép truy cập trực tiếp vào DOM có thể khiến ứng dụng của bạn dễ bị tấn công XSS hơn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.