角度指令未将焦点正确设置在最近禁用的输入上

时间:2018-11-16 12:26:01

标签: angular angular-directive

我有一个输入字段,我想在用户按下“ enter”键时禁用它。然后它将尝试获取用户详细信息。当它获取了详细信息或出现错误时,我想再次启用输入字段并将焦点设置在该字段上。

我正在使用此指令将焦点设置在输入字段上:

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

@Directive({
  selector: '[myFocus]'
})
export class FocusDirective implements OnChanges {

  @Input('myFocus') isFocused: boolean;
  constructor(private hostElement: ElementRef) { }

  ngOnChanges() {
    if(this.isFocused) {
        this.hostElement.nativeElement.focus();
    }
  }
}

它是在html中实现的,就像这样:

<input type="text" class="form-control" [(ngModel)]="text"
    [myFocus]="isFocused" [disabled]="disableInput" 
    (keydown)="podcastsKeyDown($event)">

podcastKeyDown()函数如下所示:

podcastsKeyDown(event) {
    if(event.keyCode == 13) {
        this.disableInput = true;
        this.getUser(this.text).subscribe(account => {
            this.disableInput = false;
            this.isFocused = true;
        });
    }
}

当用户帐户成功返回后,我将输入字段设置为启用,此字段将正常运行。但是,焦点不会返回到此输入。我尝试过在重新设置焦点周围设置超时,以防万一出现时序问题,但是这也不起作用。代码如下:

setTimeout(() => {
    this.isPodcastNamesFocused = true;
}, 100);

任何想法在这里发生了什么以及如何解决?

谢谢。

编辑1:-确切地说,它实际上在使用超时时第一次起作用,但是对于随后的用户输入却不起作用...

3 个答案:

答案 0 :(得分:1)

我建议以下解决方案。我在本地测试了此代码。它每次都对我有效。 这里的大多数代码都是用于演示。

指令:

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

@Directive({
  selector: '[myFocus]'
})
export class MyFocusDirective {
  // as for me I prefer using setters for properties that have non-trivial logic when being set. They are simple, predictable and robust.
  @Input('myFocus')
  set isFocused(val: boolean) {
    this._isFocused = val;
    if (this._isFocused) {
      this.hostElement.nativeElement.focus();
    }
  }
  get isFocused(): boolean {
    return this._isFocused;
  }

  _isFocused: boolean;
  constructor(private hostElement: ElementRef) { }
}

组件:

import { Component } from '@angular/core';
import {Observable, of, timer} from 'rxjs';
import {map, take} from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  text = 'app';
  isFocused: boolean = true;
  isDisabled: boolean = false;
  userData: {name: string, age: number} = null;

  podcastsKeyDown(event) {
    if (event.keyCode === 13) {
      this.isFocused = false;
      this.isDisabled = true;
      // take(1) not to have subscriptions hanging around
      this.getUser(this.text).pipe(take(1)).subscribe(account => {
        this.isDisabled = false;
        this.userData = account;
        // timeout first makes DOM to render setting isDisabled to false
        // when field is enabled we can make it focused
        setTimeout(() => {
          this.isFocused = true;
        }); 
      });
    }
  }

  // some mock data
  private getUser(name: string): Observable<{name: string, age: number}> {
    return timer(2000).pipe(map(() => {
      return {name, age: Math.round(Math.random() * 30 + 10)};
    }));
  }
}
<input type="text" class="form-control" [(ngModel)]="text"
       [myFocus]="isFocused" [disabled]="isDisabled"
       (keydown)="podcastsKeyDown($event)">
<p>{{account | json}}</p>

答案 1 :(得分:0)

ngOnChanges仅在存在纯更改的情况下才有效,这意味着您已将this.isFocused声明为true并试图再次将其设置为true {{1 }}不会触发

尝试类似的方法

ngOnChanges

或更改您的指令构造函数,例如@Input('myFocus') set isFocused(value: boolean) { if (value) { this.hostElement.nativeElement.focus(); } };

刚有了一个主意-希望它能起作用

尝试将constructor(@Host() @Self() private hostElement: ElementRef) { }变量作为reference传递并读取

@Input()

<input type="text" class="form-control" [(ngModel)]="text" #inputType [element]="inputType" [myFocus]="isFocused" [disabled]="disableInput" (keydown)="podcastsKeyDown($event)">

尝试立即关注@Input('element') elementType : ElementRef;

希望它有效-编码愉快!!

答案 2 :(得分:0)

这似乎是通过使用setTimeout()函数并记住在获取用户时将焦点设置为false来解决的。这一定是导致focus指令感到困惑的原因。

podcastsKeyDown(event) {
    if(event.keyCode == 13) {

        //This is the important line that was missing
        this.isFocused = false;

        this.disableInput = true;
        this.getUser(this.text).subscribe(account => {
            this.disableInput = false;
            setTimeout(() => {
                this.isFocused = true;
            });
        });
    }
}