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;
}

Angular forms

谢谢尤里,

虽然他的回答没有直接完整的解决我的问题,但对我最终解决我的问题起到了主要的引导作用。留在这里以供参考:

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;
   }
}