如何专注于一个领域?

13

我将Angular 2升级到Angular 4,文档中写道应该使用Renderer2而不是已经被弃用的Renderer

现在我正在查看Renderer 源代码,但无法找到调用focus()方法的方法,就像以前那样。

旧方法:

this.renderer.invokeElementMethod(element, 'focus', []);

新方法是什么?

编辑

如果我要聚焦的元素是通过QuerySelector获取的呢?

例如:

let inputField = document.querySelectorAll('.dialog input');
if ( inputField[0] ) {
   inputField[0].focus();
}

由于它是通过QuerySelector获得的,因此它上面不存在focus()方法。


https://dev59.com/YlsW5IYBdhLWcg3w2aOK#34573219 - Günter Zöchbauer
1
请查看我的更新问题。我正在使用document.querySelectorAll方法。 - Hassan
6个回答

20

invokeElementMethod已经被弃用,并不会出现在新的渲染器中。要将焦点设置到元素上,现在可以直接使用以下方法:

element.nativeElement.focus();

如果你正在使用 document.querySelectorAll,那么你做的事情并不符合Angular的方式,你应该找到另一种方法来解决问题。如果没有其他方法,那么同样的原则也适用。 focus() 方法是纯 JavaScript 的,所以你可以在 Angular2/TypeScript 中使用它。但要确保在组件的 ngAfterViewInit 钩子中执行 querySelectorAll :

ngAfterViewInit() {
   let inputField: HTMLElement = <HTMLElement>document.querySelectorAll('.dialog input')[0];
   inputField && inputField.focus();
}

@Hassan 我已经更新了我的答案,因为我也意识到了这一点。querySelectorAll返回一个Element数组,但你可以确定它是一个HTMLElement,所以你可以将其类型提示为这样,这样TypeScript就不会抱怨了。 - Poul Kruijt
是的,现在根据您的更新答案它完美地运行了。非常感谢! :) - Hassan
3
我们不应该直接调用nativeElement,因为这样没有充分利用Angular的抽象层。相反,我们应该使用Renderer2 API来实现操作。 - Manu Chadha
@ManuChadha,渲染器上没有这样的方法 :) 但是在HTML中自己的元素上添加一个模板引用,并在其上调用focus()可能更具Angular风格。 - Poul Kruijt
我同意。我正在尝试按照Angular的方式编码,同时也试图保持抽象完整,但不幸的是,这个框架并不像我想象的那么抽象。我在Angular方面还很新,所以可能是我错了。 - Manu Chadha

15

另外,您还可以使用 Renderer2selectRootElement 方法。

例如:

constructor(private renderer: Renderer2) {}

this.renderer.selectRootElement('#domElementId').focus()

其中domElementId是您HTML中的id='domElementId'


谢谢伙计,Renderer2 API的selectRootElement对我有用。this.renderer.selectRootElement(this.email.nativeElement).focus();element.nativeElement.focus(); // 对我没用 - Hemant Kumar Singh
不推荐使用这种方法--https://github.com/angular/angular/issues/19554说,“selectRootElement()并不是用于一般情况的,正如你所猜测的那样,它只在引导程序中非常有用。” - Tim

6
template reference variable :#inlineEditControl
<input #inlineEditControl class="form-control form-control-lg"  [placeholder]="label">

 @ViewChild('inlineEditControl') inlineEditControl: ElementRef; // input DOM element

this.inlineEditControl.nativeElement.focus();

抱歉,我已经更新了我的问题,使用了document.querySelectorAll方法,因为我想要聚焦的字段应该是动态选择的。 - Hassan
1
我们应该避免直接在nativeElement上调用focus,因为这样我们没有正确使用Angular的抽象层。相反,我们应该使用Renderer2 API吗? - Manu Chadha

4

如果您正在使用Angular CDK,您可以使用FocusMonitor.focusVia方法来设置焦点,该方法是@angular/cdk/a11y可访问性模块(A11yModule)的一部分。

这样你就可以避免任何DOM操作并完全避免引用nativeElement


import { FocusMonitor } from '@angular/cdk/a11y';


export class ExampleComponent implements AfterViewInit {
  @ViewChild('elem', {static: false}) elemRef;

  constructor(private focusMonitor: FocusMonitor) {
  }

  ngAfterViewInit() {
    // Programmatically set focus. Focus source is 'program'.
    this.focusMonitor.focusVia(this.elemRef, 'program');
  }
}

3

目前看来,如果不直接修改DOM,这个任务似乎无法完成。正如其他人所说,invokeElementMethod()已被弃用。但是,使用selectRootElement也会出现问题,因为这并不是它的预期目的,你最终将失去DIV内部的子元素。以下是一个SO问题的链接,详细解释了selectRootElement(因为Angular文档太糟糕了):

Renderer multiple selectRootElement Issue (with plnkr provided)

目前,似乎唯一轻松实现目标的方法是使用模板引用变量、视图子元素和yourViewChild.nativeElement.focus()(尽管这种做法也不推荐)

还有一种建议的解决方法,可以在这个GitHub问题中找到FocusService:https://github.com/angular/angular/issues/15674

在这个问题中,Tytskyi提到可以实现一个FocusService,并根据AppModuleBrowser和AppModuleServer提供不同的focus()实现。我不确定它的详细工作原理,但这可能是在目前情况下实现此目标而不使用nativeElement的唯一方法。

谢谢。我一直在寻找这个解释。我知道我们应该使用Renderer2,但我找不到一个使用Renderer2来聚焦输入的示例。想知道为什么Renderer2中没有这样的方法,考虑到它似乎非常有用。有任何想法吗? - Manu Chadha

2
如果您声明了inputField变量的正确类型,就可以使用任何JS查询选择器。
例如,声明:
let inputField: HTMLInputElement = document.querySelector('.dialog input');
使您可以调用
inputField.focus();
这将起作用且不会抛出TS错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接