【问题标题】:On Angular, is binding to FormGroup (instead to a 'real model') a bad practice?在 Angular 上,绑定到 FormGroup(而不是绑定到“真实模型”)是一种不好的做法吗?
【发布时间】:2019-03-06 09:25:27
【问题描述】:

我有一个具有以下结构的示例反应形式:

- id
- firstName
- lastName
- siblings
  - id
  - firstName
  - lastName

Siblings 是由子组件渲染的数组。我测试了两种绑定父/子的方法:

1) 使用表单组

<app-child *ngFor="let sibling of form.get('siblings').controls" [formGroup]="sibling"></app-child>

2) 使用“真实模型”

<app-child *ngFor="let sibling of model.siblings; let i = index;" [model]="sibling" (changed)=updateSiblingModel($event,i)>

两者都运行良好。我的问题是:一种方法被认为优于另一种方法吗?

响应式表单与真实模型(由this.form.value 获得)具有几乎 1:1 的关系,以及一些有趣的好处,例如级联验证器和事件。它工作得非常好,但我觉得我从子组件到外部组件中提取了“太多逻辑/结构”。

绑定到模型也可以正常工作,但我有更多的接线要做(例如(changed)=updateSiblingModel($event,i),但我觉得它更有凝聚力。

这是比较两种策略的完整示例:

https://stackblitz.com/edit/angular-form-binding

我对大型 Angular 项目没有太多经验,所以我也想知道是否有任何方法比其他方法更适合扩展。

谢谢

【问题讨论】:

    标签: angular architecture angular-reactive-forms


    【解决方案1】:

    我看到“表单模型”和“数据模型”之间的一个关键区别是“表单模型”只包含表单上实际存在的数据。

    如果您的数据模型具有 id 属性,则 id 很可能不会出现在您的表单中,因此它不会成为您的“表单模型”的一部分。对于可能的其他字段(例如“上次更新日期”或时间戳)也是如此。

    另外,如果后来有人决定对您的一些表单字段进行分组以进行额外验证,则可能会破坏您预期的“表单模型”。

    就我个人而言,我会一直使用数据模型,因为您可以更好地控制它。您可以更轻松地对其执行操作,例如排序或过滤。您可以更轻松地将其存储在本地存储中以供离线操作。

    另外,如果应用程序随着时间的推移变得越来越大,您可以迁移到更正式的状态管理库,例如 NgRx,它将与您的“数据模型”一起使用。

    【讨论】:

      【解决方案2】:

      我想你是在问Ng ModelReactive Forms 之间的绑定区别

      ng-model 更适合处理简单的绑定,而reactive formForm 操作提供了强大的支持,例如type(如布尔值、字符串、数组等)、structure(重用结构/形式组喜欢添加其他人的名字和姓氏),validation(自动验证输入和表单),default value(值更改取决于其他字段),disable 等。

      简而言之,reactive form 适用于复杂表单、动态表单(如字段值或验证更改取决于其他字段)和代码维护。

      【讨论】:

      • 我实际上是在询问反应式表单中的两种不同策略。
      【解决方案3】:

      我能够使用 FormGroup 绑定,但将逻辑/结构保留在每个孩子内部。我发现这个解决方案可以为我正在寻找的目标提供最佳平衡。

      免责声明:我并没有声称此解决方案的独创性,它是在互联网上研究和测试许多解决方案的结果。

      所以父组件使用 FormGroup 绑定到子组件:

      <div class="sibling" *ngFor="let sibling of getSiblings(); let i = index;">
          <app-child [form]="sibling" (deleted)="deleteSibling(i)"></app-child>
      </div>
      

      父级中的 getSiblings() 方法只是一个简单的助手:

      getSiblings(): AbstractControl[] {
        return (<FormArray>this.form.get('siblings')).controls;
      }
      

      关键区别在于兄弟FormGroups是如何在父级上创建的:

      addSibling() {
        (<FormArray>this.form.controls.siblings).push(ChildComponent.toFormGroup());
      }
      

      上面的addSibling() 使用来自子组件的静态方法来创建空白的FormGroup:

      static toFormGroup(model: any = {}) {
        return new FormGroup({
          id: new FormControl(model.id, Validators.required),
          firstName: new FormControl(model.firstName, Validators.required),
          lastName: new FormControl(model.lastName, Validators.required),
        });
      }
      

      如上所示,不仅可以创建空白 FormGroups。父级的 loadModel 方法也可以利用每个子逻辑来创建归档的 FormGroups。这里我们只有一种子类型,但我们可以有很多:

      loadModel(model: any) {
        this.form.patchValue(model);
      
        const formArray = this.form.get('siblings') as FormArray;
        while (formArray.length) {
          formArray.removeAt(0);
        }
        model.siblings.forEach(s => formArray.push(ChildComponent.toFormGroup(s)));
      }
      

      排序和过滤也不复杂。这里有多种策略,但作为一个简单的测试用例:

      orderChildren() {
        this.currentModel.siblings = this.currentModel.siblings.sort((a, b) => a.id - b.id);
        this.loadModel(this.currentModel);
      }
      

      通过这种方法,我可以使用响应式表单级联功能,如有效状态、原始/脏标志、更改事件等,同时将每个子级的逻辑/结构保持在其组件类中。举例来说,级联有效性可以在最终解决方案的实际操作中看到(红色边框)。

      由于绑定到 FormGroup 的主要问题是必须将子级的逻辑/结构移至父级,因此使用这种方法可以避免这种情况,因此我更愿意接受这不能被视为一种不好的做法.

      完整的例子可以在 StackBlitz 上看到:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-30
        • 2017-02-04
        • 1970-01-01
        • 2019-09-13
        • 1970-01-01
        • 1970-01-01
        • 2018-02-24
        • 2021-04-08
        相关资源
        最近更新 更多