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

时间:2015-09-21 10:34:46

标签: angular typescript angular-components

有人知道如何获取组件模板中定义的元素吗?使用$$$

,聚合物变得非常简单

我只是想知道如何在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元素的引用?

13 个答案:

答案 0 :(得分:775)

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

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

元素

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

StackBlitz example

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

<强>后代

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

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

<击> {descendants: true}应该是默认值,但不是2.0.0最终版本considered a 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);
  }
}

获取任意预计内容

请参阅Access transcluded content

答案 1 :(得分:185)

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

constructor(myElement: ElementRef) { ... }

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

答案 2 :(得分:46)

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

已更新示例以使用最新版本

有关原生元素的更多详情,here

答案 3 :(得分:17)

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

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

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

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

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之外。角。因为我需要给新部分渲染时间,所以元素部分被包装在超时中以允许渲染线程在尝试元素选择之前赶上时间。完成所有设置之后,我可以使用基本的CSS选择器简单地调用元素。

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

答案 4 :(得分:12)

对于尝试抓取*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 { ... }

使用此模板获取ElementRefMyComponent实例

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

答案 5 :(得分:8)

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

答案 6 :(得分:8)

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

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



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

    class TemplateFormComponent {

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

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

源: https://codecraft.tv/courses/angular/forms/template-driven/

答案 7 :(得分:3)

我想补充一点,如果你按照所有答案的建议使用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) { }
}

答案 8 :(得分:3)

用于快速使用的最小示例:

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攻击

答案 9 :(得分:1)

立即获得下一个兄弟,请使用此

event.source._elementRef.nativeElement.nextElementSibling

答案 10 :(得分:1)

我有两种方式:

第一种方式:

您可以通过将 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);
  }

答案 11 :(得分:0)

如果您使用的是Angular Material,则可以利用cdkFocusInitial指令。

示例:<input matInput cdkFocusInitial>

在此处了解更多信息: https://material.angular.io/cdk/a11y/overview#regions

答案 12 :(得分:0)

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

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

当[className]中的条件为true时,将显示div,并使用#works命名组件,并且可以从打字稿代码中进行选择。如果条件为假,则不显示它,而且我也不需要选择它。

组件:

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