簡體   English   中英

如何使用反應式表單將表單綁定到 Angular 6 中的模型?

[英]How can I bind a form to a model in Angular 6 using reactive forms?

在 angular 6 之前,我使用[(ngModel)]將表單字段直接綁定到模型。 現在已棄用(不能與反應式表單一起使用),我不確定如何使用表單值更新我的模型。 我可以使用form.getRawValue()但這需要我用新的 rawValue 替換我當前的模型 - 這不是有利的,因為我的主模型比本地表單模型更大並且有更多的字段。

有任何想法嗎?

不要使用[(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>

field1field2subgroupNamesubfield2 ,和myRequiredField都是任意的控制和控制組名稱相應於你的形式的部分,看到在創建時下面FormGroup對象)。

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對象中維護着自己的內部模型。

正如Angular Documentation 中更完整的解釋,使用反應式表單,您不會將表單直接綁定到您的模型。 相反,您使用 FormBuilder 來構建一個 FormGroup 對象(本質上是“表單”),該對象將維護它自己的模型。 在構建過程中,您有機會在表單中設置初始值,這通常會在您的模型中進行。

然后將模板中的表單控件綁定到表單的模型。 用戶與表單控件的交互會更新表單的模型。

當您准備好對表單數據執行某些操作時(例如“提交”表單),您可以使用 FormGroup 的 value 屬性或 getRawValue() 方法從表單字段中獲取值 - 這兩種行為不同,請參閱有關詳細信息的文檔。

一旦你從形式獲取值,如果你願意,你可以更新表單中的數值模型

您可以訂閱表單組中的更改並使用它來更新您的模型。 但這並不安全。 因為您必須確保您的表單字段與模型字段匹配或添加驗證模型中的字段存在。

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 }這樣的復雜字段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;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM