从嵌套类设计嵌套反应式表单



我有以下类:

class License {
name:string;
.. lots of other fields.
nameAttributes:NameAttributes[];
}
class nameAttributes:NameAttributes{
index:number;
attribute:string;
}

我知道我可以像这样创建表单,但它需要我手动创建每个字段(控件),并且每次许可证类更改时,我都必须使用新字段更新类和此表单组。

this.licenseForm = formBuilder.group({
name:['name value'],
otherFields:['their values'],
nameAttributes: this.formBuilder.array([
this.formBuilder.group({
index:[1],
attribute:["attr1"]
}),
this.formBuilder.group({
index:[2],
attribute:["attr2"]
})
])
});

我宁愿我能将许可证类传递到 formBuilder 中,它会自动创建必要的 FormGroups 并根据它们的名称使它们可用,因此以下内容将创建两个组,一个"许可证"组和一个嵌套的"nameAttributes"组,其中包含许可证和 nameAttributes 的所有相关控件。

this.licenseForm = formBuilder.group({
license:formBuilder.group(license)
});

我是否错过了什么,或者如果没有一些严肃的类内省代码,这是不可能的?

@cyberthreat 如承诺的那样,这是一个带有ngx-sub-form的版本。

首先,这是一个现场演示的链接:https://stackblitz.com/edit/angular-td2iew

现在让我们看看它是如何构建的:

app.component.ts

@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public licenseUpdated(license: License): void {
console.log(JSON.stringify(license, null, 2));
}
}

app.component.html

<app-license-form (licenseUpdated)="licenseUpdated($event)"></app-license-form>

这里没有什么太花哨的,但请注意这样一个事实,即从这个组件中,我们绝对不在乎我们如何获得许可证的数据。我们只是希望在有更新后立即收到警告。

license-form.component.ts

@Component({
selector: 'app-license-form',
templateUrl: './license-form.component.html',
styleUrls: ['./license-form.component.css']
})
export class LicenseFormComponent extends NgxRootFormComponent<License> {
@DataInput()
@Input('license')
public dataInput: License | null | undefined;
@Output('licenseUpdated') public dataOutput: EventEmitter<License> = new EventEmitter();
protected getFormControls(): Controls<License> {
return {
name: new FormControl(null, [Validators.required]),
nameAttributes: new FormControl(null, [Validators.required]),
};
}
}

license-form.component.html

<form [formGroup]="formGroup">
<div>
Name<br>
<input type="text" [formControlName]="formControlNames.name">
</div>
<div>
License attributes<br>
<app-license-attributes-form [formControlName]="formControlNames.nameAttributes"></app-license-attributes-form>
</div>
<button class="save" (click)="manualSave()">Save form</button>
(look at your console to see when the form is saved!)
</form>
<div class="values">
Form group values
<pre>{{ formGroupValues | json }}</pre>
</div>

在这里,如果你不熟悉ngx-sub-form我邀请你阅读那篇博客文章:https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9 和/或项目的自述文件:https://github.com/cloudnc/ngx-sub-form

这里要注意的重要一点是,我们只关心原始值,其他一切都分解为子表单组件!此外,关于表单的所有内容都是类型安全的(tshtml!

license-attributes-form.component.ts

interface LicenseAttributesForm {
attributes: NameAttribute[];
}
@Component({
selector: 'app-license-attributes-form',
templateUrl: './license-attributes-form.component.html',
styleUrls: ['./license-attributes-form.component.css'],
providers: subformComponentProviders(LicenseAttributesFormComponent)
})
export class LicenseAttributesFormComponent extends NgxSubFormRemapComponent<NameAttribute[], LicenseAttributesForm> {
protected getFormControls(): Controls<LicenseAttributesForm> {
return {
attributes: new FormArray([], [Validators.required]),
};
}
protected transformToFormGroup(obj: NameAttribute[]): LicenseAttributesForm {
return {
attributes: obj ? obj : [],
};
}
protected transformFromFormGroup(formValue: LicenseAttributesForm): NameAttribute[] | null {
return formValue.attributes;
}
public addAttribute(): void {
(this.formGroupControls.attributes as FormArray).push(
this.createFormArrayControl(
'attributes',
{
index: null,
attribute: null
}
)
);
}
public removeAttribute(index: number): void {
(this.formGroupControls.attributes as FormArray).removeAt(index);
}
public createFormArrayControl(
key: any,
value: any,
): FormControl {
return new FormControl(value, [Validators.required]);
}
}

license-attributes-form.component.html

<div [formGroup]="formGroup">
<button (click)="addAttribute()">Add an attribute</button>
<div
class="attribute"
formArrayName="attributes"
*ngFor="let attribute of formGroupControls.attributes.controls; let i = index"
>
<app-license-attribute-form [formControl]="attribute"></app-license-attribute-form>
<button (click)="removeAttribute(i)">Delete</button>
</div>
</div>

最后是最后一个

license-attribute-form.component.ts

@Component({
selector: 'app-license-attribute-form',
templateUrl: './license-attribute-form.component.html',
styleUrls: ['./license-attribute-form.component.css'],
providers: subformComponentProviders(LicenseAttributeFormComponent)
})
export class LicenseAttributeFormComponent extends NgxSubFormComponent<NameAttribute> {
protected getFormControls(): Controls<NameAttribute> {
return {
index: new FormControl(null, [Validators.required]),
attribute: new FormControl(null, [Validators.required]),
};
}
}
license-attribute-form.component.html
<form [formGroup]="formGroup">
<div>
Index<br>
<input type="number" [formControlName]="formControlNames.index">
</div>
<div>
Attribute<br>
<input type="text" [formControlName]="formControlNames.attribute">
</div>
</form>

我真的鼓励你看看Stackblitz并玩这个演示,这将是更容易理解和发现我认为:)

如果你的对象有数据,你当然可以做到

看看这个堆叠闪电战

你有一个像这样的函数

createForm(data:any):FormGroup
{
const group=new FormGroup({});
for (let key in data)
{
if (Array.isArray(data[key])) //if is an array
{                             //first create the formArray
group.addControl(key,new FormArray([this.createForm(data[key][0])]))
for (let i=1;i<data[key].length;i++)  //after add the rest of the elements
(group.get(key) as FormArray).push(this.createForm(data[key][i]))
}
else
{
if (typeof(data[key])=="object") //if is an object we add a formGroup
group.addControl(key,this.createForm(data[key]))
else  //add a simple control
group.addControl(key,new FormControl(data[key]))
}
}
return group
}

并称其为

this.form=this.createForm(this.obj)

最新更新