【问题标题】:How to focus on a field?如何专注于一个领域?
【发布时间】:2017-08-17 05:07:41
【问题描述】:

我从 Angular 2 更新到了 Angular 4,并且在文档中它被写入使用 Renderer2 而不是已弃用的 Renderer

现在我正在研究渲染器 source,但找不到像以前那样调用 focus() 方法的方法。

旧方法:

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

什么是新方法?

编辑

如果我关注的元素是通过QuerySelector获得的呢?

例如:

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

由于是通过QuerySelector获取的,所以上面不存在focus()方法。

【问题讨论】:

标签: javascript angular typescript


【解决方案1】:

invokeElementMethod 已弃用,并且不会重新回到新的渲染器中。要设置焦点到一个元素,您现在可以简单地使用它:

element.nativeElement.focus();

如果您使用document.querySelectorAll,您所做的事情不是角度方式,您应该找到另一种方式来做。如果没有其他方法可以做到这一点,那么同样的原则也适用。 focus() 方法是纯 javascript,因此您可以在 angular2/typescript 中使用它。不过,请务必在组件的 ngAfterViewInit 挂钩内执行 querySelectorAll

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

【讨论】:

  • @Hassan 我已经更新了我的答案,因为我意识到了这一点。 querySelectorAll 返回一个 Element 的数组,但您可能可以确定它是 HTMLElement,因此您可以输入提示,这样 TypeScript 就不会抱怨了
  • 是的,它现在与您更新的答案完美配合。非常感谢! :)
  • 我们不应该避免直接在 nativeElement 上调用焦点,因为那时我们没有正确使用 Angular 的抽象层。相反,我们应该使用 Renderer2 API 吗?
  • @ManuChadha 在 renderer2 上没有方法可以做到这一点:) 但也许 html self 中元素上的模板引用并调用 focus() 更加有角度
  • 我同意。我正在尝试以 Angular 方式编写代码,并尝试保持抽象的完整性,但不幸的是,该框架并不像我想象的那样抽象。在 Angular 中仍然很新,所以我可能错了
【解决方案2】:

你也可以使用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(); // 不适合我
  • 不是推荐的方法——github.com/angular/angular/issues/19554 说,“selectRootElement() 不适合一般用途,而且正如您所猜测的那样,它实际上只在引导程序中有用。”
【解决方案3】:
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 方法更新了我的问题,因为我想关注的领域应该是动态选择的。
  • 再一次,我们不应该避免直接在 nativeElement 上调用焦点,因为那时我们没有正确使用 Angular 的抽象层。相反,我们应该使用 Renderer2 API 吗?
【解决方案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');
  }
}

【讨论】:

    【解决方案5】:

    目前看来,如果不直接修改 DOM,这个任务似乎是无法完成的。正如其他人所说,invokeElementMethod() 已被弃用。但是,您也会发现使用 selectRootElement 的问题,因为这不是它的预期目的,您最终会失去 DIV 中的子项。这是一个 SO question 的链接,它更详细地解释了 selectRootElement (因为角度文档很糟糕):

    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 中不可用,考虑到这似乎非常有用。有什么想法吗?
    【解决方案6】:

    如果你声明了你的inputField var的正确类型,你可以使用任何JS查询选择器。

    例如声明:
    let inputField: HTMLInputElement = document.querySelector('.dialog input');
    让您可以拨打电话
    inputField.focus();
    它会工作并且不会抛出 TS 错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多