【问题标题】:How can I bind a form to a model in Angular 6 using reactive forms?如何使用响应式表单将表单绑定到 Angular 6 中的模型?
【发布时间】:2018-12-22 21:02:40
【问题描述】:

在 Angular 6 之前,我使用 [(ngModel)] 将表单字段直接绑定到模型。现在已弃用(不能与反应式表单一起使用),我不确定如何使用表单值更新我的模型。我可以使用 form.getRawValue() 但这需要我用新的 rawValue 替换我当前的模型 - 这并不有利,因为我的主模型比本地表单模型更大并且具有更多字段。

有什么想法吗?

【问题讨论】:

标签: angular typescript angular-reactive-forms


【解决方案1】:

不要使用[(ngModel)]!反应形式要好得多。他们使手动 ngModel 绑定过时,并且他们有一些非常可爱的内置功能,我将在这个答案中介绍其中的几个。

绑定到表单

如果您要绑定到文本输入等表单控件,请使用以下模板语法:

<ng-container [formGroup]="this.myFormGroup">
    <input type="text" formControlName="field1">
    <input type="text" formControlName="field2">
    <ng-container formGroupName="subgroupName">
        <input type="text" formControlName="subfield2">
    </ng-container>
    <input type="text" formControlName="myRequiredField">
</ng-container>

(field1field2subgroupNamesubfield2myRequiredField 都是与您的表单部分相对应的任意控件和控件组名称,请参阅下面的创建 @987654329 @对象。)

FormGroup 模型的只读数据绑定在您的模板中的访问方式略有不同:

{{ this.myFormGroup.get('field1').value }}
{{ this.myFormGroup.get('subgroupName.subfield2').value }}
<!-- Hint: use an array if you have field names that contain "." -->
{{ this.myFormGroup.get(['subgroupName', 'subfield2']).value }}

创建FormGroup

在您的组件类中,在constructor()(这应该在模板呈现之前)中,使用以下语法构建一个表单组来与这个表单对话:

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

...
    public readonly myFormGroup: FormGroup;
...
    constructor(private readonly formBuilder: FormBuilder) {
        this.myFormGroup = this.formBuilder.group({
            field1: [],
            field2: [],
            subgroupName: this.formBuilder.group({
                subfield2: [],
            }),
            myRequiredField: ['', Validators.required],
        });
        this.retrieveData();
    }

用数据填写表单

如果您的组件需要在加载时从服务中检索数据,您必须确保它在构建表单后开始传输,然后使用patchValue() 将数据从您的对象放入FormGroup

    private retrieveData(): void {
        this.dataService.getData()
            .subscribe((res: SomeDataStructure) => {
                // Assuming res has a structure like:
                // res = {
                //     field1: "some-string",
                //     field2: "other-string",
                //     subgroupName: {
                //         subfield2: "another-string"
                //     },
                // }
                // Values in res that don't line up to the form structure
                // are discarded. You can also pass in your own object you
                // construct ad-hoc.
                this.myFormGroup.patchValue(res);
            });
    }

从表单中获取数据

现在,假设您的用户单击提交,现在您需要从表单中取回数据,并通过服务将其POST 返回到您的 API。只需使用getRawValue

public onClickSubmit(): void {
    if (this.myFormGroup.invalid) {
        // stop here if it's invalid
        alert('Invalid input');
        return;
    }
    this.myDataService.submitUpdate(this.myFormGroup.getRawValue())
        .subscribe((): void => {
            alert('Saved!');
        });
}

所有这些技术都消除了对任何[(ngModel)] 绑定的需要,因为表单在FormGroup 对象中维护了自己的内部模型。

【讨论】:

  • "this.myFormGroup.patchValue(res);"这对我来说是关键,文档很烂-我试图弄清楚如何从从服务调用收到的对象中加载编辑表单(我是 Angular 的新手)-谢谢!
  • 响应式表单在哪里绑定到模型对象?
  • 通过[formGroup]="this.myFormGroup" 指令完成。这会将其绑定到存储在 FormGroup 对象中的模型,稍后您可以通过调用 this.myFormGroup.getRawValue 等来获取该模型。
  • 查看具体示例非常有帮助。很难找到!
  • 这个答案帮助我更好地理解了表单生成器。非常感谢。
【解决方案2】:

正如Angular Documentation 中更全面地解释的那样,对于反应式表单,您不会将表单直接绑定到您的 模型。相反,您使用 FormBuilder 来构建一个 FormGroup 对象(本质上是“表单”),它将维护它自己的模型。 在构建过程中,您有机会在表单中设置初始值,这通常是在您的模型中进行的。

然后,您将模板中的表单控件绑定到表单的模型。 用户与表单控件的交互会更新表单的模型。

当您准备好对表单数据进行处理时(例如“提交”表单), 您可以通过使用 FormGroup 的 value 属性或其 getRawValue() 方法从表单字段中获取值 - 这两种行为不同,请参阅文档了解详细信息。

从表单中获取值后,如果您愿意,可以使用表单中的值更新您的模型。

【讨论】:

  • > You then bind form controls in your template to the form's model. User interaction with the form controls updates the form's model. 您是否建议在模板中使用[(ngModel)]
  • 没有。您在组件中创建一个 FormGroup,并将 HTML 输入绑定到具有“formControlName”属性的那个。同样,请参阅文档,例如angular.io/api/forms/FormControlName
  • 如何将响应式表单绑定到模型的 id?使用隐藏表单控件?
  • 你没有。这就是重点。表格有它自己的模型。它不受你的约束。您可以获取表单的“值”属性,并使用它做您想做的事情,包括更新您的模型,但您的模型明确地未绑定到表单。
  • 您可以一直说查看文档,但文档并没有告诉您如何执行此操作。他们没有向您展示如何将您的反应形式绑定到模型对象。他们展示了如何将一些数据记录到控制台,但找不到任何示例来说明如何将一种方式和两种方式绑定到支持模型。我从警告消息中的链接开始,然后你的链接是一个类似的故事。没有绑定到模型的实际解释或示例。这就是 ngModel 所做的......它与反应式表单绑定我有带有数据的表单控件,但没有数据进入模型。
【解决方案3】:

您可以订阅表单组中的更改并使用它来更新您的模型。但这并不安全。因为您必须确保您的表单字段与模型字段匹配或添加验证模型中的字段是否存在。

bindModelToForm(model: any, form: FormGroup) {
    const keys = Object.keys(form.controls);
    keys.forEach(key => {
        form.controls[key].valueChanges.subscribe(
            (newValue) => {
                model[key] = newValue;
            }
        )
    });
}

我的服务的完整代码:
referenceFields - 表示如果您有像 student: { name, group } 这样的复杂字段,其中 group 是一个引用模型,您需要只能从此模型中获取 id:

import { Injectable } from '@angular/core';
import { FormGroup } from "@angular/forms";

@Injectable({
    providedIn: 'root'
})
export class FormService {

    constructor() {
    }

    bindModelToForm(model: any, form: FormGroup, referenceFields: string[] = []) {
        if (!this.checkFieldsMatching(model, form)) {
            throw new Error('FormService -> bindModelToForm: Model and Form fields is not matching');
        }
        this.initForm(model, form);
        const formKeys = Object.keys(form.controls);
        formKeys.forEach(key => {
            if (referenceFields.includes(key)) {
                form.controls[key].valueChanges.subscribe(
                    (newValue) => {
                        model[key] = newValue.id;
                    }
                )
            } else {
                form.controls[key].valueChanges.subscribe(
                    (newValue) => {
                        model[key] = newValue;
                    }
                )
            }
        });
    }

    private initForm(model: any, form: FormGroup) {
        const keys = Object.keys(form.controls);
        keys.forEach(key => {
            form.controls[key].setValue(model[key]);
        });
    }

    private checkFieldsMatching(model: any, form: FormGroup): boolean {
        const formKeys = Object.keys(form.controls);
        const modelKeys = Object.keys(model);
        formKeys.forEach(formKey => {
            if (!modelKeys.includes(formKey)) {
                return false;
            }
        });
        return true;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-29
    相关资源
    最近更新 更多