关于angular:如何在组件模板中选择元素?

How can I select an element in a component template?

有人知道如何获取组件模板中定义的元素吗? 使用$$$,Polymer非常容易。

我只是想知道如何在Angular中实现它。

以教程为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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元素的引用?


可以使用声明方式代替直接访问视图中的元素,而不是从那里注入ElementRef和使用querySelector或类似的东西:

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

元件

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

StackBlitz的例子

  • @ViewChild()支持指令或组件类型作为参数,或模板变量的名称(字符串)。
  • @ViewChildren()还支持以逗号分隔列表的名称列表(当前不允许空格@ViewChildren('var1,var2,var3'))。
  • @ContentChild()和@ContentChildren()在轻量级DOM(投影元素)中执行相同的操作。

后人

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

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

<击>
{descendants: true}应该是默认值,但不是2.0.0 final,它被认为是一个bug

这是在2.0.1中修复的

如果有组件和指令,read参数允许指定应返回哪个实例。

例如,动态创建的组件而不是默认的ElementRef所需的ViewContainerRef

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

订阅更改

即使只在调用ngAfterViewInit()时设置了视图子项,并且仅在调用ngAfterContentInit()时设置了内容子项,如果要订阅查询结果的更改,则应在ngOnInit()中完成

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

1
2
3
4
5
6
7
@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元素,但不能查询组件或指令实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
  }
}

获得任意投射内容

请参阅访问已转换的内容


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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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) {
    // ...
  }
}

更新示例以使用最新版本

有关native元素的更多详细信息,请在此处


Angular 4+:
renderer.selectRootElement与CSS选择器一起使用以访问该元素。

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

截至目前,数据输入部分尚未分解为组件,因此这些部分使用* ngIf指令进行管理。如果它们是现有客户端,我需要将重点放在项目备注字段上,如果它们是新的,则需要将焦点放在第一个名称字段上。

我尝试了解决方案但没有成功。但是,这个答案中的更新3给了我最终解决方案的一半。另一半来自MatteoNY在这个帖子中的回应。结果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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选择器简单地调用元素。

我知道这个例子主要涉及焦点事件,但我很难在其他情况下使用它。


对于试图在*ngIf*ngSwitchCase中获取组件实例的人,您可以遵循此技巧。

创建init指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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之类的名称导出组件

1
2
3
4
5
6
7
@Component({
    selector: 'wm-my-component',
    templateUrl: 'my-component.component.html',
    styleUrls: ['my-component.component.css'],
    exportAs: 'myComponent'
})
export class MyComponent { ... }

使用此模板获取ElementRef AND myComponent实例

1
2
3
4
5
    <wm-my-component
           #myComponent="myComponent"
           *ngSwitchCase="Type.MyType"
           (init)="init($event, myComponent)">
    </wm-my-component>

在TypeScript中使用此代码

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

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

HTML代码:

1
2
3
4
<form #f="ngForm">
  ...
  ...
</form>

TS代码:

1
2
3
4
5
6
7
8
9
import { ViewChild } from '@angular/core';

class TemplateFormComponent {

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

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

Source


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 */
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*/
        };
    }
}


我想补充一点,如果你按照所有答案的建议使用ElementRef,那么你会立即遇到ElementRef有一个看起来很糟糕的类型声明的问题

1
2
3
export declare class ElementRef {
  nativeElement: any;
}

这在nativeElement是HTMLElement的浏览器环境中是愚蠢的。

要解决此问题,您可以使用以下技术

1
2
3
4
5
6
7
8
9
import {Inject, ElementRef as ErrorProneElementRef} from '@angular/core';

interface ElementRef {
  nativeElement: HTMLElement;
}

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


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

组件代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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代码:

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

</li>

css代码:

1
2
3
4
.selected {
  color: red;
  background:blue;
}

为了得到下一个兄弟,请使用它

1
event.source._elementRef.nativeElement.nextElementSibling