如何在组件模板中选择元素?

有人知道如何获取组件模板中定义的元素吗?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;
}
}

如何从类定义中捕获持有或获取pinput元素的引用?

878288 次浏览

您可以通过ElementRef将DOM元素的句柄注入组件的构造函数来获取它:

constructor(private myElement: ElementRef) { ... }

文档:https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html

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) {
// ...
}
}

示例更新以使用最新版本

有关原生元素的更多详细信息,这里

与其注入ElementRef并使用querySelector或类似的方法,不如使用声明式方式直接访问视图中的元素:

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

元素

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

StackBlitz示例

  • @陈志立支持指令或组件类型作为参数,或者模板变量的名称(字符串)。
  • @张子安还支持以逗号分隔列表的名称列表(目前不允许使用空格@ViewChildren('var1,var2,var3'))。
  • @ContentChildren()@ContentChildren()执行相同的操作,但在光DOM中(<ng-content>投影元素)。

后代

@ContentChildren()是唯一允许也查询后代的

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

<罢工> {descendants: true}应该是默认值,但不是在2.0.0最终版本中,它是被视为bug
这是在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);
}
}

获取任意投影内容

查看访问转录内容

对于试图在*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 { ... }

使用此模板获取ElementRef ANDMyComponent实例

<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) {
}
 */
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*/
};
}
}

注意:这不适用于Angular 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) { }
}

角4+: 使用renderer.selectRootElement和CSS选择器来访问元素。

我有一个最初显示电子邮件输入的表单。输入电子邮件后,表单将展开以允许他们继续添加与他们的项目相关的信息。但是,如果他们是没有现有客户,表单将包括项目信息部分上方的地址部分。

到目前为止,数据输入部分还没有被分解成组件,所以这些部分是用*ngif指令管理的。如果它们是现有客户端,我需要将焦点设置在项目笔记字段上,如果它们是新客户端,则设置在名字字段上。

我尝试了解决方案,但没有成功。然而,这个答案中的更新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');
}
}

由于我唯一要做的就是将焦点设置在元素上,因此我不需要担心更改检测,因此我实际上可以在Angular之外运行对renderer.selectRootElement的调用。因为我需要给新部分渲染时间,所以元素部分被包裹在超时中,以允许渲染线程在尝试元素选择之前赶上时间。一旦所有这些设置完成,我可以简单地使用基本的CSS选择器调用元素。

我知道这个例子主要处理焦点事件,但对我来说,这很难在其他情况下使用。

更新:Angular在Angular 4中放弃了对Renderer的支持,并在Angular 9中完全删除了它。此解决方案不应该受到迁移到Renderer2的影响。有关更多信息,请参阅此链接: 渲染器迁移到Renderer2

找到下一个兄弟姐妹用这个

event.source._elementRef.nativeElement.nextElementSibling

@angular/core导入ViewChild装饰器,如下所示:

超文本标记语言代码:

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

TS代码:

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


class TemplateFormComponent {


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

现在,您可以使用'myForm'对象来访问类中的任何元素。

Source

从列表中选择目标元素。很容易从相同元素的列表中选择特定元素。

组件代码:

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

快速使用的Mimimum示例:

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元素上。在我们的示例中,这是<input>标签上的#inputEl
  2. 在我们的组件类中,通过@ViewChildren装饰器注入DOM元素
  3. 访问ngAfterViewInit生命周期钩子中的元素。

备注:

如果您想操作DOM元素,请使用Renderer2 API而不是直接访问元素。允许直接访问DOM会使您的应用程序更容易受到XSS攻击

如果您使用的是Angular材质,您可以利用cdkFocus usInious指令。

示例:<input matInput cdkFocusInitial>

阅读更多: https://material.angular.io/cdk/a11y/overview#regions

对于*ngIf内部的组件,另一种方法:

我想选择的组件在div的*ngif语句中,上面@jsgoupil的答案可能有效(谢谢@jsgoupil!),但我最终找到了一种避免使用*ngif的方法,通过使用CSS来隐藏元素。

当[class Name]中的条件为真时,会显示div,并且使用#命名组件有效,并且可以从打字脚本代码中选择它。当条件为假时,它不会显示,无论如何我都不需要选择它。

组件:

@Component({
selector: 'bla',
templateUrl: 'bla.component.html',
styleUrls: ['bla.component.scss']
})
export class BlaComponent implements OnInit, OnDestroy {
@ViewChild('myComponentWidget', {static: true}) public myComponentWidget: any;
@Input('action') action: ActionType; // an enum defined in our code. (action could also be declared locally)


constructor() {
etc;
}


// this lets you use an enum in the HMTL (ActionType.SomeType)
public get actionTypeEnum(): typeOf ActionType {
return ActionType;
}


public someMethodXYZ: void {
this.myComponentWidget.someMethod(); // use it like that, assuming the method exists
}

然后在bla.component.html文件中:

<div [className]="action === actionTypeEnum.SomeType ? 'show-it' : 'do-not-show'">


<my-component #myComponentWidget etc></my-component>
</div>
<div>
<button type="reset" class="bunch-of-classes" (click)="someMethodXYZ()">
<span>XYZ</span>
</button>
</div>

和CSS文件:

 ::ng-deep {
.show-it {
display: block;   // example, actually a lot more css in our code
}
.do-not-show {
display: none';
}
}

我用了两种方法:

第一种方式:

您可以通过ElementRef将DOM元素的句柄注入组件的构造函数来获取它:

constructor(private myElement: ElementRef) {
this.myElement.nativeElement // <- your direct element reference
}

第二种方式:

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


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