首页 文章

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

提问于
浏览
351

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

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

10 回答

  • 16

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

    event.source._elementRef.nativeElement.nextElementSibling
    
  • 1

    你可以通过 ElementRef 获取DOM元素的句柄,方法是将它注入到组件的构造函数中:

    constructor(myElement: ElementRef) { ... }
    

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

  • 3
    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

  • 6

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

    component code:

    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 code:

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

    css code:

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

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

    Create an init directive.

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

    Export your component with a name such as myComponent

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

    Use this template to get the ElementRef AND MyComponent instance

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

    Use this code in TypeScript

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

    从@ 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/

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

    我想补充一点,如果您正在使用 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) { }
    }
    
  • 12

    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选择器简单地调用元素 .

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

  • 42

    可以使用声明方式代替直接访问视图中的元素,而不是从那里注入 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> 投影元素)中 .

    descendants

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

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

    {descendants:true}应该是默认值,但不是2.0.0 final,而且它被认为是一个bug
    这在2.0.1中得到修复

    read

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

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

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

    subscribe changes

    即使仅在调用 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));
    }
    

    direct DOM access

    只能查询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);
      }
    }
    

    get arbitrary projected content

    Access transcluded content

相关问题