动态表单库

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. 【原创】推流录屏软件OBS使用教程--录屏

    之前有录屏需要,写了一篇关于ffmpeg录屏的文章,反响还不错,但是直接用ffmpeg门槛有些高,今天写一篇图形界面的录屏推流工具OBS的使用教程.这次先写OBS的录屏教程 下载安装 点击 OBS官网 ...

  2. 了解Pytorch|Get Started with PyTorch

    一个开源的机器学习框架,加速了从研究原型到生产部署的路径. !pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple import ...

  3. PTA2022 520钻石争霸赛题解

    7-1 520表白 不用说 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int max ...

  4. BGCN Rec:模型结构概述

    简单论述 BGCN将user-item interaction,user-bundle interaction和bundle-item affiliation 关联到统一的异构图中.以项目节点为桥梁, ...

  5. C言语语法总结(随时更新)

    一.gcc1. gcc xxx.c -o xxx #把原代码编译成可执行文件xxx2. gcc -c xxx.c #编译: 把原代码编译xxx.o后辍的目标文件3. gcc xxx.o -o xxx ...

  6. 【杂谈】2021-CSP退役记

    Part1:复赛前一周 感觉复赛来的好快...... 我还没 颓够 准备好就来了QAQ 根据模拟赛 爆零 的光辉事迹,这次复赛我特别慌,虽然但是还是不想复习 但无所谓了,复赛一下子就只剩一天了 Par ...

  7. 题解 SP10500 HAYBALE - Haybale stacking

    前言 想了好久树状数组啥的,后来想想写打个差分再说,结果写完一遍AC了-- 强烈安利 题意 一个由 \(n\) 个元素组成的序列,给出 \(k\) 个操作,每次将 \(a\sim b\) 加上 \(1 ...

  8. VB6查看桌面分辨率和工作区大小 2022.08.22 name.vt

    VB6查看桌面分辨率和工作区大小 2022.08.22 name.vt Form1 内代码如下: ' 2022年8月22日 15时15分 ' 作者:name.vt Private Sub cmdCle ...

  9. 如何解读Linux Kernel OOPS信息

    OOPS信息解读 root@firefly:~/mnt/module# insmod oops_module.ko [ 867.140514] Unable to handle kernel NULL ...

  10. SpringBoot问题集合

    Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as ...