Angular - 响应式表单:如何正确获取动态和可重用子表单的值
Angular - Reactive Form: How to correctly get values of dynamical and reusable sub-forms
我正在处理如下表格
我的代码
嵌套-form.component.ts
public nestedForm: FormGroup;
constructor(
private formBuilder: FormBuilder) {
this.calibrationForm = this.buildFormGroup(formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
return new FormGroup({
motor: new FormControl('', Validators.required),
properties: new FormControl(""),
});
}
save() {
console.log(this.nestedForm.value);
}
嵌套-form.component.html
<form [formGroup]="nestedForm" >
<mat-select class="field-select" [(ngModel)]="_selectedMotor"
(selectionChange)="selectMotor($event.value)" formControlName="motor">
<mat-option class="select-option" *ngFor="let item of motors" [value]="item">
item</b>
</mat-option>
</mat-select>
<app-properties formControlName="properties" [type] = "_selectedMotor"></app-properties>
...
</form>
properties.component.ts
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
}
]
})
export class PropertiesComponent implements OnInit, ControlValueAccessor {
@Input() type: string = null;
public propertiesForm: FormGroup;
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
ngOnChanges(changes): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
switch (this.type) {
case "OLD":
return new FormGroup(
{
tilt: new FormControl(0, [Validators.required]),
fine_tilt: new FormControl(0, [Validators.required]),
rotate: new FormControl(0, [Validators.required]),
fine_rotate: new FormControl(0, [Validators.required])
});
case "VINTEN":
return new FormGroup(
{
vinten_tilt: new FormControl(0, [Validators.required]),
vinten_rotate: new FormControl(0, [Validators.required])
});
}
}
public onTouched: () => void = () => { };
writeValue(val: any): void {
val && this.propertiesForm.setValue(val, { emitEvent: false });
}
registerOnChange(fn: any): void {
this.propertiesForm.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.propertiesForm.disable() : this.propertiesForm.enable();
}
validate(c: AbstractControl): ValidationErrors | null {
return this.propertiesForm.valid ? null : { invalidForm: { valid: false, message: "propertiesForm fields are invalid" } };
}
...
}
properties.component.html
<form *ngIf="type" class="col" [formGroup]="propertiesForm" [ngSwitch]="type">
<div *ngSwitchCase="OLD">
<input type="number" class="field-input" id="rotate" formControlName="rotate" />
<input type="number" class="field-input" id="fine_rotate" formControlName="fine_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="tilt" />
<input type="number" class="field-input" id="fine_tilt" formControlName="fine_tilt" />
</div>
<div *ngSwitchCase="VINTEN">
<input type="number" class="field-input" id="rotate" formControlName="vinten_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="vinten_tilt" />
</div>
</form>
结果:
假设加载表单时 OLD 是默认值。
我输入了所有字段并单击 OLD 的“保存”按钮,按预期正确收集了表单值。
但是,当我从 OLD 切换到 VINTEN 时,表单 UI 已更新,但保存按钮的 valid/invalid 状态不再起作用,单击保存按钮后,表单值是仍然是旧属性值,而不是 VINTEN 属性值。
我是不是遗漏了什么或做错了什么?任何建议表示赞赏。
要通知父控件有关内部更改,您必须收听 propertiesForm.valueChanges()
。
然后打电话给 fn
你被 registerOnChange(fn: any): void
给了
onModelChange = () => {}
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
this.propertiesForm.valueChanges().subscribe(value => this.onModelChange(value))
}
registerOnChange(fn: any): void {
this.onModelChange = fn;
}
谢谢尤里,
虽然他的回答没有直接完整的解决我的问题,但对我最终解决我的问题起到了主要的引导作用。留在这里以供参考:
export class PropertiesComponent implements OnInit, ControlValueAccessor {
@Input() type: string = null;
subscriptions: Subscription[] = [];
...
ngOnChanges(changes): void {
this.subscriptions.forEach(s => s.unsubscribe());
this.propertiesForm = this.buildFormGroup(this.formBuilder);
this.subscriptions.push(
this.propertiesForm.valueChanges.subscribe(value => {
this.onChange(value);
this.onTouched();
})
);
}
...
onChange: any = () => {};
registerOnChange(fn: any): void {
this.onChange = fn;
}
}
我正在处理如下表格
我的代码
嵌套-form.component.ts
public nestedForm: FormGroup;
constructor(
private formBuilder: FormBuilder) {
this.calibrationForm = this.buildFormGroup(formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
return new FormGroup({
motor: new FormControl('', Validators.required),
properties: new FormControl(""),
});
}
save() {
console.log(this.nestedForm.value);
}
嵌套-form.component.html
<form [formGroup]="nestedForm" >
<mat-select class="field-select" [(ngModel)]="_selectedMotor"
(selectionChange)="selectMotor($event.value)" formControlName="motor">
<mat-option class="select-option" *ngFor="let item of motors" [value]="item">
item</b>
</mat-option>
</mat-select>
<app-properties formControlName="properties" [type] = "_selectedMotor"></app-properties>
...
</form>
properties.component.ts
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
}
]
})
export class PropertiesComponent implements OnInit, ControlValueAccessor {
@Input() type: string = null;
public propertiesForm: FormGroup;
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
ngOnChanges(changes): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
switch (this.type) {
case "OLD":
return new FormGroup(
{
tilt: new FormControl(0, [Validators.required]),
fine_tilt: new FormControl(0, [Validators.required]),
rotate: new FormControl(0, [Validators.required]),
fine_rotate: new FormControl(0, [Validators.required])
});
case "VINTEN":
return new FormGroup(
{
vinten_tilt: new FormControl(0, [Validators.required]),
vinten_rotate: new FormControl(0, [Validators.required])
});
}
}
public onTouched: () => void = () => { };
writeValue(val: any): void {
val && this.propertiesForm.setValue(val, { emitEvent: false });
}
registerOnChange(fn: any): void {
this.propertiesForm.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.propertiesForm.disable() : this.propertiesForm.enable();
}
validate(c: AbstractControl): ValidationErrors | null {
return this.propertiesForm.valid ? null : { invalidForm: { valid: false, message: "propertiesForm fields are invalid" } };
}
...
}
properties.component.html
<form *ngIf="type" class="col" [formGroup]="propertiesForm" [ngSwitch]="type">
<div *ngSwitchCase="OLD">
<input type="number" class="field-input" id="rotate" formControlName="rotate" />
<input type="number" class="field-input" id="fine_rotate" formControlName="fine_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="tilt" />
<input type="number" class="field-input" id="fine_tilt" formControlName="fine_tilt" />
</div>
<div *ngSwitchCase="VINTEN">
<input type="number" class="field-input" id="rotate" formControlName="vinten_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="vinten_tilt" />
</div>
</form>
结果:
假设加载表单时 OLD 是默认值。
我输入了所有字段并单击 OLD 的“保存”按钮,按预期正确收集了表单值。
但是,当我从 OLD 切换到 VINTEN 时,表单 UI 已更新,但保存按钮的 valid/invalid 状态不再起作用,单击保存按钮后,表单值是仍然是旧属性值,而不是 VINTEN 属性值。
我是不是遗漏了什么或做错了什么?任何建议表示赞赏。
要通知父控件有关内部更改,您必须收听 propertiesForm.valueChanges()
。
然后打电话给 fn
你被 registerOnChange(fn: any): void
onModelChange = () => {}
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
this.propertiesForm.valueChanges().subscribe(value => this.onModelChange(value))
}
registerOnChange(fn: any): void {
this.onModelChange = fn;
}
谢谢尤里,
虽然他的回答没有直接完整的解决我的问题,但对我最终解决我的问题起到了主要的引导作用。留在这里以供参考:
export class PropertiesComponent implements OnInit, ControlValueAccessor {
@Input() type: string = null;
subscriptions: Subscription[] = [];
...
ngOnChanges(changes): void {
this.subscriptions.forEach(s => s.unsubscribe());
this.propertiesForm = this.buildFormGroup(this.formBuilder);
this.subscriptions.push(
this.propertiesForm.valueChanges.subscribe(value => {
this.onChange(value);
this.onTouched();
})
);
}
...
onChange: any = () => {};
registerOnChange(fn: any): void {
this.onChange = fn;
}
}