Реактивные формы — как вызвать отдельное действие только для одного поля в форме

#angular #angular-reactive-forms

#угловые #угловые реактивные формы

Вопрос:

У меня есть следующая простая форма. Я хочу кнопку, которая проверяет, доступно ли имя пользователя в одном конкретном поле. Если имя пользователя доступно, то основная кнопка отправки включена. Как мне этого добиться? Это форма внутри формы?

 <form mat-dialog-content [formGroup]="myForm"> 
<mat-form-field>
  <input matInput formControlName="firstName" />
  <mat-error>{{ error }}</mat-error>
</mat-form-field>

<mat-form-field>
  <input matInput  formControlName="lastName" />
  <mat-error>{{ error }}</mat-error>
</mat-form-field>

<mat-form-field>
  <input matInput formControlName="username" />
  <mat-error>{{ error }}</mat-error>
</mat-form-field>

//This button needs to be disabled unless username is filled out.  If user changes username the main submit button should be disabled unless user checks with this button.
<button (click)="userNameCheck()" >Check if available</button>


<mat-form-field>
  <input matInput formControlName="notes" />
  <mat-error>{{ error }}</mat-error>
</mat-form-field>

<button (click)="save()" [disabled]="myForm.invalid">Submit</button>
</form>

export const FORM_CONTROLS = {
  firstName: [null, Validators.required],
  lastName: [null, Validators.required],
  username: [null, Validators.required],
  notes: [null],
}


this.myForm = this.formBuilder.group(FORM_CONTROLS);

save() {
    const myData  = this.myForm.value;
    this.myService.saveData(myData);
}
  

Ответ №1:

Иметь форму внутри формы — плохая практика. «форма не должна содержать других элементов формы. Запреты HTML

Лучшим подходом было бы объявить свойство, например, usernameCheckField и включить его в привязку формы к ngModel см. Пример ниже

 <input type="password" [(ngModel)] = "usernameCheckField" [ngModelOptions]="{standalone: true}" />
  

обратите внимание на [ngModelOptions]="{standalone: true}" . Это позволит вам использовать шаблон, управляемый для модели, внутри модели реактивных форм.

Лучшим подходом к проверке, существует ли пользователь, было бы подписаться на valueChanges свойство username поля в вашем ngOnInit крючке жизненного цикла

 ngOnInit() {
  this.myForm.get("username").valueChanges.pipe(
    mergeMap((username) => this.myService.checkIfUserExists(username)),
    tap(res => {
      // Do some operations here
    })
  ).subscribe()
}
  

Не забудьте отказаться от подписки!. Вы можете рассмотреть возможность использования takeWhile операторов takeUntil из rxjs/operators для этого

Комментарии:

1. Является ли это рекомендуемым подходом? Похоже, что это уходит от формы, используя реактивные формы?

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

3. @KingKongFrog, «рекомендуемый подход» заключается в использовании асинхронных валидаторов: angular.io/guide /… . Другой подход заключается в том, что кнопка всегда была включена, и перед отправкой проверьте, существует ли сообщение электронной почты.

4. @KingKongFrog @Eliseo Я предоставил второй подход, чтобы гарантировать, что вы поддерживаете реактивный подход к форме. Для проблемы с несколькими вызовами рассмотрите возможность передачи debounce оператора и установки интервала между вводом пользователем и временем вызова службы, и вместо mergeMap использования switchMap , которое отменяет ранее наблюдаемое, когда предоставляется новый поток, в данном случае, когда пользователь вводит