【问题标题】:How to bind model to formbuilder using reactive forms in angular8如何使用 Angular 8 中的反应形式将模型绑定到表单构建器
【发布时间】:2020-05-19 02:43:45
【问题描述】:

我在我的应用程序中使用了响应式表单,并使用 formcontrolName 绑定到 html。但是现在我不需要在表单构建器中声明属性,而是需要调用一个模型并将该模型分配给表单构建器并在 html 中使用它。 我浏览了很多链接,但没有找到根据我的要求工作的任何地方。

HTML:

<div class="tab-pane fade h-100 active show" id="tab-user_1" role="tabpanel" aria-labelledby="user_1"
    *ngIf="userInfoForm" [formGroup]="userInfoForm">
    <div class="row">
      <div class="col-6">
        <div class="form-group">
          <label for="">Agent Code <span class="text-danger">*</span></label>
          <input type="text" formControlName="agentCode" class="form-control">
        </div>
      </div>
</div>

TS:

 this.userInfoForm = this.FB.group({
        Id: 0,
        agentCode: this.agentCode,
        userName: [''],
        firstName: [''],
        middleName: '',
        lastName: [''],
        department: [''],
      });
      this.userInfoForm.controls['agentCode'].disable();

model.ts:

export class UserFormDetails {
   Id: number;
   agentCode: number;
   userName: string;
   firstName: string;
   middleName: string;
   lastName: string;
   department: string;
}

DEMO

【问题讨论】:

    标签: angular angular-reactive-forms reactive


    【解决方案1】:

    基于 cmets,听起来您想要一个基于模型的动态表单......没有“开箱即用”的解决方案可以做到这一点。您可以编写自己的表单模型:

    interface MyFormModelField {
      initial?: {
        value: any,
        disabled: boolean
      },
      controlOptions?: AbstractControlOptions
      // anything you find useful really?
    }
    
    export type MyFormModel<T> = Record<keyof T, MyFormModelField>
    

    然后像这样实现它:

    const USER_FORM_MODEL: MyFormModel<UserFormDetails> = {
        Id: { },
        agentCode: {
            initial: {
              value: '',
              disabled: true
            },
            controlOptions: {
              validators: Validators.required
            }
        },
        userName: { },
        firstName: { },
        middleName: { },
        lastName: { },
        department: { },
    }
    

    那么您可以编写一个服务来基于表单模型的实例生成表单组...

    @Injectable({providedIn: 'root'})
    export class MyFormBuilder {
      constructor(private: fb) { }
    
      buildForm<T>(formModel: MyFormModel<T>) {
        const fg = Object.keys(formModel).reduce((grp, k) => {
          const fld = formModel[k];
          return Object.assign(grp, {
            [k]: [fld.initial || '', fld.controlOptions]
          })
        }, {});
        return this.fb.group(fg);
      }
    }
    

    现在,只要您对 UserFormDetails 界面进行更改,打字稿就会对您大喊大叫,用字段信息更新相应的表单模型......虽然更新不会从模型更新中“自动”流动,但您至少在您的表单中具有某种类型安全性。我个人会在这里停下来。

    下一部分并不完美,我也不喜欢它,但如果你真的希望下一步从类定义中或多或少地自动化,你可以编写一个函数来构建一个类的默认表单模型,如下所示:

    function buildDefaultFormModel<T>(model: new () => T) {
      return Object.keys(new model()).reduce((form, k) => {
          return Object.assign(form, {[k]: {}})
      }, {} as MyFormModel<T>)
    }
    

    这需要两件事,1,您构建表单的类必须为每个字段指定一个默认值,不包含没有默认值的字段。 (这引入了对具有默认值的字段的隐藏依赖,这就是我不喜欢这种方法的原因,但是,如果您编写的类具有没有默认值或构造函数分配的属性,您可以配置 ts 对您大喊大叫):

    class UserFormDetails {
       Id: number = 0;
       agentCode: number = 0;
       userName: string = '';
       firstName: string = '';
       middleName: string = '';
       lastName: string = '';
       department: string = '';
    }
    

    2、对象构造函数必须不带参数。但是如果你在这种情况下试图滥用它,TS 会骂你。

    函数可以这样使用:

    const USER_FORM_MODEL: MyFormModel<UserFormDetails> = {
      ...buildDefaultFormModel(UserFormDetails),
      ...{
        agentCode: {
          initial: {
            value: '',
            disabled: true
          },
          controlOptions: {
            validators: Validators.required
          }
        },
      }
    }
    

    这样您可以使用默认字段,也可以为特定字段提供覆盖。

    最后,您还可以利用此表单模型来构建模板,但这是一项更大的任务。

    这是所有实现的闪电战(我继续清理控制器代码):https://stackblitz.com/edit/github-ck3u8u-qtnasc?file=src%2Fapp%2Fuser-table%2Fuser-table.validators.ts

    【讨论】:

    • 感谢您的回复,它对我有帮助。如果我不清楚任何概念,我会回复你。再次感谢
    【解决方案2】:

    您必须使用 patchValue 将您的模型绑定到 FormGroup 表单控件中。

    this.userInfoForm = this.FB.group({
            Id: 0,
            agentCode: this.agentCode,
            userName: [''],
            firstName: [''],
            middleName: '',
            lastName: [''],
            department: [''],
          });
    

    这是正确的。 当您获得 UserFormDetails 对象时,您必须将其传递给您必须做的表单:

     this.userInfoForm.patchValue({
       'Id': UserFormDetails.id 
        ...
    })
    

    当您必须将表单控件值传递给对象时:

     this.UserFormDetail.id = this.userInfoForm.get('Id').value
    

    等等……

    【讨论】:

    • 感谢回复,但我不是在找这个,我想将模型绑定到 formGroup
    • 不清楚你在问什么..? “绑定模型”是什么意思?
    • 我相信他想做和我写的一样的事情,但没有patchValue,直接使用模型。
    • 嗨,这里我需要将模型值分配给formbuilder,所以如果以后有额外的字段,我可以将它添加到模型中,所以会在formBuilder中更新
    • 从来没有遇到过这种情况。此外,根据您的要求,如果在模型和 formBuilder 中添加了额外的属性,您可能还需要在 UI 上以输入字段的形式显示它。隐藏元素可能是一种情况,但也不会使用表单更新值,因此可以直接从 .ts 文件中的逻辑完成。
    【解决方案3】:

    我认为这可能是您正在寻找的解决方案:

    private initUserForm() {
       // mapping the object into the form model
       this.userInfoForm = this.FB.group({ ...this.userDetails });
    
       // if you want to add validators or disable buttons...
       this.userInfoForm.controls.agentCode.setValidators(Validators.required); // adding validations
       this.userInfoForm.controls.agentCode.disable();
       // console.log(this.userInfoForm.controls);
    }
    

    这种方式满足了您对“绑定”的需求,这不是真正的绑定,因为框架不允许这样的事情,但它允许您从对象模型/值映射以创建反应式表单模型。


    看看添加额外字段后,表单如何获取该控件及其值:

    【讨论】:

    • 这是如何工作的?使用 FormBuilder 的文档(angular.io/guide/… 这就是你的 this.FB,对吗?)期望 this.FB.group() 的对象参数中的每个值都有一个数组,初始字段值作为第一个值数组,以及同步和异步验证器作为第二和第三个元素。在这种情况下,我很惊讶 .group() 方法接受具有非数组字段的对象。而且我认为一旦 this.userDetails 模型上的一个字段是数组,这就会爆炸。
    猜你喜欢
    • 2018-12-22
    • 1970-01-01
    • 2019-03-02
    • 2019-05-02
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 2019-07-03
    • 2022-01-03
    相关资源
    最近更新 更多