se 编辑

简化表单HTML模板的高阶组件,并进一步优化了一些细节:

  • 更友好的表单校验状态

  • 自动化响应式布局

  • 自动维护表单 id

它由 se-container 容器(指令)和 se 组件来表示一个表单,一个简单HTML模板表单是这么写的:

  1. <form nz-form #f="ngForm" se-container="2">
  2. <se label="App Key">
  3. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required>
  4. </se>
  5. <se label="App Secret">
  6. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32">
  7. </se>
  8. <se>
  9. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  10. </se>
  11. </form>

同时,会自动处理所有 Angular 内置校验指令,例如:requiredmaxlengthminpattern 等,并以红色边框来表示无效值状态。

  1. import { SEModule } from '@delon/abc/se';

代码演示

se 编辑 - 图1

基础

一行两列表单。

  1. import { Component } from '@angular/core';
  2. import { NzMessageService } from 'ng-zorro-antd/message';
  3. @Component({
  4. selector: 'components-se-basic',
  5. template: `
  6. <form nz-form #f="ngForm" se-container gutter="32">
  7. <se label="App Key" [error]="{ required: '请填写', pattern: '只能包含a-z, 0-9之间' }">
  8. <input
  9. type="text"
  10. nz-input
  11. [(ngModel)]="i.ak"
  12. name="ak"
  13. required
  14. pattern="^[a-z0-9]*$"
  15. placeholder="必填项,且只能包含a-z, 0-9之间"
  16. />
  17. </se>
  18. <ng-template #appSecretRequired> 请填写,密钥<a (click)="msg.success('success')">生成</a>地址。 </ng-template>
  19. <se label="App Secret" [error]="{ required: appSecretRequired, pattern: '只能包含0-9之间' }">
  20. <input
  21. type="text"
  22. nz-input
  23. [(ngModel)]="i.sk"
  24. name="sk"
  25. required
  26. maxlength="32"
  27. pattern="^[0-9]*$"
  28. placeholder="必填项,且只能包含0-9之间"
  29. />
  30. </se>
  31. <se>
  32. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  33. </se>
  34. </form>`,
  35. })
  36. export class ComponentsSeBasicComponent {
  37. i: any = {};
  38. constructor(public msg: NzMessageService) {}
  39. }

se 编辑 - 图2

响应式表单

支持响应式表单。

注意:

  • 响应式表单无法自动获取 Validators.required 来判断是否需要必填项标识符,因此需要手动标识 required 属性。

  • 当需要使用 patchValue 赋值时,因为 dirty 依然保持为 false 的因素,需要对所有表单元素设置 markAsDirty() 或者使用 ingoreDirty 属性强制忽略 dirty 的校验。

  1. import { Component } from '@angular/core';
  2. import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  3. import { NzMessageService } from 'ng-zorro-antd/message';
  4. @Component({
  5. selector: 'components-se-reactive',
  6. template: ` <form nz-form [formGroup]="validateForm" (ngSubmit)="submitForm()" se-container gutter="32" ingoreDirty>
  7. <se label="App Key" required [error]="{ required: 'Please input your username!', pattern: 'Incorrect format, muse be A' }">
  8. <input formControlName="userName" nz-input placeholder="Username" />
  9. </se>
  10. <se label="App Secret" required error="Please input your Password!">
  11. <input formControlName="password" nz-input type="password" placeholder="Password" />
  12. </se>
  13. <se>
  14. <button nz-button nzType="primary" [disabled]="!validateForm.valid">Log in</button>
  15. <button nz-button nzType="link" type="button" (click)="updateValue()">Update value via patchValue</button>
  16. </se>
  17. </form>`,
  18. })
  19. export class ComponentsSeReactiveComponent {
  20. validateForm: FormGroup;
  21. constructor(fb: FormBuilder, private msg: NzMessageService) {
  22. this.validateForm = fb.group({
  23. userName: [null, [Validators.required, Validators.pattern(/A/)]],
  24. password: [null, [Validators.required]],
  25. remember: [true],
  26. });
  27. }
  28. submitForm(): void {
  29. this.msg.success(JSON.stringify(this.validateForm.value));
  30. }
  31. updateValue(): void {
  32. this.validateForm.patchValue({ userName: 'a' });
  33. }
  34. }

se 编辑 - 图3

紧凑型

强制忽略 errorextra 展示。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-compact',
  4. template: `
  5. <form nz-form #f="ngForm" se-container labelWidth="150" gutter="32" size="compact">
  6. <se label="App Key" error="请填写" optional="(选填)" optionalHelp="通过控制台-查看KEY获取" extra="额外提示信息">
  7. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required>
  8. </se>
  9. <se label="App Secret" error="请填写,最多32位">
  10. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32">
  11. </se>
  12. <se>
  13. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  14. </se>
  15. </form>`,
  16. })
  17. export class ComponentsSeCompactComponent {
  18. i: any = {};
  19. }

se 编辑 - 图4

水平排列

一行一列表单。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-horizontal',
  4. template: `
  5. <form nz-form #f="ngForm" se-container="1" labelWidth="150">
  6. <se label="App Key" error="请填写" optional="(选填)" optionalHelp="通过控制台-查看KEY获取" extra="额外提示信息">
  7. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required>
  8. </se>
  9. <se label="App Secret" error="请填写,最多32位">
  10. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32">
  11. </se>
  12. <se>
  13. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  14. </se>
  15. </form>`,
  16. })
  17. export class ComponentsSeHorizontalComponent {
  18. i: any = {};
  19. }

se 编辑 - 图5

内联布局

nzLayout: inline 时强制使用紧凑型,一般用于搜索框。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-inline',
  4. template: `
  5. <form nz-form nzLayout="inline" #f="ngForm" se-container>
  6. <se label="App Key" error="请填写" optional="(选填)" optionalHelp="通过控制台-查看KEY获取" extra="额外提示信息">
  7. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required>
  8. </se>
  9. <se label="App Secret" error="请填写,最多32位">
  10. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32">
  11. </se>
  12. <se>
  13. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  14. </se>
  15. </form>`,
  16. })
  17. export class ComponentsSeInlineComponent {
  18. i: any = {};
  19. }

se 编辑 - 图6

垂直布局

垂直布局表单。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-vertical',
  4. template: `
  5. <form nz-form nzLayout="vertical" #f="ngForm" se-container>
  6. <se label="App Key" error="请填写" optional="(选填)" optionalHelp="通过控制台-查看KEY获取" extra="额外提示信息">
  7. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required>
  8. </se>
  9. <se label="App Secret" error="请填写,最多32位">
  10. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32">
  11. </se>
  12. <se>
  13. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  14. </se>
  15. </form>`,
  16. })
  17. export class ComponentsSeVerticalComponent {
  18. i: any = {};
  19. }

se 编辑 - 图7

分隔线

构建一个左右结构的标准表单行。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-line',
  4. template: `
  5. <form nz-form #f="ngForm" se-container="1" size="compact" gutter="32">
  6. <se label="所属类目" line>头像</se>
  7. <se label="图片">
  8. <nz-input-group nzSearch [nzAddOnAfter]="suffixButton">
  9. <input type="text" nz-input placeholder="请贴入网络图片地址">
  10. </nz-input-group>
  11. <ng-template #suffixButton>
  12. <button nz-button nzType="primary" nzSearch>提取</button>
  13. </ng-template>
  14. </se>
  15. </form>`,
  16. })
  17. export class ComponentsSeLineComponent {
  18. i: any = {};
  19. }

se 编辑 - 图8

批量重置错误消息

利用 errors 可以批量对所有 se 组件重置 error 值。

  1. import { Component } from '@angular/core';
  2. import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  3. import { SEErrorRefresh } from '@delon/abc/se';
  4. import { NzMessageService } from 'ng-zorro-antd/message';
  5. @Component({
  6. selector: 'components-se-reset-errors',
  7. template: `
  8. <h3>Operating</h3>
  9. <div class="mb-md">
  10. <button nz-button (click)="resetErrors()">Reset all errors</button>
  11. </div>
  12. <form nz-form #f="ngForm" se-container [errors]="ngModelErrors" gutter="32">
  13. <se label="App Key" [error]="{ required: '请填写', pattern: '只能包含a-z, 0-9之间' }">
  14. <input
  15. type="text"
  16. nz-input
  17. [(ngModel)]="i.ak"
  18. name="ak"
  19. required
  20. pattern="^[a-z0-9]*$"
  21. placeholder="必填项,且只能包含a-z, 0-9之间"
  22. />
  23. </se>
  24. <ng-template #appSecretRequired> 请填写,密钥<a (click)="msg.success('success')">生成</a>地址。 </ng-template>
  25. <se label="App Secret" [error]="{ required: appSecretRequired, pattern: '只能包含0-9之间' }">
  26. <input
  27. type="text"
  28. nz-input
  29. [(ngModel)]="i.sk"
  30. name="sk"
  31. required
  32. maxlength="32"
  33. pattern="^[0-9]*$"
  34. placeholder="必填项,且只能包含0-9之间"
  35. />
  36. </se>
  37. <se>
  38. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  39. </se>
  40. </form>
  41. <h3>Reactive</h3>
  42. <form nz-form [formGroup]="validateForm" se-container gutter="32" [errors]="reactiveErrors">
  43. <se label="App Key" [error]="{ required: 'Please input your username!', pattern: 'Incorrect format, muse be A' }">
  44. <input formControlName="userName" nz-input placeholder="Username" />
  45. </se>
  46. <se label="App Secret" error="Please input your Password!">
  47. <input formControlName="password" nz-input type="password" placeholder="Password" />
  48. </se>
  49. <se>
  50. <button nz-button nzType="primary" [disabled]="!validateForm.valid">Log in</button>
  51. </se>
  52. </form>
  53. `,
  54. })
  55. export class ComponentsSeResetErrorsComponent {
  56. validateForm: FormGroup;
  57. i: any = {};
  58. ngModelErrors: SEErrorRefresh[] = [];
  59. reactiveErrors: SEErrorRefresh[] = [];
  60. constructor(fb: FormBuilder, public msg: NzMessageService) {
  61. this.validateForm = fb.group({
  62. userName: [null, [Validators.required, Validators.pattern(/A/)]],
  63. password: [null, [Validators.required]],
  64. remember: [true],
  65. });
  66. }
  67. resetErrors(): void {
  68. this.ngModelErrors = [{ name: 'ak', error: 'Required field, and can only contain a-z, 0-9' }];
  69. this.reactiveErrors = [
  70. { name: 'userName', error: 'Required username' },
  71. { name: 'password', error: 'Required password' },
  72. ];
  73. }
  74. }

se 编辑 - 图9

不规则布局

利用 col 可以构建复杂不规则布局。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'components-se-complex',
  4. template: `
  5. <form nz-form #f="ngForm" se-container size="compact" gutter="24">
  6. <se-title>Title 1</se-title>
  7. <se label="ID" col="1" [optionalHelp]="optionalHelpTpl">
  8. 1000
  9. <ng-template #optionalHelpTpl>
  10. Via by ng-template
  11. </ng-template>
  12. </se>
  13. <se label="Name" required col="3">
  14. <input type="text" nz-input [(ngModel)]="i.user_name" name="user_name" required />
  15. </se>
  16. <se label="Age" required col="3">
  17. <nz-select [(ngModel)]="i.user_age" name="user_age" nzAllowClear nzPlaceHolder="Choose">
  18. <nz-option [nzValue]="1" nzLabel="1"></nz-option>
  19. <nz-option [nzValue]="2" nzLabel="2"></nz-option>
  20. <nz-option [nzValue]="3" nzLabel="3"></nz-option>
  21. <nz-option [nzValue]="4" nzLabel="4"></nz-option>
  22. <nz-option [nzValue]="5" nzLabel="5"></nz-option>
  23. </nz-select>
  24. </se>
  25. <se label="Brithday" required col="3">
  26. <nz-date-picker [(ngModel)]="i.user_birthday" name="user_birthday" nzShowTime></nz-date-picker>
  27. </se>
  28. <se label="App Key" required>
  29. <input type="text" nz-input [(ngModel)]="i.ak" name="ak" required />
  30. </se>
  31. <se label="App Secret" required>
  32. <input type="text" nz-input [(ngModel)]="i.sk" name="sk" required maxlength="32" />
  33. </se>
  34. <se label="Phone Number" required>
  35. <nz-input-group [nzAddOnBefore]="addOnBeforeTemplate">
  36. <ng-template #addOnBeforeTemplate>
  37. <nz-select [(ngModel)]="i.phoneNumberPrefix" name="phoneNumberPrefix" style="width: 70px;">
  38. <nz-option nzLabel="+86" nzValue="+86"></nz-option>
  39. <nz-option nzLabel="+87" nzValue="+87"></nz-option>
  40. </nz-select>
  41. </ng-template>
  42. <input type="text" nz-input [(ngModel)]="i.phoneNumber" name="phoneNumber" required maxlength="32" />
  43. </nz-input-group>
  44. </se>
  45. <se>
  46. <label nz-checkbox [(ngModel)]="i.agree" name="agree">
  47. <span>I have read the <a>agreement</a></span>
  48. </label>
  49. </se>
  50. <se-title>Title 2</se-title>
  51. <se label="Long Long Long Long Long Long Label" col="1">
  52. <textarea [(ngModel)]="i.comment" name="comment" nz-input rows="2" placeholder="write any thing"></textarea>
  53. </se>
  54. <se col="1">
  55. <button nz-button nzType="primary" [disabled]="f.invalid">Save</button>
  56. </se>
  57. </form>
  58. `,
  59. })
  60. export class ComponentsSeComplexComponent {
  61. i: any = {
  62. phoneNumberPrefix: '+86',
  63. agree: true,
  64. user_age: 3,
  65. };
  66. }

API

se-container

成员说明类型默认值全局配置
[gutter]间距,当 nzLayout:horizontal 时有效number32
[se-container]指定表单元素最多分几列展示,最终一行几列由 col 配置结合响应式规则决定‘1’,’2’,’3’,’4’,’5’,’6’-
[col]指定表单元素最多分几列展示,最终一行几列由 col 配置结合响应式规则决定‘1’,’2’,’3’,’4’,’5’,’6’-
[labelWidth]表单元素默认标签文本宽度,单位:pxnumber150
[nzLayout]表单布局,当 inline 时强制大小为 compact‘horizontal’,’vertical’,’inline’‘horizontal’
[size]大小 compact 紧凑型,强制忽略 errorextra 展示‘default’,’compact’‘default’
[firstVisual]是否立即呈现错误视觉booleanfalse
[ingoreDirty]是否忽略 dirty 校验booleanfalse
[line]分隔线booleanfalse
[title]标题string,TemplateRef<void>-
[errors]批量修改 se 错误消息描述SEErrorRefresh[]-

se

成员说明类型默认值
[col]指定表单元素最多分几列展示,最终一行几列由 col 配置结合响应式规则决定(继承于 se-container‘1’,’2’,’3’,’4’,’5’,’6’-
[label]标签文本string, TemplateRef<void>-
[labelWidth]标签文本宽度,单位:px(继承于 se-containernumber-
[optional]标签可选信息string, TemplateRef<void>-
[optionalHelp]标签可选帮助string, TemplateRef<void>-
[error]错误描述string, TemplateRef<void>, { [key: string]: string, TemplateRef }-
[extra]额外提示信息string, TemplateRef<void>-
[required]是否必填项标识符,若不设置自动根据表单元素是否有 RequiredValidator 校验来设置值string-
[controlClass]控件区域样式名string-
[id]自定义组件 idstring-
[line]分隔线(继承于 se-containerboolean-

se-title

用于展示标题,单独一行。

常见问题

什么时候自定义组件id

点击表单的 Label 会将光标定位至相应组件下,而 ng-edit 会自动根据 ngModel 状态合理的设定 id,绝大多数情况下你无须关心 id 的绑定状态,当然若你手动指定 id 值则优先级更高但同时你需要自己维护组件对应的 id 值。