Угловая пользовательская фокусировка Директива.Фокус первого неверного ввода формы - PullRequest
0 голосов
/ 10 июня 2019

Я создал директиву для фокусировки ввода, если он недействителен

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

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[focusOnError]'
})
export class HighlightDirective implements OnChanges {
  @Input() submitted: string;

  constructor(private renderer: Renderer2, private el: ElementRef) { }

  ngOnChanges(): void {
    const el = this.renderer.selectRootElement(this.el.nativeElement);
    if (this.submitted && el && el.classList.contains('ng-invalid') && el.focus) {
      setTimeout(() => el.focus());
    }
  }

}

У меня есть реактивная форма с двумя входами, и я применил директиву к обоим входам

<form>
  ...
  <input type="text" id="familyName" focusOnError />
  ...
  <input type="text" id="appointmentCode" focusOnError />
  ...
</form>

После отправки формы она работает нормально, но я пытаюсь добиться следующего:

Ожидаемый результат: - После отправки формы, если оба ввода неверны, следует указать только первый.

Текущий результат: - После отправки формы, если оба ввода неверны, второй становится сфокусированным.

Я не знаю, как указать «делайте это, только если это первый ребенок», я безуспешно пытался использовать селектор директивы.

Есть идеи?

Заранее большое спасибо.

Ответы [ 2 ]

1 голос
/ 10 июня 2019

Для управления входами формы, я думаю, лучшее решение - использовать ViewChildren для получения всех элементов. Таким образом, мы можем зациклить эти элементы и сосредоточить внимание на первом.

Итак, мы можем иметь более простую директиву:

@Directive({
  selector: '[focusOnError]'
})
export class FocusOnErrorDirective  {

  public get invalid()
  {
    return this.control?this.control.invalid:false;
  }
  public focus()
  {
     this.el.nativeElement.focus()
  }
  constructor(@Optional() private control: NgControl,  private el: ElementRef) {  }
}

И, в нашем компоненте у нас есть что-то вроде

@ViewChildren(FocusOnErrorDirective) fields:QueryList<FocusOnErrorDirective>
check() {
    const fields=this.fields.toArray();
    for (let field of fields)
    {
      if (field.invalid)
      {
        field.focus();
        break;
      }
    }
  }

Вы можете увидеть в действии * * * * * * * * * * * * * * * 1011 *

ОБНОВЛЕНИЕ всегда можно улучшить:

Почему бы не создать директиву, которая применяется к форме?

@Directive({
  selector: '[focusOnError]'
})
export class FocusOnErrorDirective {

  @ContentChildren(NgControl) fields: QueryList<NgControl>

  @HostListener('submit')
  check() {
    const fields = this.fields.toArray();
    for (let field of fields) {
      if (field.invalid) {
        (field.valueAccessor as any)._elementRef.nativeElement.focus();
        break;
      }
    }
  }

Итак, наш .html это как

<form [formGroup]="myForm" focusOnError>
  <input type="text" formControlName="familyName" />
  <input type="text" formControlName="appointmentCode" />
  <button >click</button>
</form>

См. stackblitz

Еще больше, если использовать в качестве селектора форму

@Directive({
  selector: 'form'
})

Даже мы можем убрать focusOnError в виде

<form [formGroup]="myForm" (submit)="submit(myForm)">
..
</form>

Обновление 2 Проблемы с formGroup с formGroup.

NgControl учитывает только те элементы управления, которые имеют [(ngModel)], formControlName и [formControl], поэтому. Если мы можем использовать форму как

myForm = new FormGroup({
    familyName: new FormControl('', Validators.required),
    appointmentCode: new FormControl('', Validators.required),
    group: new FormGroup({
      subfamilyName: new FormControl('', Validators.required),
      subappointmentCode: new FormControl('', Validators.required)
    })
  })

Мы можем использовать форму как:

<form [formGroup]="myForm"  focusOnError (submit)="submit(myForm)">
  <input type="text" formControlName="familyName" />
  <input type="text" formControlName="appointmentCode" />
  <div >
    <input type="text" [formControl]="group.get('subfamilyName')" />
    <input type="text" [formControl]="group.get('subappointmentCode')" />
  </div>
  <button >click</button>
</form>

где в .ts у нас есть

get group()
  {
    return this.myForm.get('group')
  }
1 голос
/ 10 июня 2019

ну, просто смешно stackblitz . Если у нас есть formControl, мы можем внедрить ngControl, что это сам элемент управления. Таким образом, мы можем получить formGroup. Я управляю «подчиненным», делая обходной путь в app.component

<button (click)="check()">click</button>

  check() {
    this.submited = false;
    setTimeout(() => {
      this.submited = true;
    })
  }

Директива похожа на

export class FocusOnErrorDirective implements OnInit {
  @HostListener('input')
  onInput() {
    this._submited = false;
  }

  //I used "set" to avoid ngChanges, but then I need the "ugly" work-around in app.component
  @Input('focusOnError')
  set submited(value) {
    this._submited = value;
    if (this._submited) {  ((is submited is true
      if (this.control && this.control.invalid) { //if the control is invalid
        if (this.form) {
          for (let key of this.keys)  //I loop over all the
          {                           //controls ordered
            if (this.form.get(key).invalid) {  //If I find one invalid
              if (key == this.control.name) {  //If it's the own control
                setTimeout(() => {
                  this.el.nativeElement.focus()   //focus
                });
              }
              break;                           //end of loop
            }
          }
        }
        else
          this.el.nativeElement.focus()
      }
    }
  }
  private form: FormGroup;
  private _submited: boolean;
  private keys: string[];

  constructor(@Optional() private control: NgControl,  private el: ElementRef) {  }

  ngOnInit() {
    //in this.form we has the formGroup.
    this.form = this.control?this.control.control.parent as FormGroup:null;
    //we need store the names of the control in an array "keys"
    if (this.form)
    this.keys = JSON.stringify(this.form.value)
      .replace(/[&\/\\#+()$~%.'"*?<>{}]/g, '')
      .split(',')
      .map(x => x.split(':')[0]);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...