动态表单库

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. csv2ECharts,**一行命令查看数据趋势图 工具分享**

    csv2ECharts 一行命令查看数据趋势图! 联系:luomgf@163.com,欢迎交流提出建议 只有一个文件,基于shell,实现将CSV格式数据转化为数据图.运维中尝尝需要查看某个监控指标的 ...

  2. 洛谷P1725 琪露诺 (单调队列/堆优化DP)

    显然的DP题..... 对于位置i,它由i-r~i-l的位置转移过来,容易得到方程 dp[i]=dp[i]+max(dp[i−r],...,dp[i−l]). 第一种:n2的暴力,只能拿部分分. 1 ...

  3. struts.xml 中用OGNL表达式取不到中文文件名的原因

    在struts2中xml配置如下,以execl文件为例: <result name="success" type="stream">    < ...

  4. tensorflow-gpu版本安装及深度神经网络训练与cpu版本对比

    tensorflow1.0和tensorflow2.0的区别主要是1.0用的静态图 一般情况1.0已经足够,但是如果要进行深度神经网络的训练,当然还是tensorflow2.*-gpu比较快啦. 其中 ...

  5. java集合框架复习----(1)

    文章目录 1 .集合框架思维导图 一.什么是集合 二.collection接口 1 .集合框架思维导图 一.什么是集合 存放在java.util.*.是一个存放对象的容器. 存放的是对象的引用,不是对 ...

  6. golang单元测试一(简单函数测试)

    0.1.索引 https://blog.waterflow.link/articles/1663688140724 1.简介 单元测试是测试代码.组件和模块的单元函数.单元测试的目的是清除代码中的错误 ...

  7. vue3+element-plus+登录逻辑token+环境搭建

    vue3+element-plus+登录逻辑token环境搭建 安装脚手架工具 1 npm i @vue/cli@4.5.13 -g 验证是否安装成功 1 vue -V # 输出 @vue/cli 4 ...

  8. python(牛客)试题解析1 - 入门级

    导航: 一.NC103 反转字符串 二.NC141 判断是否为回文字符串 三.NC151 最大公约数 四.NC65 斐波那契数列 - - - - - - - - - - 分-割-线 - - - - - ...

  9. Python基础之模块:5、 第三方模块 requests模块 openpyxl模块

    目录 一.第三方模块的下载与使用 1.什么是第三方模块 2.如何安装第三方模块 方式一:pip工具 方式二:pycharm中下载 3.注意事项 1.报错并有警告信息 2.报错,提示关键字 3.报错,无 ...

  10. 无线adb连接

    使用USB数据线连接Android设备; 在dos命令行输入adb  tcpip  [xxxx]命令 (例如:adb tcpip 8888,xxx代表端口号); 断开USB数据线(数据线和设备断开连接 ...