Angular表单:复杂嵌套响应式表单和根组件验证的最佳实践

10

我已经苦恼了几天,想找一个适合我的用例的良好模式。我有一个非常复杂的响应式表单,充满了嵌套组件,一些字段是必填的,一些字段可能根据特定条件出现等等... 这给代码维护带来了很大的问题。我迄今为止使用的方法是将formControl传递给子组件并从那里进行填充,但随着表单大小的增加,调试变得非常困难。对于该表单来说,必须在整个表单提交时对所有嵌套字段进行验证,并标记任何未插入但必需的字段为markAsTouched。我已经探索了两种方法,但这两种方法似乎都没有什么帮助:

  • 控制值访问器(Control Value Accessor):这个概念很完美,逻辑在子组件间分割得非常好,而不需要父级关注它,但缺点是子控件没有暴露,我无法标记所有子表单以显示相应的错误。
  • 控制容器(Control Container):这种方法似乎需要在父级别定义所有控件,但是由于表单非常大,这似乎真的会产生反作用,而且并不能真正解决我的问题。

我想知道是否有人有这样的表单经验,能够提供一些指导,告诉我在这种情况下最佳实践是什么。

我已经用Control Container制作了一个非常简单的stackblitz示例,只有一个子组件,但不幸的是我无法使其运行 https://stackblitz.com/edit/angular-ivy-axbgr5


3
一些关于IT主题的阅读材料 https://link.medium.com/TUIPSUU6m7 The Best Way to build reactive sub-forms with Angular by Thomas Trajan - Alex Biro
谢谢@AlexBiro,我会阅读。 - user3353167
2个回答

14

在 Angular 中,复杂的表单可能会带来很大的麻烦。从我的经验来看,最好的方法是创建一个有状态的父组件和许多无状态的子组件。

父组件应该专门用于特定的表单。子组件可以在许多地方重复使用。

父组件规则:

  • 有状态
  • 创建和保存表单定义
  • 在每次表单更改时发出表单状态(值、有效性、原始状态)
  • 保存自定义验证逻辑

子组件规则:

  • 无状态
  • 从父组件接收表单部分(嵌套的 FormGroups
  • 没有自定义验证逻辑

在上述情况中,子组件是“可重用视图”,没有任何验证逻辑。它将始终来自父组件。

如何将表单部分传递给子组件?

您可以通过以下方式传递嵌套的 FormGroups

ControlValueAccessor

我认为使用 ControlValueAccessor 创建表单部分不是一个好主意。验证逻辑被封装在其中。它适用于创建一些真正困难的部分,比如“颜色选择器”,但不适用于只有几个字段的“客户地址”。

将业务逻辑移出组件

我还尝试了以下简单的代码来将业务逻辑移出组件:

constructor(public businessLogic: VendorBusinessLogicService) { }

ngOnInit() {
    this.form = this.businessLogic.createForm(this.initialValue);

    this.subscription = this.form.valueChanges.subscribe(value => {
        this.businessLogic.applyBusinessLogic(value);
        this.emitFormState();
    });

    this.emitFormState();
}

当然,要求在服务内部保存表单的引用。老实说,我看不出这样做有什么好处。对于业务逻辑来说,这个服务看起来很可怕。(https://github.com/p-programowanie/angular-forms/tree/forms-separated-business-logic


1
我真的很喜欢这个答案。 - WangJi
1
更多指出为什么不使用ControlValueAccessor: 如果您有复杂的表单(例如分散在几个选项卡视图中),并且希望获取该完整表单的验证状态,但又不想强制用户再次浏览所有选项卡,则无法封装子组件中的验证状态,因为它只会在子组件被渲染时计算。 - elegon

1

我发现最好创建一个专门的服务来处理所有表单逻辑和结构。所有验证、依赖项、订阅、填充表单组和数组都在那里。这样,测试表单部分并重复使用它们就非常容易了。


我曾经考虑过这个,但是那样的话我仍然需要处理一个非常非常庞大的表单,并且逻辑非常复杂,我更喜欢在它们各自的组件中处理。 - user3353167
1
我们曾经尝试过。我们的团队发现它很难支持。老实说,我在两种方法中都看到了优缺点 (: - IAfanasov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接