動態表單庫
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,
},
},
];