【问题标题】:How to get `DOM Element` in Angular 2? [duplicate]如何在 Angular 2 中获取“DOM 元素”? [复制]
【发布时间】:2016-12-21 01:08:25
【问题描述】:

我有一个包含<p> 元素的组件。这是(click) 事件将其更改为<textarea>。因此,用户可以编辑数据。我的问题是:

  • 如何将焦点放在textarea 上?
  • 如何获取元素,以便在其上应用.focus()
  • 我可以避免使用document.getElemenntById()吗?

我曾尝试使用“ElementRef”和“@ViewChild()”,但我似乎遗漏了一些东西:

app.component.ts

@ViewChild('tasknoteId') taskNoteRef:ElementRef;

noteEditMode: boolean = false;

get isShowNote (){
  return  !this.noteEditMode && this.todo.note  ? true : false;
}
taskNote: string;
toggleNoteEditMode () {
  this.noteEditMode = !this.noteEditMode; 
  this.renderer.invokeElementMethod(
    this.taskNoteRef.nativeElement,'focus'
  );
}

app.component.html

<span class="the-insert">
  <form [hidden]="!noteEditMode && todo.note">
    <textarea #tasknoteId id="tasknote"
     name="tasknote"
     [(ngModel)]="todo.note"
     placeholder="{{ notePlaceholder }}"
     style="background-color:pink"
     (blur)="updateNote()" (click)="toggleNoteEditMode()"
     [autofocus]="noteEditMode"
     [innerHTML]="todo.note">
   </textarea>
 </form>
</span>

【问题讨论】:

标签: angular dom


【解决方案1】:

ViewChild#TemplateVariable 一起使用,如下所示,

<textarea  #someVar  id="tasknote"
                  name="tasknote"
                  [(ngModel)]="taskNote"
                  placeholder="{{ notePlaceholder }}"
                  style="background-color: pink"
                  (blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }} 

</textarea>
 

在组件中, #最古老的方式

import {ElementRef} from '@angular/core';
@ViewChild('someVar') el:ElementRef;

ngAfterViewInit()
{
   this.el.nativeElement.focus();
}

#旧方式
import {ElementRef} from '@angular/core';
@ViewChild('someVar') el:ElementRef;
    
constructor(private rd: Renderer) {}
ngAfterViewInit() {
    this.rd.invokeElementMethod(this.el.nativeElement,'focus');
}

**2017 年 3 月 22 日(3 月)更新** #新的方法

请注意,Angular v4.0.0-rc.3 (2017-03-10) 已更改了一些内容。 由于 Angular 团队将弃用 invokeElementMethod,因此无法再使用上述代码。

重大变化

从 4.0 rc.1 开始:

将 RendererV2 重命名为 Renderer2
将 RendererTypeV2 重命名为 RendererType2
将 RendererFactoryV2 重命名为 RendererFactory2

import {ElementRef,Renderer2} from '@angular/core';
@ViewChild('someVar') el:ElementRef;

constructor(private rd: Renderer2) {}

ngAfterViewInit() {
      console.log(this.rd); 
      this.el.nativeElement.focus();      //<<<=====same as oldest way
}

console.log(this.rd) 将为您提供以下方法,您现在可以看到 invokeElementMethod 不存在。 附加 img 尚未记录。

注意:您可以使用以下 Rendere2 方法(带/不带 ViewChild 变量)来做很多事情。

【讨论】:

  • 它只是第一次工作,你知道吗?
  • 是的,它只会工作一次。你想要什么?
  • 我可以通过选择器找到 Renderer2 的子元素吗?
  • 这一切都是真的吗?在 vue 中,您在元素上放置一个 ref="myDomEl",然后是 this.$refs.myDomEl.focus().click().getBoundingClientRect() 或其他。无需导入,可工作 x 次,可在 vue 内部或外部工作。你不想一直这样做,但你也不希望它是不可能的???
  • 所以我试图将 tabindex=0 放置在由库动态创建的组件上 - 模板不存在,因此我无法向其中添加模板引用 (#someVar) ...我必须等待 dom 完成渲染,然后通过 vanilla javascript 获取它。但是,据我了解,Angular 不鼓励这样做……尽管这篇文章有所帮助-> blog.angularindepth.com/…
【解决方案2】:

更新(使用渲染器):

请注意,原来的 Renderer 服务现已在 Renderer2的青睐

Renderer2 official doc.

此外,正如@GünterZöchbauer 指出的那样:

其实用 ElementRef 就好了。也使用 带有 Renderer2 的 ElementRef.nativeElement 很好。什么是气馁 正在直接访问 ElementRef.nativeElement.xxx 的属性。


您可以通过使用elementRef 以及ViewChild 来实现此目的。但是不建议使用elementRef,原因是:

  • 安全问题
  • 紧密耦合

正如official ng2 documentation所指出的那样。

1。使用elementRef(直接访问):

export class MyComponent {    
constructor (private _elementRef : ElementRef) {
 this._elementRef.nativeElement.querySelector('textarea').focus();
 }
}

2。使用ViewChild更好的方法):

<textarea  #tasknote name="tasknote" [(ngModel)]="taskNote" placeholder="{{ notePlaceholder }}" 
style="background-color: pink" (blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }} </textarea> // <-- changes id to local var


export class MyComponent implements AfterViewInit {
  @ViewChild('tasknote') input: ElementRef;

   ngAfterViewInit() {
    this.input.nativeElement.focus();

  }
}

3。使用renderer:

export class MyComponent implements AfterViewInit {
      @ViewChild('tasknote') input: ElementRef;
         constructor(private renderer: Renderer2){           
          }

       ngAfterViewInit() {
       //using selectRootElement instead of depreaced invokeElementMethod
       this.renderer.selectRootElement(this.input["nativeElement"]).focus();
      }

    }

【讨论】:

  • 当我执行所有你的方法时,我得到无法读取属性 nativeElement pf undefined
  • 你在哪里使用renderer?刚刚在构造函数上注入了
  • 同样的错误,当我尝试在 上执行此方法时,我得到“无法读取未定义的属性'nativeElement'..”我正在使用 Angular 5,我尝试导入Elevant Zoom 脚本。
  • constructor (private _elementRef : ElementRef) 我用大写 E,从核心导入。
  • @PrasadW 您可以使用@ViewChildren('elemName')(第二种方法)来获取所有元素。
【解决方案3】:

Angular 2.0.0 最终版:

我发现使用ViewChild setter 是设置初始表单控件焦点的最可靠方法:

@ViewChild("myInput")
set myInput(_input: ElementRef | undefined) {
    if (_input !== undefined) {
        setTimeout(() => {
            this._renderer.invokeElementMethod(_input.nativeElement, "focus");
        }, 0);
    }
}

首先使用 undefined 值调用 setter,然后使用初始化的 ElementRef 调用。

工作示例和完整来源:http://plnkr.co/edit/u0sLLi?p=preview

使用 TypeScript 2.0.3 Final/RTM、Angular 2.0.0 Final/RTM 和 Chrome 53.0.2785.116 m(64 位)。

Angular 4+ 更新

Renderer 已弃用,取而代之的是 Renderer2,但 Renderer2 does not have invokeElementMethod。您将需要直接访问 DOM 以设置焦点,如 input.nativeElement.focus()

我仍然发现 ViewChild setter 方法效果最好。使用AfterViewInit 时,有时会出现read property 'nativeElement' of undefined 错误。

@ViewChild("myInput")
set myInput(_input: ElementRef | undefined) {
    if (_input !== undefined) {
        setTimeout(() => { //This setTimeout call may not be necessary anymore.
            _input.nativeElement.focus();
        }, 0);
    }
}

【讨论】:

  • setTimeout 包裹焦点的丑陋黑客在我的代码中发挥了魔力。谢谢你。
  • 如果您的 html 元素位于 index.html 中,这不是可靠的方法
猜你喜欢
  • 2017-06-18
  • 2017-10-19
  • 1970-01-01
  • 2017-05-16
  • 2017-02-28
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多