动态表单库

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. jq修改多个css样式

    $("#xxx").css({"属性名称": "属性值", "属性名称": "属性值" });

  2. 二手商城集成jwt认证授权

    ------------恢复内容开始------------ 使用jwt进行认证授权的主要流程 参考博客(https://www.cnblogs.com/RayWang/p/9536524.html) ...

  3. map集合类型/实体类类型的参数

    map集合类型的参数 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合, 将这些数据放在map中 只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要 ...

  4. File常用的方法操作、在磁盘上创建File、获取指定目录下的所有文件、File文件的重命名、将数据写入File文件

    文章目录 1.基本介绍 2.构造方法 3.常用的方法 4.代码实例 4.1 创建文件和目录(目录不存在) 4.1.1 代码 4.1.2 测试结果 4.2 测试目录存在的情况.直接写绝对的路径名 4.2 ...

  5. 齐博x1小程序集群一个重要功能升级,可以根据圈子会员组显示不同的菜单。

    如下图所示,虽然之前圈子小程序可以自定义会员中心菜单,但是存在一个问题,就是所有会员,比如圈主与普通会员的菜单都将是一样的. 现在升级后,就可以设置不同的圈子会员组,拥有不同的菜单. 比如一个商家,店 ...

  6. 关于ASP.NET Core WebSocket实现集群的思考

    前言 提到WebSocket相信大家都听说过,它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议.在没有WebSocket之前只能通过浏览器到服务端的请求应答 ...

  7. Xmake v2.7.3 发布,包组件和 C++ 模块增量构建支持

    Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...

  8. 定位java程序中占用cpu最高的线程堆栈信息

    找出占用cpu最高的线程堆栈信息 在java编码中,有时会因为粗心导致cpu占用较高的情况,为了避免影响程序的正常运行,需要找到问题并解决.这里模拟一个cpu占用较高的场景,并尝试定位到代码行. 示例 ...

  9. 【云原生 · Docker】Docker虚拟化技术

    1.Docker入门简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化. 容器是完全使用沙箱 ...

  10. Axios +Vue + themeleay

    1.pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId& ...