请先阅读:

一、重要的配置项

1. expressionProperties

expressionProperties可以动态更改表单控件的其他属性

  fields: FormlyFieldConfig[] = [
    {
      key: 'disabledControl',
      type: 'checkbox',
      defaultValue: false,
      templateOptions: {
        label: '禁用',
      },
    },
    {
      key: 'requiredControl',
      type: 'checkbox',
      defaultValue: false,
      templateOptions: {
        label: '必填',
      },
    },
    {
      key: 'hiddenControl',
      type: 'checkbox',
      defaultValue: false,
      templateOptions: {
        label: '隐藏',
      },
    },
    {
      key: 'text',
      type: 'input',
      templateOptions: {
        label: '姓名',
      },
      expressionProperties: {
      	// 该表单控件templateOptions配置项disabled的值由表单控件disabledControl的值决定,从而控制是否禁用
        'templateOptions.disabled': 'model.disabledControl',
        // 该表单控件templateOptions配置项required的值由表单控件requiredControl的值决定,从而控制是否必填
        'templateOptions.required': 'model.requiredControl',
        // 该表单控件配置项hideExpression的值由表单控件requiredControl的值决定,从而控制是否隐藏,注意hideExpression可以接收的类型 boolean/string/function
        hideExpression: 'model.hiddenControl',
        // 或者配置项hide
        hide: 'model.hiddenControl',
      },
    },
  ];

2. modelOptions

modelOptions可配置表单控件的行为,如防抖,失去焦点时再更新model等等

  fields: FormlyFieldConfig[] = [
    {
      key: 'text',
      type: 'input',
      modelOptions: {
        debounce: {
          default: 2000,	// 防抖
        },
        // updateOn: 'submit',      // blur:失去焦点时更新,submit:提交时更新
      },
      templateOptions: {
        label: 'modelOptions配置',
      },
    },
  ];

3. options.resetModel()和options.updateInitialValue()

  <form nz-form [nzLayout]="'vertical'" [formGroup]="form" (ngSubmit)="onSubmit()">
    <formly-form
      [form]="form"
      [model]="model"
      [fields]="fields"
      [options]="options"
    >
    </formly-form>
    <button nz-button nzType="primary" type="submit">提交</button>
    <button nz-button nzType="danger" type="reset">重置</button>	<!-- 推荐使用type类型为reset的按钮来重置表单 -->
    <button nz-button nzType="primary" (click)="options.resetModel()">重置</button>	<!-- 重置表单,但是内部会再次调用onSubmit方法 -->
    <button nz-button nzType="primary" (click)="options.updateInitialValue()">更新初始化数据</button>	<!-- 更新初始化数据后,调用options.resetModel重置表单回到初始化数据 -->
  </form>

4. options.formState

一般用于初始化或控制表单状态、数值等

  options: FormlyFormOptions = {
    formState: {
      disabled: true,
      options: [
        { label: '蛋糕', value: 1 },
        { label: '水果', value: 2 },
        { label: '咖啡', value: 3 },
      ]
    },
  };

  fields: FormlyFieldConfig[] = [
    {
      key: 'text',
      type: 'input',
      templateOptions: {
        label: '姓名',
      },
      expressionProperties: {
        // 控件的禁用状态基于formState
        'templateOptions.disabled': 'formState.disabled',
      },
    },
    {
      key: 'food',
      type: 'select',
      templateOptions: {
        label: '食物',
      },
      expressionProperties: {
        // select控件的options基于formState的options
        'templateOptions.options': 'formState.options',
      },
    },
  ];

  // 控制this.options.formState以控制表单控件的状态
  toggleDisabled() {
    this.options.formState.disabled = !this.options.formState.disabled;
  }

二、验证

1.内置验证

templateOptions对象中可选配置:requiredminmaxminLengthmaxLengthpattern
validation对象配置相对应的验证消息message的内容

  fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      templateOptions: {
        label: '姓名',
        placeholder: '请输入姓名',
        required: true,	// 是否必填
        min: 1,			// 一般用于number输入框,最小数值不能小于min
        max: 1000,		// 一般用于number输入框,最大数值不能大于max
        minLength: 1,	// 限制最小长度
        maxLength: 10,  // 限制最大长度
        pattern: /\d/,  // 正则匹配
        rows: 5,		// 用于textarea表单控件
      },
      // 配置验证输出的message
      validation: {
        messages: {
          required: '该项必填',
          pattern: (error, field: FormlyFieldConfig) => `姓名"${field.formControl.value}" 不合法`,
          max: (error, field: FormlyFieldConfig) => `最大数值不能超过"${field.templateOptions.max}"`,
        },
      },
    },
  ];  

app.module配置FormlyModule实现全局配置内置验证的验证消息message

  const maxValidationMessage = (err, field: FormlyFieldConfig): string => {
    return `最大数值不能超过 ${field.templateOptions.max}`;
  };
  // app.module.ts	
  imports: [
    // ...各种模块
    FormlyModule.forRoot({
      // name属性的值对应内置验证模块,message配置验证消息
      validationMessages: [
        { name: 'required', message: '该项必填' },
        { name: 'max', message: maxValidationMessage },
      ],
    }),
  ],

2.自定义验证

1.同步验证validators

  fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      templateOptions: {
        label: '姓名',
        placeholder: '请输入姓名',
        required: true,
      },
      // 配置自定义同步验证器
      validators: {
      	// name一般对应配置的表单控件key的值,或者自定义
        name: {
          // expression写验证逻辑,返回true时表示验证通过,否则验证不通过
          expression: (formControl: FormControl) => !/\d/.test(formControl.value),
          // 验证不通过时的提示消息,接收string或function
          message: (error, field: FormlyFieldConfig) => `姓名"${field.formControl.value}"不合法!`
        }
      },
    },
  ];  

2.异步验证asyncValidators

  fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      templateOptions: {
        label: '姓名',
        placeholder: '请输入姓名',
        required: true,
      },
      // 失去焦点时触发验证,一般用于异步验证,以提高性能
      modelOptions: {
        updateOn: 'blur',
      },
      // 配置自定义异步验证器
      asyncValidators: {
        nameValidator: {
          // 需要返回promise对象,并resolve或reject一个boolean值,或者返回observable对象,比如of(true/flase)
          expression: c =>  new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve(!/\d/.test(c.value));
            }, 2000);
          }),
          message: (error, field: FormlyFieldConfig) => `姓名"${field.formControl.value}"不合法!`
        }
      },
    },
  ]; 

app.module配置FormlyModule实现全局配置(比如全局配置身份证号码验证identityNum

  // 身份证正则表达式
  const idcardReg = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/
  // 自定义验证器
  const identityNumValidator = (control: FormControl): ValidationErrors {
  return !control.value || idcardReg.test(control.value) ? null : { 'identityNum': true };
}
  // 自定义验证消息
  const identityNumValidatorMessage = (err, field: FormlyFieldConfig): string => {
    return `${field.formControl.value}”不是合法的身份证号码`;
  };
  // app.module.ts
  imports: [
  	// ...各种模块
    FormlyModule.forRoot({
      validators: [
        { name: 'identityNum', validation: identityNumValidator },
      ],
      validationMessages: [
        { name: 'identityNum', message: identityNumValidatorMessage },
      ],
    }),
  ],

三、常用的表单控件

1. select

单选与多选

  fields: FormlyFieldConfig[] = [
    {
      key: 'selectKey1',
      type: 'select',
      templateOptions: {
        label: '普通选择',
        options: [
          { label: '钢铁侠', value: 1001 },
          { label: '美国队长', value: 1002 },
          { label: '黑寡妇', value: 1003 },
          { label: '浩克', value: 1004 },
          { label: '女巫', value: 1005 },
        ],
      },
    },
    {
      key: 'selectKey2',
      type: 'select',
      templateOptions: {
        label: '普通选择(options是Observable对象)',
        options: of([
          { label: '钢铁侠', value: 1001 },
          { label: '美国队长', value: 1002 },
          { label: '黑寡妇', value: 1003 },
          { label: '浩克', value: 1004 },
          { label: '女巫', value: 1005 },
        ]),
      },
    },
    {
      key: 'selectKey3',
      type: 'select',
      templateOptions: {
        label: '分组选择',
        options: [
          { label: '钢铁侠', value: 1001, group: '男' },
          { label: '美国队长', value: 1002, group: '男' },
          { label: '黑寡妇', value: 1003, group: '女' },
          { label: '浩克', value: 1004, group: '男' },
          { label: '女巫', value: 1005, group: '女' },
        ],
      },
    },
    {
      key: 'selectKey4',
      type: 'select',
      templateOptions: {
        label: '配置属性的分组选择',
        options: [
          {name: '钢铁侠', id: 1001, gender: '男'},
          {name: '美国队长', id: 1002, gender: '男'},
          {name: '黑寡妇', id: 1003, gender: '女'},
          {name: '浩克', id: 1004, gender: '男'},
          {name: '女巫', id: 1005, gender: '女'},
        ],
        groupProp: 'gender',
        valueProp: 'id',
        labelProp: 'name',
      },
    },
    {
      key: 'selectKey5',
      type: 'select',
      templateOptions: {
        label: '多选',
        multiple: true,
        options: [
          { label: '钢铁侠', value: 1001 },
          { label: '美国队长', value: 1002 },
          { label: '黑寡妇', value: 1003 },
          { label: '浩克', value: 1004 },
          { label: '女巫', value: 1005 },
        ],
      },
    },
  ];

级联选择I(省市区)

fields: FormlyFieldConfig[] = [
    {
      key: 'province',
      type: 'select',
      templateOptions: {
        label: '省',
        options: [
          { name: '广东省', id: 1 },
          { name: '湖南省', id: 2 },
        ],
        valueProp: 'id',
        labelProp: 'name',
      },
    },
    {
      key: 'city',
      type: 'select',
      templateOptions: {
        label: '市',
        options: [],
        valueProp: 'id',
        labelProp: 'name',
      },
      hooks: {
        onInit: field => {
          const cityList = [
            { id: 1, name: '广州市', provinceId: 1 },
            { id: 2, name: '深圳市', provinceId: 1 },
            { id: 3, name: '株洲市', provinceId: 2 },
            { id: 4, name: '长沙市', provinceId: 2 },
            { id: 5, name: '汕头市', provinceId: 1 },
          ];
          const provinceControl = this.form.get('province');
          field.templateOptions.options = provinceControl.valueChanges.pipe(
            tap(() => field.formControl.setValue(null)),
            map(id => cityList.filter(city => city.provinceId === id)),
          );
        },
      },
    },
    {
      key: 'area',
      type: 'select',
      templateOptions: {
        label: '区',
        options: [],
        valueProp: 'id',
        labelProp: 'name',
      },
      hooks: {
        onInit: field => {
          const areaList = [
            { id: 1, name: '福田区', cityId: 2 },
            { id: 2, name: '天河区', cityId: 1 },
            { id: 3, name: '南山区', cityId: 2 },
            { id: 4, name: '潮阳区', cityId: 5 },
            { id: 5, name: '白云区', cityId: 1 },
            { id: 6, name: '芙蓉区', cityId: 4 },
            { id: 7, name: '荷塘区', cityId: 3 },
            { id: 8, name: '石峰区', cityId: 3 },
            { id: 9, name: '宝安区', cityId: 2 },
            { id: 10, name: '雨花区', cityId: 4 },
          ];
          const cityControl = this.form.get('city');
          field.templateOptions.options = cityControl.valueChanges.pipe(
            tap(() => field.formControl.setValue(null)),
            map(id => areaList.filter(area => area.cityId === id)),
          );
        },
      },
    },
  ];

级联选择II(省市区)

  options: FormlyFormOptions = {
    formState: {
      provinceList: [
        { name: '广东省', id: 1 },
        { name: '湖南省', id: 2 },
      ],
      cityList: [
        { id: 1, name: '广州市', provinceId: 1 },
        { id: 2, name: '深圳市', provinceId: 1 },
        { id: 3, name: '株洲市', provinceId: 2 },
        { id: 4, name: '长沙市', provinceId: 2 },
        { id: 5, name: '汕头市', provinceId: 1 },
      ],
      areaList: [
        { id: 1, name: '福田区', cityId: 2 },
        { id: 2, name: '天河区', cityId: 1 },
        { id: 3, name: '南山区', cityId: 2 },
        { id: 4, name: '潮阳区', cityId: 5 },
        { id: 5, name: '白云区', cityId: 1 },
        { id: 6, name: '芙蓉区', cityId: 4 },
        { id: 7, name: '荷塘区', cityId: 3 },
        { id: 8, name: '石峰区', cityId: 3 },
        { id: 9, name: '宝安区', cityId: 2 },
        { id: 10, name: '雨花区', cityId: 4 },
      ]
    }
  };
  fields: FormlyFieldConfig[] = [
    {
      key: 'province',
      type: 'select',
      templateOptions: {
        label: '省',
        options: [],
        valueProp: 'id',
        labelProp: 'name',
      },
      expressionProperties: {
        'templateOptions.options': 'formState.provinceList'
      },
    },
    {
      key: 'city',
      type: 'select',
      templateOptions: {
        label: '市',
        options: [],
        valueProp: 'id',
        labelProp: 'name',
      },
      expressionProperties: {
        'templateOptions.options': 'formState.cityList.filter(city => city.provinceId === model.province)',
        'model.city': `field.templateOptions.options.find(city => city.id === model.city) ? model.city : null`,
      },
    },
    {
      key: 'area',
      type: 'select',
      templateOptions: {
        label: '区',
        options: [],
        valueProp: 'id',
        labelProp: 'name',
      },
      expressionProperties: {
        'templateOptions.options': 'formState.areaList.filter(area => area.cityId === model.city)',
        'model.area': `field.templateOptions.options.find(area => area.id === model.area) ? model.area : null`,
      },
    },
  ];

参考文献: