Angular 9 реактивная форма обходит проверку формы, если раскрывающийся список заполняется автоматически - PullRequest
0 голосов
/ 17 июня 2020

Итак, я работаю над реактивной формой для информации о пользователе. У меня есть 2 раскрывающихся списка для провинции и муниципалитета . После инициализации формы в поле провинции будет указано *ngFor l oop, и в зависимости от выбора параметры муниципалитета также загрузятся с использованием *ngFor. Это при создании нового пользователя, но при редактировании я установил patchForm для автоматического выбора / отображения существующей провинции и муниципалитета.

В режиме редактирования формы я попытался изменить провинцию, которая вызывает муниципалитет, чтобы очистить существующее значение и загрузить выбор. Если я не выберу муниципалитет, форма все равно будет считаться действительной, но на console.log(this.form.value) я вижу, что municipality: null.

html файл

<div class="col-sm-12 col-md-3 col-lg-3 col-xl-3">
  <div class="form-group">
    <label for="province">Province<span class="text-danger">*</span></label>
    <select id="province" class="form-control" formControlName="province" (change)="onProvinceSelect()">
       <option value="">Please Select...</option>
       <option *ngFor="let province of provinces; let i = index" [value]="province">{{ province }}</option>
    </select>
  </div>
</div>
<div class="col-sm-12 col-md-3 col-lg-3 col-xl-3">
  <div class="form-group">
    <label for="municipality">Municipality<span class="text-danger">*</span></label>
    <select id="municipality" class="form-control" formControlName="municipality" (change)="onMunicipalitySelect()">
      <option value="">Please Select...</option>
      <option *ngFor="let municipality of municipalities" [value]="municipality.name">{{ municipality.name }}</option>
    </select>
  </div>
</div>

TS файл (форма инициализации - часть)

municipality: new FormControl(null, Validators.required),
province: new FormControl(null, Validators.required)

(если форма находится в режиме редактирования)

this.form.patchValue({
  province: 'ABC',
  municipality: 'DEF'
});
onProvinceSelect() {
  // some filtering of municipality based on province
  this.form.value.homeMunicipalityCity = null;
}

На данный момент я добавляю эту строку внутри onProvinceSelect() this.form.patchValue({ homeMunicipalityCity: null });

1 Ответ

0 голосов
/ 17 июня 2020

вы можете решить, используя конвейер asyn c и два наблюдаемых.

1. -Объявление двух наблюдаемых

  province$ = this.service.getProvince();
  municipy$;

2.-Создайте функцию для создания формы

createForm(data: any) {
    data = data || { province: null, municipy: null };
    return new FormGroup({
      province: new FormControl(data.province),
      municipy: new FormControl(data.municipy)
    });
  }

3.-Создайте функцию, которая возвращает Observable муниципалитетов

  listenChanges(form:FormGroup) {
    return form.get("province").valueChanges.pipe(
      distinctUntilChanged(),
      startWith(form.value?form.value.province:null),
      switchMap(res => this.service.getMunicipy(res)),
      tap((res: any[]) => {

        if (!res || !res.find(x => x == form.value.municipy))
          form.get("municipy").setValue(null);
      })
    );
  }

4.- при создании формы используйте функцию listenChanges

this.form = this.createForm({ province: "b", municipy: "bb" });
this.municipy$=this.listenChanges(this.form)

The. html

<form *ngIf="form" [formGroup]="form">
  Province:
  <select formControlName="province">
    <option *ngFor="let prov of province$|async" [ngValue]="prov">{{prov}}</option>
  </select>
  Municipy:
  <select formControlName="municipy">
    <option *ngFor="let prov of municipy$|async" [ngValue]="prov">{{prov}}</option>
  </select>
  </form>
  <button (click)="changeValues()">Change</button>

The stackblitz

Обновление БОНУС: как поставить "загрузка .." (*)

Если мы тоже определили Subject

  loading$=new Subject<boolean>()
  loadingDebounce$=this.loading$.pipe(debounceTime(50))

Мы можем использовать this.loading$.next(true) и this.loading$.next(false) в функции "listenChanges", используя "tap"

  listenChanges(form:FormGroup) {
    let alive=true;
    return form.get("province").valueChanges.pipe(
      distinctUntilChanged(),
      startWith(form.value?form.value.province:null),
      tap(()=>this.loading$.next(true)),
      switchMap(res => this.service.getMunicipy(res)),
      tap((res: any[]) => {
        this.loading$.next(false)
        if (!res || !res.find(x => x == form.value.municipy))
          form.get("municipy").setValue(null);
      })
    );
  }

И нашу форму, например,

<form *ngIf="form" [formGroup]="form">
   Province:
   <select  *ngIf="{data:province$|async} as provinces" formControlName="province">
      <ng-container *ngIf="provinces.data">
        <option [ngValue]="null" hidden>Select one</option>
        <option *ngFor="let prov of provinces.data" [ngValue]="prov">
          {{prov}}
        </option>
      </ng-container>
      <option *ngIf="!provinces.data">Loading....</option>
   </select>
   Municipy:
   <ng-container *ngIf="{data:municipy$|async} as municipies">
      <ng-container *ngIf="{obs:loadingDebounce$ | async} as loading">
         <select formControlName="municipy">
            <ng-container *ngIf="!loading.obs">
              <option [ngValue]="null" hidden>Select one</option>
              <option *ngFor="let prov of municipies.data" [ngValue]="prov">
                {{prov}}
              </option>
            </ng-container>
            <option *ngIf="loading.obs">Loading....</option>
         </select>
      </ng-container>
    </ng-container>
</form>

(*) ну вкратце объясню это "экстраендж" *ngIf="{data:municipy|async}". Это хорошо объясняется в блоге Юрия Каткова . Если такого рода всегда возвращает истину, поскольку это объект, иногда получает значение {data: null}, а иногда {data: [.....]}, поэтому элементы под <ng-container> всегда отображаются

...