Angular Directive неправильно устанавливает фокус на недавно отключенный вход - PullRequest
0 голосов
/ 16 ноября 2018

У меня есть поле ввода, которое я хочу отключить, когда пользователь нажимает клавишу «Ввод».Затем он попытается получить данные пользователя.Когда он извлек подробности или произошла ошибка, я хочу снова включить поле ввода и установить фокус на этом поле.

Я использую эту директиву, чтобы установить фокус на поле ввода:

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 голосов
/ 16 ноября 2018

Предлагаю следующее решение.Я тестировал этот код локально.И это работает каждый раз для меня.Большая часть кода приведена для демонстрации.

Директива:

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>
0 голосов
/ 16 ноября 2018

Похоже, что это было исправлено с помощью функции setTimeout () и запоминания, чтобы установить фокус на false во время выборки пользователя.Это, должно быть, приводило к путанице в директиве фокусировки.

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;
            });
        });
    }
}
0 голосов
/ 16 ноября 2018

ngOnChanges будет работать, только если имеется чистое средство изменения, если вы объявили this.isFocused как true и пытаетесь установить его как true снова 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;

Попробуйте сфокусировать elementType сейчас

Надеюсь, это работает - Удачного кодирования!!

...