【问题标题】:Angular - Reactive Form: How to correctly get values of dynamical and reusable sub-formsAngular - 反应式表单:如何正确获取动态和可重用子表单的值
【发布时间】:2021-04-19 19:18:16
【问题描述】:

我正在制作如下表格

我的代码

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

nested-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 已更新,但保存按钮的有效/无效状态不再起作用,并且单击保存按钮后,表单值仍然是 OLD 属性值,而不是 VINTEN属性值。

我是否遗漏了什么或做错了什么?任何建议表示赞赏。

【问题讨论】:

  • 我没有在 ControlValueAccessor 接口的 Properties 组件实现中看到。拥有它后,您可以向父控件发出更改
  • 是的,我为 ControlValueAccessor 实现了所有必需的。我没有将它们包含在示例代码中,以保持代码简短,只是主要代码。
  • 仅供参考:为了实现 ControlValueAccessor,我关注了这篇文章:medium.com/angular-in-depth/…
  • 您能否将问题复制到 StackBlitz 应用程序或类似的应用程序中?

标签: angular typescript forms angular-reactive-forms


【解决方案1】:

要通知父控件内部更改,您必须收听 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

【讨论】:

  • 我实现了一个 ControlValueAccessor: registerOnChange(fn: any): void { this.propertiesForm.valueChanges.subscribe(fn); } 。是不是一样的逻辑?
  • 两种方式都有效。但是如果你想在其他地方调用onModelChange你需要存储参考
  • 谢谢,我找到了解决方案。我接受你的回答,具体问题请参考我的回答。
【解决方案2】:

谢谢尤里,

虽然他的回答并没有直接彻底地解决我的问题,但这是我最终解决问题的主要线索。留在这里以供参考:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-22
    • 2021-09-07
    • 2019-06-24
    • 2019-06-04
    • 1970-01-01
    相关资源
    最近更新 更多