Angular2 +自动对焦输入元素

时间:2017-01-26 12:53:18

标签: angular typescript

如何自动对焦输入元素?与this问题类似,但与AngularDart不同。像这样:

<input type="text" [(ngModel)]="title" [focus] />
//or 
<input type="text" [(ngModel)]="title" autofocus />

Angular2是否支持此功能?

最近的问题是this one,但有没有更短/更简单的解决方案,因为我没有“输入框列表”。在提供的链接中使用*ngFor="#input of inputs",但我在控件模板中只有1个输入。

10 个答案:

答案 0 :(得分:57)

这是我目前的代码:

import { Directive, ElementRef, Input } from "@angular/core";

@Directive({
    selector: "[autofocus]"
})
export class AutofocusDirective
{
    private focus = true;

    constructor(private el: ElementRef)
    {
    }

    ngOnInit()
    {
        if (this.focus)
        {
            //Otherwise Angular throws error: Expression has changed after it was checked.
            window.setTimeout(() =>
            {
                this.el.nativeElement.focus(); //For SSR (server side rendering) this is not safe. Use: https://github.com/angular/angular/issues/15008#issuecomment-285141070)
            });
        }
    }

    @Input() set autofocus(condition: boolean)
    {
        this.focus = condition !== false;
    }
}

用例:

[autofocus] //will focus
[autofocus]="true" //will focus
[autofocus]="false" //will not focus

过时的代码(旧答案,以防万一):
我最终得到了这段代码:

import {Directive, ElementRef, Renderer} from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class Autofocus
{
    constructor(private el: ElementRef, private renderer: Renderer)
    {        
    }

    ngOnInit()
    {        
    }

    ngAfterViewInit()
    {
        this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }
}

如果我将代码放在ngOnViewInit中,则无效。代码也使用最佳实践,因为直接调用元素不是recommended

已编辑(有条件自动对焦):
几天前我需要条件自动对焦,因为我隐藏了第一个自动对焦元素,我想要聚焦另一个,但只有当第一个不可见时,我才结束这段代码:

import { Directive, ElementRef, Renderer, Input } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class AutofocusDirective
{
    private _autofocus;
    constructor(private el: ElementRef, private renderer: Renderer)
    {
    }

    ngOnInit()
    {
    }

    ngAfterViewInit()
    {
        if (this._autofocus || typeof this._autofocus === "undefined")
            this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }

    @Input() set autofocus(condition: boolean)
    {
        this._autofocus = condition != false;
    }
}

<强> Edited2:
Renderer.invokeElementMethod is deprecated和新的Renderer2不支持它。 所以我们回归本地焦点(例如,它不能在DOM之外工作 - 例如SSR!)。

import { Directive, ElementRef, Input } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class AutofocusDirective
{
    private _autofocus;
    constructor(private el: ElementRef)
    {
    }

    ngOnInit()
    {
        if (this._autofocus || typeof this._autofocus === "undefined")
            this.el.nativeElement.focus();      //For SSR (server side rendering) this is not safe. Use: https://github.com/angular/angular/issues/15008#issuecomment-285141070)
    }

    @Input() set autofocus(condition: boolean)
    {
        this._autofocus = condition != false;
    }
}

用例:

[autofocus] //will focus
[autofocus]="true" //will focus
[autofocus]="false" //will not focus

答案 1 :(得分:34)

autofocus是一个原生的html功能,至少应该用于页面初始化。但是,它无法与许多角度场景一起使用,尤其是使用*ngIf

您可以制作一个非常简单的自定义指令来获得所需的行为。

import { Directive, OnInit, ElementRef } from '@angular/core';

@Directive({
  selector: '[myAutofocus]'
})
export class AutofocusDirective implements OnInit {

  constructor(private elementRef: ElementRef) { };

  ngOnInit(): void {
    this.elementRef.nativeElement.focus();
  }

}

上述指令适用于我的用例。

如何使用

<input *ngIf="someCondition" myAutofocus />

编辑:似乎有一些用例,在OnInit生命周期方法中尽早调用焦点。如果是这种情况,请改为OnAfterViewInit

答案 2 :(得分:14)

以下指令适用于我使用Angular 4.0.1

import {Directive, ElementRef, AfterViewInit} from '@angular/core';

@Directive({
  selector: '[myAutofocus]'
})
export class MyAutofocusDirective implements AfterViewInit {
  constructor(private el: ElementRef)
  {
  }
  ngAfterViewInit()
  {
    this.el.nativeElement.focus();
  }
}

像这样使用它:

<md-input-container>
    <input mdInput placeholder="Item Id" formControlName="itemId" name="itemId" myAutofocus>
</md-input-container>

使用OnInit生命周期事件的选项对我不起作用。我也尝试在另一个对我不起作用的答案中使用渲染器。

答案 3 :(得分:13)

<input type="text" [(ngModel)]="title" #myInput />
{{ myInput.focus() }}

在模板中输入后立即添加 {{myInput.focus()}}

答案 4 :(得分:12)

您可以为input元素指定模板引用变量#myInput

<input type="text" [(ngModel)]="title" #myInput />

让您的组件实现AfterViewInit,使用ViewChild注释获取输入元素的引用,并将您的元素集中在ngAfterViewInit钩子中:

export class MyComponent implements AfterViewInit {
    @ViewChild("myInput") private _inputElement: ElementRef;

    [...]

    ngAfterViewInit(): void {
        this._inputElement.nativeElement.focus();
    }
}

答案 5 :(得分:3)

从IE11开始,像其他所有现代浏览器一样,输入的本机HTML autofocus Attribute应该也可以正常工作,而无需使用Angular:

<input autofocus>
 
<input type="text" [(ngModel)]="title" autofocus>

答案 6 :(得分:1)

如果您不需要对/错功能,但始终需要设置自动对焦,那么Makla解决方案的实现方式会更短:

autofocus.directive.ts:

import { Directive, ElementRef, Input, OnInit } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})

export class AutofocusDirective implements AfterViewInit {

    constructor(private el: ElementRef) {
    }

    ngAfterViewInit() {
        // Otherwise Angular throws error: Expression has changed after it was checked.
        window.setTimeout(() => {
            this.el.nativeElement.focus();
        });
    }
}

用例:

<input autofocus> //will focus

使用AfterViewInit代替OnInit可以将光标放置在输入字段中的内容之后(如果填充了该光标)。

记住要在模块中声明导出您的指令!

答案 7 :(得分:1)

尝试这个简单但有效的功能。

function addFocusInput() {
  document.getElementById('text-focus').focus();
}

addFocusInput();
<input id="text-focus" type="text" placeholder=""/>

答案 8 :(得分:0)

我知道这是一篇旧文章,但对于其他寻求新答案的人: 我正在使用角度材质对话框,它是自动选择关闭按钮而不是输入。

使用cdk-focus-start(CDK的一部分)可以解决此问题,而无需其他代码。

答案 9 :(得分:0)

我的解决方案:

 <input type="text" id="searchInput">
// put focus on element with id 'searchInput', try every 100ms and retry 30 times
this.focus('searchInput',30,100);
focus( idElement:string, maxNbRetries:number, intervalMs:number){

    let nbRetries = 0;
    let elt = null;
    const stop$ = new Subject<boolean>();
    const source = interval(intervalMs);
    const source$ = source.pipe(
      tap(item=>{
        elt = document.getElementById(idElement);
        nbRetries++;
        if(nbRetries>maxNbRetries){
          stop$.next(true);
          console.log(`unable to put the focus on the element !`)
        }
      }),
      filter(item=>elt !=null),
      map(item=>{
        elt.focus();
        stop$.next(true);
      }),
      takeUntil(stop$)

    ).subscribe();
  }

焦点不适用于角度生命周期。为了将其强制在字段上,我运行了一个可观察到的对象,该对象发出了每个“ intervalMs”。如果该元素已经渲染,我可以通过其ID找到它。之后,我可以设置焦点。 如果找到nbRetries> maxNbRetries或元素ID,我将使用takeUntil运算符停止可观察对象。