angular - 如果这些数据依赖于几个 Observable,如何在 FormGroup 中显示 FormArray 数据?
问题描述
我浪费了几天时间试图显示异步动态加载的 FormArray 数据,但没有运气。简而言之,我有FormGroup
它是在单击时创建并显示在模式窗口中的(显示在订阅条例实体详细信息 API 调用中加载的数据没有问题(数据被加载到selectedOrdinance
对象中):
buildOrdinanceForm() {
this.ordinanceForm = this.fb.group({
ordinanceId: this.selectedOrdinance.ordinanceId // already available, loaded via API call to Ordinance details page in service observable 'subscribe'
...
ordinanceLanguages: new FormArray([]) // trouble here - data needs to be loaded asynchronously using another API
});
...
}
get ordinanceLanguages$(): Observable<FormArray> {
return combineLatest([
this.languageCodesLookupData$,
of(this.selectedOrdinance)
])
.pipe(
map(([lookupLanguages, selectedOrdinance]) => {
const ordinanceLanguageFormArray = new FormArray(
lookupLanguages
.map(x => new FormGroup({
ordinanceId: new FormControl(),
languageCode: new FormControl(x.id),
ordinanceAbbreviation: new FormControl({ value: '' }, [Validators.maxLength(20), Validators.required]),
...
})
));
ordinanceLanguageFormArray.controls.forEach(item => {
const matchingLanguage = selectedOrdinance.ordinanceLanguages?.find(
x => x.ordinanceId === selectedOrdinance.ordinanceId
&& x.languageCode === item.get('languageCode').value);
item.patchValue({
ordinanceId: selectedOrdinance.ordinanceId,
ordinanceAbbreviation: matchingLanguage?.ordinanceAbbreviation,
...
});
});
return ordinanceLanguageFormArray;
}),
catchError(error => of(new FormArray([])))
);
}
OrdinanceLanguages
导致麻烦:它依赖于已经可用的字段(selectedOrdinance
对象),也依赖于异步加载的 Observable 数据(languageCodesLookupData$
)。ordinanceLanguages
加载完成后我无法显示。这里的问题是 [formGroupName] 指令需要ordinanceLanguages
填写表单字段。而且我不知道如何在访问之前将数据从字段传输ordinanceLanguages$
到. 我试过使用订阅,使用类似的东西直接分配。但没有什么能正常工作 - 我要么根本没有得到数据,要么得到无穷无尽的输出,要么只有在完全加载后才能得到正确的数据..ordinanceLanguages
ordinanceLanguage.controls
ordinanceLanguages$
ngIf="setOrdinanceLanguagesToForm(ol)
languageCodesLookupData$
<ng-container *ngIf="(languageCodesLookupData$ | async) && (ordinanceLanguages$ | async) as ol">
<ng-container formArrayName="ordinanceLanguages" *ngFor="let item of ordinanceLanguages.controls; let i = index;">
<div [formGroupName]="i">
<div class="form-group row">...</div>
</div>
</ng-container>
</ng-container>
请看一下测试示例: https ://stackblitz.com/edit/angular-ivy-nnosbf?file=src/app/app.component.ts
我需要实现的是ordinanceLanguages
一旦准备好就加载 FormArray 数据......
解决方案
您应该在从 API 获取数据后简单地延迟表单创建,或者在检索到数据后重新创建表单。
我已经简化了一点你的代码,所以不再有这样的混乱Observables
。我还删除了表单修补步骤 - 只需立即创建具有适当值的表单。现在,对案件至关重要的所有内容都包含在buildOrdinanceForm()
.
https://stackblitz.com/edit/angular-ivy-gapevo?file=src/app/app.component.ts
import { ChangeDetectorRef, Component, OnInit, VERSION } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable, of, debounceTime, combineLatest, map, catchError, delay } from 'rxjs';
export interface Ordinance {
ordinanceId: number;
ordinanceLanguages: OrdinanceLanguage[];
}
export interface OrdinanceLanguage {
ordinanceId: number;
languageCode: string;
ordinanceAbbreviation: string;
}
export interface Lookup<T> {
id: T;
displayName: string;
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
ordinanceForm: FormGroup;
selectedOrdinance: Ordinance;
open: boolean;
isLoading=true;
constructor(private fb: FormBuilder,private cdr:ChangeDetectorRef) {}
get ordinanceLanguages(): FormArray {
return this.ordinanceForm.get('ordinanceLanguages') as FormArray;
}
getLanguageLookupDataApiCall(): Observable<Lookup<string>[]> {
const langs = [] as Lookup<string>[];
langs.push({ id: 'E', displayName: 'English' });
langs.push({ id: 'I', displayName: 'Italian' });
langs.push({ id: 'F', displayName: 'French' });
langs.push({ id: 'G', displayName: 'German' });
return of(langs).pipe(delay(1000));
}
ngOnInit(): void {
}
openDetails() {
this.open = true;
this.constructOrdinance();
this.buildOrdinanceForm();
}
closeDetails() {
this.open = false;
}
constructOrdinance() {
this.selectedOrdinance = {
ordinanceId: 1,
ordinanceLanguages: [
{ languageCode: 'E', ordinanceId: 1, ordinanceAbbreviation: 'EN-1' },
{ languageCode: 'G', ordinanceId: 1, ordinanceAbbreviation: 'DE-1' }
]
} as Ordinance;
}
buildOrdinanceForm() {
this.isLoading=true;
this.getLanguageLookupDataApiCall().subscribe(lookupLanguages=>{
const ordinanceLanguageFormArray = new FormArray(
lookupLanguages
.map(lang => {
const matchingLang=this.selectedOrdinance.ordinanceLanguages?.find(
x => x.ordinanceId === this.selectedOrdinance.ordinanceId
&& x.languageCode === lang.id);
console.log(matchingLang);
return new FormGroup({
ordinanceId: new FormControl(this.selectedOrdinance.ordinanceId),
languageCode: new FormControl(lang.id),
ordinanceAbbreviation: new FormControl(matchingLang?.ordinanceAbbreviation ?? '' ,[Validators.maxLength(20), Validators.required])});
})
);
this.ordinanceForm = this.fb.group({
ordinanceId: [
typeof this.selectedOrdinance.ordinanceId === 'number'
? this.selectedOrdinance.ordinanceId
: null
],
ordinanceLanguages: ordinanceLanguageFormArray
});
console.log(this.ordinanceForm.value);
},err=>console.error(err),()=>{this.isLoading=false}
)
}
}
推荐阅读
- firefox - How to fix "InvalidSessionIdException: Tried to run command without establishing a connection" error in RobotFramework?
- java - 如何使用 ThreadLocal.withInitial 初始化类型为 Map 的线程本地?
- xmpp - How to identify delivered and undelivered messages while retreiving chat history from ejabberd?
- spring - Spring Data JPA 排除特定 RequestMapping 的属性
- swift - 无法随我的请求发送授权标头
- html - How to make html tags responsive to layout size adjustments?
- ios - How do I get the lyrics of the currently playing song in Swift 4?
- r - 有没有办法进行过滤排名以保留完整的数据框?
- android - Can't load 64bit crypto++ library. Wrong path
- java - Merge two lists with common key in Java