вы можете решить, используя конвейер 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>
всегда отображаются