ChatGPT解决这个技术问题 Extra ChatGPT

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

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


G
Günter Zöchbauer

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

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

元素

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

StackBlitz example

@ViewChild() 支持指令或组件类型作为参数,或模板变量的名称(字符串)。

@ViewChildren() 还支持以逗号分隔的名称列表(当前不允许使用空格 @ViewChildren('var1,var2,var3'))。

@ContentChild() 和 @ContentChildren() 做同样的事情,但在 light DOM( 投影元素)中。

子孙

@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


角度团队建议不要使用 ElementRef,这是更好的解决方案。
实际上 input 也是一个 ElementRef,但是您获得了对您实际想要的元素的引用,而不是从主机 ElementRef 查询它。
实际上使用 ElementRef 就可以了。也可以将 ElementRef.nativeElementRenderer 一起使用。 不鼓励直接访问 ElementRef.nativeElement.xxx 的属性。
@Natanael 我不知道是否或在何处明确记录了这一点,但在问题或其他讨论(也来自 Angular 团队成员)中经常提到应该避免直接 DOM 访问。直接访问 DOM(这是访问 ElementRef.nativeElement) 的属性和方法的方式,阻止您使用 Angulars 服务器端渲染和 WebWorker 功能(我不知道它是否也会破坏即将推出的离线模板编译器 - 但我想不会) .
如上面 read 部分所述,如果您想使用 ViewChild 获取元素的 nativeElement,您必须执行以下操作:@ViewChild('myObj', { read: ElementRef }) myObj: ElementRef;
u
user3761308

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

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

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


@Brocco 你能更新你的答案吗?由于 ElementRef 已经消失,我希望看到当前的解决方案。
ElementRef 可用(再次?)。
link 当需要直接访问 DOM 时,将此 API 用作最后的手段。改用 Angular 提供的模板和数据绑定。或者,您可以查看 Renderer,它提供了即使不支持直接访问本机元素也可以安全使用的 API。依赖直接 DOM 访问会在您的应用程序和渲染层之间创建紧密耦合,这将使得无法将两者分开并将您的应用程序部署到 Web Worker 中。
@sandeeptalabathula 查找元素以将浮动日期选择器组件从第三方库附加到的更好选择是什么?我知道这不是最初的问题,但你发现在 DOM 中查找元素在所有情况下都是不好的......
@john 啊.. 好的。您可以试试这个 - this.element.nativeElement.querySelector('#someElementId') 并像这样将 ElementRef 传递给构造函数.. private element: ElementRef, 导入库... import { ElementRef } from '@angular/core';
g
gdi2290
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


N
Neil T.

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

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

到目前为止,数据输入部分还没有被分解成组件,所以这些部分是用 *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');
    }
}

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

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

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


自 Angular 4.3.0 以来,Renderer 类已被弃用。 angular.io/api/core/Renderer
我们不能只使用 Renderer2 @Jamie 吗? https://angular.io/api/core/Renderer2#selectRootElement
j
jsgoupil

对于试图在 *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 AND MyComponent 实例

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

P
Prashant Pimpale

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

HTML 代码:

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

TS 代码:

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

class TemplateFormComponent {

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

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

Source


但是您应该注意到,您几乎不需要访问组件类中的模板元素,您只需要正确理解角度逻辑即可。
不要使用任何,类型是 ElementRef
我最终不得不使用“any”,因为我需要访问的元素是另一个包装 Kendo UI 元素的角度组件,我需要调用组件上的方法,然后调用 Kendo 元素上的方法。
K
Komal12
 */
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*/
        };
    }
}

请对此代码提供一些解释。非常不鼓励在没有解释的情况下简单地转储代码。
这不起作用:通过 @ViewChild 注释设置的属性仅在 ngAfterViewInit 生命周期事件之后可用。在这种情况下,访问构造函数中的值将为 inputTxt 产生一个未定义的值。
S
Sunderam Dubey

我使用了两种方式:

第一种方式:

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

A
Aluan Haddad

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

这解释了我遇到的一个问题。这不起作用,因为它会说 item 需要是一个 ElementRef,即使您将它设置为另一个 ElementRef: let item:ElementRef, item2:ElementRef; item = item2; // no can do. 。非常混乱。但这很好:let item:ElementRef, item2:ElementRef; item = item2.nativeElement 因为您指出的实现。
实际上,您的第一个示例 let item: ElementRef, item2: ElementRef; item = item2 由于明确的分配分析而失败。您的第二个失败的原因相同,但如果 item2 由于所讨论的原因而被初始化(或者作为对可分配性的有用快速检查,我们可以在此处使用 declare let)。无论如何,在这样的公共 API 上看到 any 真是太可惜了。
W
Willem van der Veen

快速使用的最小示例:

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

在感兴趣的 DOM 元素上放置一个模板引用变量。在我们的示例中,这是 标签上的#inputEl。在我们的组件类中通过 @ViewChild 装饰器注入 DOM 元素访问 ngAfterViewInit 生命周期钩子中的元素。

笔记:

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


A
Apoorv

要获得下一个兄弟姐妹,请使用此

event.source._elementRef.nativeElement.nextElementSibling

S
Stphane

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

组件代码:

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

T
TomEberhard

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

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

当 [className] 中的条件为真时,会显示 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'; 
    }
}

D
Djidel

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

示例:<input matInput cdkFocusInitial>

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