动态表单库

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允许您定义最多三个方法,这些方法将在表单构建过程中按此顺序调用:

  1. prePopulate
  2. onPopulate
  3. 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,
},
},
];

formly-form 动态表单的更多相关文章

  1. 解析:使用easyui的form提交表单,在IE下出现类似附件下载时提示是否保存的现象

    之前开发时遇到的一个问题,使用easyui的form提交表单,在Chrome下时没问题的,但是在IE下出现类似附件下载时提示是否保存的现象. 这里记录一下如何解决的.其实这个现象不光是easyui的f ...

  2. Vue+Element的动态表单,动态表格(后端发送配置,前端动态生成)

    Vue+Element的动态表单,动态表格(后端发送配置,前端动态生成) 动态表单生成 ElementUI官网引导 Element表单生成 Element动态增减表单,在线代码 关键配置 templa ...

  3. angularjs 动态表单, 原生事件中调用angular方法

    1. 原生事件中调用angular方法, 比如 input的onChange事件想调用angular里面定义的方法 - onChange="angular.element(this).sco ...

  4. vue 开发系列(八) 动态表单开发

    概要 动态表单指的是我们的表单不是通过vue 组件一个个编写的,我们的表单是根据后端生成的vue模板,在前端通过vue构建出来的.主要的思路是,在后端生成vue的模板,前端通过ajax的方式加载后端的 ...

  5. 如何在.Net Core MVC中为动态表单开启客户端验证

    非Core中的请参照: MVC的验证 jquery.validate.unobtrusive mvc验证jquery.unobtrusive-ajax 参照向动态表单增加验证 页面引入相关JS: &l ...

  6. Angular动态表单生成(八)

    动态表单生成之拖拽生成表单(下) 我们的动态表单,最终要实现的效果与Form.io的在线生成表单的效果类似,可以参考它的demo地址:https://codepen.io/travist/full/x ...

  7. Angular动态表单生成(二)

    ng-dynamic-forms源码分析 在两个开源项目中,ng-dynamic-forms的源码相较于form.io,比较简单,所以我还勉强能看懂,下面就我自己的理解进行简单分析,若有不对的地方,请 ...

  8. Angular动态表单生成(一)

    好久不写博客了,手都生了,趁着最近老大让我研究动态表单生成的时机,撸一发博客~~ 开源项目比较 老大丢给我了两个比较不错的开源的动态表单生成工具,这两个项目在github上的star数量基本持平: h ...

  9. 使用easyui的form提交表单,在IE下出现类似附件下载时提示是否保存的现象

    之前开发时遇到的一个问题,使用easyui的form提交表单,在Chrome下时没问题的,但是在IE下出现类似附件下载时提示是否保存的现象. 这里记录一下如何解决的.其实这个现象不光是easyui的f ...

  10. jquery 实现动态表单设计

    只是实现了前台页面的动态表单的设计,并未实现后台绑定数据到数据库等功能.技术使用到的为jquery和bootstrap.俗话说有图有真相,先说下具体效果如下: 点击添加一个面板容器: 容器添加成功: ...

随机推荐

  1. TCP和UDP有啥区别?

    TCP全称: Transmission Control Protocol中文名: 传输控制协议解释: 是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793定义.用途:TCP ...

  2. PAT (Basic Level) Practice 1010 一元多项式求导 分数 25

    设计函数求一元多项式的导数.(注:xn(n为整数)的一阶导数为nxn−1.) 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过 1000 的整数).数字间以空格分隔. 输出格式: ...

  3. 玖章算术受邀参加红杉Talk「创新的复利」科技专场,共同探讨云计算的前世今生

    9月2日,本周五14:00 「创新的复利」 Sequoia Talk系列论坛,首期直播盛大启动.在第一期科技专场,4位红杉中国资深投资人.8位创新创业者将带我们深入工业软件.机器人.云计算等领域,围绕 ...

  4. 齐博X1-栏目的调用3

    本节继续说明栏目的调用之同级别栏目 同级栏目的调用  fun('sort@brother',$fid,'cms') 这个函数用的比较多,特别是栏目页,在一个页面会把一个父级栏目下的子级栏目全部列出来, ...

  5. Educational Codeforces Round 122 (Rated for Div. 2)/codeforces1633

    CodeForces1633 Div. 7 解析: 题目大意 给定 \(t\) 组数据.每组数据给定一个数 \(n\)(\(10\le n\le 999\)). 每次操作可以修改 \(n\) 任意一位 ...

  6. 记一次 .NET 某医疗器械 程序崩溃分析

    一:背景 1.讲故事 前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用 AEDebug 在程序崩溃的时候自动抽一 ...

  7. 学习Java AES加解密字符串和文件方法,然后写个简单工具类

    Reference Core Java Volume Ⅱ 10th Edition 1 对称加密 "Java密码扩展"包含了一个Cipher,它是所有密码算法的超类.通过getIn ...

  8. 优雅处理Golang中的异常

    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java.Python等语言不同的是,Go中并没有try...catch...这样的语句块,我们知道在Java中使用try...catch.. ...

  9. 基于 Docker 构建轻量级 CI 系统:Gitea 与 Woodpecker CI 集成

    WoodpeckerCI 是一个由社区维护的 DroneCI 分支,使用 Apache License 2.0 许可证发布.社区版进一步扩展了 pipeline 的功能特性.支持对文件路径设置 pip ...

  10. 记录因Sharding Jdbc批量操作引发的一次fullGC

    周五晚上告警群突然收到了一条告警消息,点开一看,应用 fullGC 了. 于是赶紧联系运维下载堆内存快照,进行分析. 内存分析 使用 MemoryAnalyzer 打开堆文件 mat 下载地址:htt ...