Предотвратить раннюю проверку при использовании автозаполнения и «updateOn: 'blur'»

Мое приложение Angular использует сложные проверки, выполняемые на стороне сервера. По этой причине я настроил, что обновления и проверки запускаются только по событиям размытие:

this.form = new FormGroup(
    { ... },
    {
        updateOn: 'blur'
    }
);

Он отлично работает, за исключением полей, в которых используется автозаполнение. Если автозаполнение открыто и пользователь щелкает мышью по записи, происходит неудачная последовательность событий:

  • Запускается событие размытие
  • Проверка выполняется с неполным старым значением и добавляет ошибку
  • Выбранное значение автозаполнения помещается в поле
  • Всплывающее окно автозаполнения закрывается, и поле снова получает фокус

Результат выглядит так, как показано ниже (упрощенный пример). Допустимое значение находится в текстовом поле, но оно помечено как ошибка, поскольку проверка выполнялась на старом значении.

Предотвратить раннюю проверку при использовании автозаполнения и «updateOn: 'blur'»

Технически правильно, что проверка была запущена, потому что щелчок всплывающего окна автозаполнения вызывает событие размытие. Однако с точки зрения пользовательского интерфейса это нонсенс. Проверка должна произойти, когда вы закончите работу с полем и перейдете к следующему полю.

Итак, как я могу предотвратить событие размытие и раннюю проверку?

Я создал простой Пример StackBlitz. Он использует аналогичную настройку, но выполняет проверку на стороне клиента (и проверяет, начинается ли текст с «ABC»). Чтобы воспроизвести проблему, введите «34», а затем выберите «ABC34» во всплывающем окне автозаполнения с помощью мыши.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
2
0
901
1

Ответы 1

для запуска при смене символа мы должны запускать событие ввода, а также событие автозаполнения изменения, поэтому вы можете попробовать что-то вроде этого:

в компоненте:

import { Component, OnInit , ViewChild , ElementRef} from '@angular/core';
import { VERSION } from '@angular/material';
import { FormGroup, FormControl } from '@angular/forms';
import { Observable, Subject } from 'rxjs';
import { startWith, map } from 'rxjs/operators';

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {


  @ViewChild('textInput') textInput: ElementRef;  




  version = VERSION;
  form: FormGroup;
  abcText: string = 'ABC1';
  anyText: string = '';
  public readonly abcChanges: Subject<string> = new Subject<string>();
  public abcSuggestions: Observable<string[]>;

  ngOnInit() {
    this.form = new FormGroup({
      abcText: new FormControl(this.abcText),
      anyText: new FormControl(this.anyText)
    }, {
        updateOn: 'blur'
      });

    this.form.valueChanges.subscribe(val => {
      this.validateData(val)}

    );

    this.abcSuggestions = this.abcChanges.pipe(
      startWith(''),
      map(val => this.generateSuggestions(val))
    );
  }

  private validateData(val: any) {
    console.info(val)
    // Would be more complex and happen on the server side
    const text: string = val['abcText'];
    const formControl = this.form.get('abcText');
    if (text.startsWith('ABC')) {
      formControl.setErrors(null);
    } else {
      formControl.setErrors({ abc: 'Must start with ABC' });
    }
  }

  private generateSuggestions(val: string) {
    let suggestions = [];
    if (!val.startsWith('ABC')) {
      suggestions.push('ABC' + val);
    }
    suggestions.push('ABC1');
    suggestions.push('ABC2');
    suggestions.push('ABC3');
    return suggestions;
  }

    validateOnCharacterChange(value) {
    console.info(value)
    const formControl = this.form.get('abcText');

    if (value.startsWith('ABC')) {
      formControl.setErrors(null);
    } else {
      formControl.setErrors({ abc: 'Must start with ABC' });
    }
    // this.textInput.nativeElement.blur();
  }
}

в html:

<mat-toolbar color = "primary">
    Angular Material 2 App
</mat-toolbar>
<div class = "basic-container">
    <form [formGroup] = "form" novalidate>
        <div>
            <mat-form-field>
                <input matInput [matAutocomplete] = "auto" formControlName = "abcText" (input) = "abcChanges.next($event.target.value)" placeholder = "Text starting with ABC" #textInput required (input) = "validateOnCharacterChange($event.target.value)">
                <mat-error>Must start with 'ABC'</mat-error>
            </mat-form-field>
            <mat-autocomplete #auto = "matAutocomplete" (optionSelected) = "validateOnCharacterChange($event.option.value)">
            <mat-option *ngFor = "let val of abcSuggestions | async" [value] = "val">{{ val }}</mat-option>
            </mat-autocomplete>
        </div>
    <div>&nbsp;</div>
    <div>
            <mat-form-field>
                <input matInput formControlName = "anyText" placeholder = "Any text">
                <mat-error></mat-error>
            </mat-form-field>
    </div>
    </form>
    <span class = "version-info">Current build: {{version.full}}</span>
</div>

проверьте рабочий stackblitz.

Кроме того, с помощью this.textInput.nativeElement.blur(); вы можете размыть в любом желаемом событии, а не просто щелкнуть за пределами ввода. надеюсь это поможет.

Спасибо за Ваш ответ. Но проверка каждого символа обходится слишком дорого. В реальном приложении проверка происходит на стороне сервера и, следовательно, занимает некоторое время. Поэтому я в первую очередь перешел на updateOn: 'blur'. Я ищу валидацию не чаще, а реже.

Codo 23.06.2018 11:37

поэтому я думаю, что достаточно проверить это на выбранной опции, удалить событие ввода и будет то, что вы хотите, и я думаю, что это не слишком дорого ?!

Fateme Fazli 23.06.2018 11:44

Да, это может быть обходной путь. Немного прискорбно, что он проверяется дважды в пределах части раздела и быстро мигает красным; но вроде работает.

Codo 23.06.2018 11:50

@Codo, или вы можете размыть выбранную опцию, и автоматически будет проверяться проверка.

Fateme Fazli 23.06.2018 11:52

Я не уверен, что понимаю решение. Как это должно работать и улучшить описанный выше обходной путь?

Codo 23.06.2018 14:25

@Codo проблема в том, что проверка проверяется, когда вы щелкаете вне ввода и размываете его, поэтому при автозаполнении, когда мы выбираем опцию, если мы не щелкаем вне ввода и не размываем его, проверка не будет проверяться и все равно будет иметь состояние до (действительный или недействительный), путем проверки проверки в методе onselect или размытия ввода в onselect проверка будет проверяться без щелчка на входе.

Fateme Fazli 23.06.2018 15:53

Извините, объяснение меня слишком сбивает с толку. Я этого не понимаю. В настоящее время у меня есть исходная реализация плюс дополнительная проверка при срабатывании optionSelected компонента автозаполнения (из вашего ответа). Кажется, что это работает, но немного некрасиво, так как ошибка проверки быстро появляется и исчезает. Что вы предлагаете изменить дополнительно?

Codo 23.06.2018 16:05

Другие вопросы по теме