angular表单那些事

本文最后更新于:2022年8月19日 晚上

1 - 响应式表单 & 模板驱动表单

Augular在其中文官网上就有非常易懂的关于表单的介绍: https://angular.cn/guide/forms-overview

二者均基于angular表单的四大基础类:

  • FormControl 实例用于追踪单个表单控件的值和验证状态。
  • FormGroup 用于追踪一个表单控件组的值和状态。
  • FormArray 用于追踪表单控件数组的值和状态。
  • ControlValueAccessor 用于在 Angular 的 FormControl 实例和内置 DOM 元素之间创建一个桥梁。

区别和使用场景

简单总结二者的区别和适用场景:

  • 模板驱动表单: 直接使用双向绑定语法, 将html模板和ts组件的变量进行双向绑定, 隐式表单模型(不直接管理FormControl)
  • 响应式表单: 直接在组件类中定义表单模型, 显式表单模型(直接管理FormControl)

通常来说, 模版驱动表单用于简单的表单场景, 如用户登录, 简单的信息填写等

而响应式表单在处理复杂表单时更加容易, 并且更方便进行验证, 测试等操作

数据流

下面是Angular给出的视图和模型之间二者的数据流, 我觉得很直观, 这里直接贴一下

响应式表单的数据流

  1. 最终用户在输入框元素中键入了一个值,这里是 “Blue”。
  2. 这个输入框元素会发出一个带有最新值的 “input” 事件。
  3. 这个控件值访问器 ControlValueAccessor 会监听表单输入框元素上的事件,并立即把新值传给 FormControl 实例。
  4. FormControl 实例会通过 valueChanges 这个可观察对象发出这个新值。
  5. valueChanges 的任何一个订阅者都会收到这个新值。

模板驱动表单的数据流

  1. 最终用户在输入框元素中敲 “Blue”。
  2. 该输入框元素会发出一个 “input” 事件,带着值 “Blue”。
  3. 附着在该输入框上的控件值访问器会触发 FormControl 实例上的 setValue() 方法。
  4. FormControl 实例通过 valueChanges 这个可观察对象发出新值。
  5. valueChanges 的任何订阅者都会收到新值。
  6. 控件值访问器 ControlValueAccessory 还会调用 NgModel.viewToModelUpdate() 方法,它会发出一个 ngModelChange 事件。
  7. 由于该组件模板双向数据绑定到了 favoriteColor,组件中的 favoriteColor 属性就会修改为 ngModelChange 事件所发出的值(”Blue”)。

值得一提的是, 虽然从数据流方便看上去模板驱动相比响应式要复杂很多, 但实际上就开发者而言是透明的, 在使用模板驱动时, 开发者只需要使用ngModel进行变量的双向绑定, 然后就会神奇的发现在组件中对该变量的任何修改会即时地显示在模板视图上.

stackblitz

从这里以inputtext为例, 展示一下响应式表单和模板驱动表单的区别

模板驱动表单:

响应式表单:

2 - ngx formly

在引出formly之前, 先谈谈滥用模板驱动的表单会出现什么情况

模板驱动表单带来的问题

在复杂的表单中使用模板驱动, 就会出现如下的情况…

各种乱起八糟的变量无规则的堆叠在一个ts组件中, 下面的截图来源于实际项目, 这样的变量, 在一个不算复杂的业务中, 堆叠了整整200多行…

举一个很现实的例子:

一个selection类型的组件(下拉菜单), 其需要处理如下逻辑:

  • 设置options(下拉选项), options受之前用户填写的多个变量的约束
  • 用户选择每一个option时, 会影响之后的表单的options
  • 需要对该组件的hidden, disabled做条件判断
  • 需要为该组件设定特定的样式
  • options选项包含过滤方法, 可以根据用户输入手动过滤表单选项

为了满足上述需求, 在模板驱动下, 我们可能会在ts中定义下面这些变量:

1
2
3
4
5
6
7
options = [a, b, c];
selectedItem = a;
onOptionSelect = function();
shouldHidden = false;
disabled = false;
onFilter = function();
// ...

除此之外, 前端需要做的事情也相当多:

1
2
<my-dropdown name="xxx" class="xxx" id="xxx" [options]="options" [style]="{'width':'80px', ....}"
[(ngModel)]="selectedItem" [disabled]="disabled" (onSelect)="xx" (onFilter)="xxx" [hidden]="shouldHidden"></my-dropdown>

这样的组件一个还好, 如果一个比较复杂的业务, 需要上百个这样的组件呢?

很显然, 由于模板驱动的无结构性, 会使得一个组件非常乱, 难以维护, 常常会出现过两周看之前的代码看不懂的情况…

这时就可以考虑使用ngx-formly

简单介绍

ngx-formly是一个基于JSON和angular响应式表单的动态表单库, 可以通过定义一系列json配置来完成整个表单逻辑的设计

你可以从这里看一个最小示例, 结合官方文档就能很清楚的了解使用方法了

通过配置FormlyFieldConfig, 可以指定绑定数据模型的key值, 模式, 以及通过templateOptions指定各种配置项如placeholder

1
2
3
4
5
6
7
8
9
10
11
fields: FormlyFieldConfig[] = [
{
key: 'email',
type: 'input',
templateOptions: {
label: 'Email address',
placeholder: 'Enter email',
required: true,
}
}
];

UI库

Formly自身适配了多个主流的Angular UI库, 包括Bootstrap, Material, primeNG等, 直接安装对应的npm包即可

@ngx-formly/primeng

自定义模板

如果你觉得预提供的组件不够用, 可以轻松创建自定义的模板组件

示例: https://formly.dev/guide/custom-formly-field

自定义包装器

比如你要在input下显示错误信息, 前面显示label之类的

示例: https://formly.dev/guide/custom-formly-wrapper


angular表单那些事
https://blog.roccoshi.top/posts/3949/
作者
RoccoShi
发布于
2022年6月12日
许可协议