formly-form 動態表單

動態表單庫

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 protected]' };
  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,
      },
    },
  ];
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章