动态表单库
https://github.com/ngx-formly/ngx-formly
安装
ng add @ngx-formly/schematics --ui-theme=ng-zorro-antd
@ngx-formly/ng-zorro-antd
选择UI
-
-
bootstrap
material
ng-zorro-antd
ionic
primeng
kendo
nativescript
-
会默认导入到Module
+ import { ReactiveFormsModule } from '@angular/forms'; + import { FormlyModule } from '@ngx-formly/core'; + import { FormlyBootstrapModule } from '@ngx-formly/bootstrap'; @NgModule({ imports: [ BrowserModule, + FormsModule, + ReactiveFormsModule, // ----------- FormlyNgZorroAntdModule, NzFormModule, // ------- + FormlyModule.forRoot(), + FormlyBootstrapModule ], ... })
formly-form
<formly-form [form]="form" [fields]="fields" [model]="model"></formly-form>
该<formly-form>
组件是表单的主要容器
fields
:用于构建表单的字段配置。form
:允许跟踪模型值和验证状态的表单实例。model
:表格要表示的模型
form = new FormGroup({}); model = { email: 'email@gmail.com' }; fields: FormlyFieldConfig[] = [ { key: 'email', type: 'input', props: { label: 'Email address', placeholder: 'Enter email', required: true, } } ];
Name | Type | Default | Required | Description |
---|---|---|---|---|
form | FormGroup or FormArray |
new FormGroup({}) |
no | 表单实例 |
fields | FormlyFieldConfig[] |
yes | 构建表单的字段配置 | |
model | any |
yes | 表单表示的模型 | |
options | FormlyFormOptions |
no | 表格的选项 |
(modelChange)
model 值发生变量的时候触发的事件
fields
Attribute | Type | Description |
---|---|---|
key | string |
model 的键 |
id | string |
请注意,id 如果未设置,则会生成。 |
name | string |
过于的表单name才有效 |
type | string |
自定义模板 |
className | string |
自定的class样式formly-field directive. |
props | object |
任何特定于模板的选项都放在此处 |
templateOptions | object |
任何特定于模板的选项都放在此处,props 进行改用(props权重高一些) |
template | string |
自定义html 内容, 而不是type |
defaultValue | any |
model 为设置,或者为undefined , 该模型的值被分配 |
hide | boolean |
是否隐藏字段 |
hideExpression | boolean /string /function |
有条件地隐藏该字段 |
expressions | boolean /string /function |
一个对象,其中键是要在主字段配置上设置的属性,值是用于分配该属性的表达式。 |
focus | boolean |
是否获取焦点. 默认为 false . 可以用 expressions 进行设置 |
wrappers | string[] |
自定义组件里面包装label , 可以设置样式 |
parsers | function[] |
每当模型更新(通常通过用户输入)时,作为管道执行的函数数组 |
fieldGroup | FormlyFieldConfig[] |
字段组, 让高级布局更简单, 对于通过模型关联的字段进行分组 |
fieldArray | FormlyFieldConfig |
|
fieldGroupClassName | string |
formly-group 组件的class |
validation | object |
校验messages 信息的显示 |
validators | any |
特定字段设置验证规则 |
asyncValidators | any |
异步验证的内容 |
formControl | AbstractControl |
该字段的FormControl。它为您提供更多控制,如运行验证器、计算状态和重置状态。 |
modelOptions | object |
控制模型更改的有用属性的对象: debounce , updateOn |
modelOptions?: { debounce?: { // 防抖的ms 值 default: number; }; // https://angular.io/api/forms/AbstractControl#updateOn 触发方式 updateOn?: 'change' | 'blur' | 'submit'; };
formState
配合option
进行状态通信
NgModule 声明中声明验证函数和消息
自定义校验
// 自定义报错信息 export function IpValidatorMessage(error: any, field: FormlyFieldConfig) { return `"${field.formControl.value}" is not a valid IP Address`; } // 报错规则 export function IpValidator(control: AbstractControl): ValidationErrors | null { return /(d{1,3}.){3}d{1,3}/.test(control.value) ? null : { 'ipTwo': true }; } ... @NgModule({ imports: [ ... FormlyModule.forRoot({ validationMessages: [ { name: 'ipTwo', message: IpValidatorMessage }, { name: 'required', message: 'This field is required' }, ], validators: [ { name: 'ipTwo', validation: IpValidator }, ], }), ] })
页面上使用
{ key: 'ip', type: 'input', props: { label: 'IP Address (using custom validation declared in ngModule)', required: true, }, validators: { validation: ['ipTwo'], }, // 异步校验器 asyncValidators: { validation: ['ipAsync'], }, },
字段声明校验函数
export function IpValidator(control: AbstractControl): ValidationErrors { return /(d{1,3}.){3}d{1,3}/.test(control.value) ? null : { 'ipTwo': true }; } { key: 'ip', type: 'input', props: { label: 'IP Address (using custom validation through `validators.validation` property)', required: true, }, validators: { validation: [IpValidator], }, // 异步函数 asyncValidators: { validation: [IpAsyncValidator], }, },
在字段定义中声明验证函数和消息
{ key: 'ip', type: 'input', props: { label: 'IP Address (using custom validation through `validators.expression` property)', description: 'custom validation message through `validators.expression` property', required: true, }, validators: { ip: { // 为true 为符合报错信息 expression: (c: AbstractControl) => /(d{1,3}.){3}d{1,3}/.test(c.value), message: (error: any, field: FormlyFieldConfig) => `"${field.formControl.value}" is not a valid IP Address`, }, }, asyncValidators: { ip: { expression: (c: AbstractControl) => return new Promise((resolve, reject) => { setTimeout(() => { resolve(/(d{1,3}.){3}d{1,3}/.test(c.value)); }, 1000); }), message: (error: any, field: FormlyFieldConfig) => `"${field.formControl.value}" is not a valid IP Address`, }, }, },
在 NgModule 声明中的表单类型和消息中声明验证函数
export function IpValidator(control: AbstractControl): boolean { return /(d{1,3}.){3}d{1,3}/.test(control.value); } @NgModule({ imports: [ ... FormlyModule.forRoot({ validationMessages: [ { name: 'ipTwo', message: 'This field is required' }, ], types: [ { name: 'ipTwo', extends: 'input', defaultOptions: { validators: { ip: IpValidator // 'ip' matches the ip validation message } }, }, }), ] })
形式表达式expression
{ key: 'text2', type: 'input', props: { label: 'Hey!', placeholder: 'This one is disabled if there is no text in the other input', }, expressions: { 'props.disabled': '!model.text', 'props.disabled': (field: FormlyFieldConfig) => { return !field.model.text; }, }, },
条件
{ key: 'iLikeTwix', type: 'checkbox', props: { label: 'I like twix', }, expressions: { hide: '!model.name', hide: (field: FormlyFieldConfig) => { return field.model?.city === "123"; }, }, }
点击
toggle(){ this.fields[0].hide = !this.fields[0].hide; }
formState 状态
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
form = new FormGroup({}); model: any = {}; options: FormlyFormOptions = { formState: { disabled: true, }, }; fields: FormlyFieldConfig[] = [ { key: 'text', type: 'input', props: { label: 'First Name', }, expressions: { // apply expressionProperty for disabled based on formState 'props.disabled': 'formState.disabled', }, }, ]; toggleDisabled() { this.options.formState.disabled = !this.options.formState.disabled; }
生命周期
hooks: { afterContentInit: () => {}, afterViewInit: () => {}, onInit: () => {}, onChanges: () => {}, onDestroy: () => {}, },
参数
export type FormlyHookFn = (field: FormlyFieldConfig) => void; export interface FormlyHookConfig { onInit?: FormlyHookFn; onChanges?: FormlyHookFn; afterContentInit?: FormlyHookFn; afterViewInit?: FormlyHookFn; onDestroy?: FormlyHookFn; }
例如
hooks:{ onInit(f: FormlyFieldConfigCache) { f.formControl = new FormControl(); } }, }
fieldChanges
值得监听(源码是对valueChanges
进行封装)
hooks: { onInit(f: FormlyFieldConfigCache) { f?.options?.fieldChanges?.subscribe(res => { console.log(res,'ressss'); }) } }, modelOptions: { debounce: {default: 3000},// 防抖 updateOn: 'change' }
自定义组件
import { Component } from '@angular/core'; import { FieldType, FieldTypeConfig } from '@ngx-formly/core'; @Component({ selector: 'formly-field-input', template: ` <input type="input" [formControl]="formControl" [formlyAttributes]="field"> `, }) export class InputFieldType extends FieldType<FieldTypeConfig> {}
ngModule 注册组件
@NgModule({ declarations: [InputFieldType], import { InputFieldType } from './intput-field.type'; @NgModule({ imports: [ FormlyModule.forRoot({ types: [ { name: 'inputOne', component: InputFieldType }, ], }), ], })
types 有两个属性
name:组件类型的名称。type您在字段的选项中使用它。 component:设置此类型时 Formly 应创建的组件。
页面使用
方式一, 直接把组件传递给字段使用
fields: FormlyFieldConfig[] = [ { key: 'firstname', type: InputFieldType, }, ];
方式二, 使用ngModule 注册的type
fields: FormlyFieldConfig[] = [ { key: 'firstname', type: 'inputOne', }, ];
自定义label 组件
创建一个代表扩展FieldWrapper
类的包装器的组件。
import { Component, ViewChild, ViewContainerRef } from '@angular/core'; import { FieldWrapper } from '@ngx-formly/core'; @Component({ selector: 'formly-wrapper-panel', template: ` <div class="card"> <h3 class="card-header">Its time to party</h3> <h3 class="card-header">{{ props.label }}</h3> <div class="card-body"> <ng-container #fieldComponent></ng-container> </div> <!-- 报错的信息 --> <ng-container *ngIf="showError"> <formly-validation-message [field]="field"></formly-validation-message> </ng-container> </div> `, }) export class PanelFieldWrapper extends FieldWrapper { }
使用
@NgModule({ declarations: [PanelFieldWrapper], imports: [ FormlyModule.forRoot({ wrappers: [ { name: 'panel', component: PanelFieldWrapper }, ], }), ], })
页面使用
方式一 直接传递组件
fields: FormlyFieldConfig[] = [ { key: 'address', wrappers: [PanelFieldWrapper], props: { label: 'Address' }, fieldGroup: [{ key: 'town', type: 'input', props: { required: true, type: 'text', label: 'Town', }, }], }, ];
方法二:将PanelFieldWrapper
别名(在 中定义FormlyModule.forRoot
)传递给字段配置。
fields: FormlyFieldConfig[] = [ { key: 'address', + wrappers: ['panel'], props: { label: 'Address' }, fieldGroup: [{ key: 'town', type: 'input', props: { required: true, type: 'text', label: 'Town', }, }], }, ];
在module组合使用
@NgModule({ imports: [ FormlyModule.forRoot({ types: [ { name: 'operator', component: OperatorComponent, //输入框组件 wrappers: ['form-field'] //label 组件 }, ], }), ],
自定义扩展接口属性
创建了一个扩展,它定义了一个默认标签(如果它FormlyFieldConfig
本身没有定义的话)
定义接口类
*default-label-extension.ts*
import { FormlyExtension } from '@ngx-formly/core'; export const defaultLabelExtension: FormlyExtension = { prePopulate(field): void { if (field.props?.label) { return; } field.props = { ...field.props, label: 'Default Label' } }, };
FormlyExtension
允许您定义最多三个方法,这些方法将在表单构建过程中按此顺序调用:
prePopulate
onPopulate
postPopulate
注册自定义扩展
@NgModule({ imports: [ FormlyModule.forRoot({ extensions: [ { name: 'default-label', extension: defaultLabelExtension, priority:1 // 优先级, 默认1 } ] }) ], })
lazyRender 延迟加载组件
默认为true
当设置为false, 渲染字段组件并使用CSS控制其可见性。
例如
constructor( private config: FormlyConfig, ) { this.config.extras.lazyRender = false; } fields: FormlyFieldConfig[] = [ { key: 'input', type: 'input', hide:true, // 设置为true,隐藏, 我们发现是通过css隐藏的 templateOptions: { label: 'Input', placeholder: 'Input placeholder', required: true, }, }, ]
renderFormlyFieldElement
是否呈现渲染每项中<form-field>
组件里面的dom
默认为true
// 我们发现里面的内容都被隐藏啦 this.config.extras.renderFormlyFieldElement = false;
我们发现form-field
组件的内容会提取到外层
异步修改值
{ key: 'input', type: 'input', props: { label: '测试1' }, expressions: { 'props.label':timer(2000).pipe( map(()=>'修改为2') ) }, }
修改一项的值
ngOnInit(): void { setTimeout(()=>{ this.fields[0]?.formControl?.setValue('aaaa'); console.log(this.model); },2000) }
key 支持括号/点的方式拿到属性
fields: FormlyFieldConfig[] = [{ key: 'name[0].age', type: 'textarea', className: 'flex-1', }] model = {name: [{age: 'sexName'}]};
下拉框
fields: FormlyFieldConfig[] = [ // 多选 { key: 'Select', type: 'select', props: { label: 'Select', placeholder: 'Placeholder', description: 'Description', required: true, options: [ { value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }, { value: 4, label: 'Option 4', disabled: true }, ], }, }, // 多选 { key: 'select_multi', type: 'select', props: { label: 'Select Multiple', placeholder: 'Placeholder', description: 'Description', required: true, multiple: true, selectAllOption: 'Select All', options: [ { value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }, { value: 4, label: 'Option 4', disabled: true }, ], }, }, ];
单选
fields: FormlyFieldConfig[] = [ { key: 'Radio', type: 'radio', props: { label: 'Radio', placeholder: 'Placeholder', description: 'Description', required: true, options: [ { value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }, { value: 4, label: 'Option 4', disabled: true }, ], }, }, ];
复选
fields: FormlyFieldConfig[] = [ { key: 'Checkbox', type: 'checkbox', props: { label: 'Accept terms', description: 'In order to proceed, please accept terms', pattern: 'true', required: true, }, validation: { messages: { pattern: 'Please accept the terms', }, }, }, ];
fields: FormlyFieldConfig[] = [ { key: 'Textarea', type: 'textarea', props: { label: 'Textarea', placeholder: 'Placeholder', description: 'Description', required: true, }, }, ];
fields: FormlyFieldConfig[] = [ { key: 'Input', type: 'input', props: { label: 'Input', placeholder: 'Placeholder', description: 'Description', required: true, }, }, ];