From 10d0e2819fc2a00ed3e89f3144c41f38cc479fb0 Mon Sep 17 00:00:00 2001 From: vthinkxie Date: Thu, 20 Jun 2019 21:56:04 +0800 Subject: [PATCH] feat(module:form): refactor form to support better template driven --- .../core/addon/string_template_outlet.ts | 90 ++++++++++++++++--- components/form/demo/advanced-search.ts | 15 +++- components/form/demo/coordinated.md | 2 +- components/form/demo/coordinated.ts | 10 +-- components/form/demo/dynamic-form-item.ts | 53 ++++++----- components/form/demo/dynamic-rule.ts | 10 +-- components/form/demo/horizontal-login.ts | 16 +--- components/form/demo/layout.ts | 10 +-- components/form/demo/normal-login.ts | 18 ++-- components/form/demo/register.ts | 67 +++++++------- components/form/demo/validate-reactive.md | 6 +- components/form/demo/validate-reactive.ts | 61 +++++-------- components/form/demo/validate-static.md | 14 +-- components/form/demo/validate-static.ts | 41 ++++++--- components/form/demo/validate-template.md | 23 +++++ components/form/demo/validate-template.ts | 65 ++++++++++++++ components/form/doc/index.en-US.md | 19 ++-- components/form/doc/index.zh-CN.md | 19 ++-- .../form/nz-form-control.component.html | 22 ++++- components/form/nz-form-control.component.ts | 56 +++++++++--- components/form/nz-form-explain.component.ts | 8 +- components/form/nz-form-extra.component.ts | 7 ++ components/form/nz-form-item.component.ts | 16 ++-- components/form/nz-form.module.ts | 3 +- 24 files changed, 423 insertions(+), 228 deletions(-) create mode 100644 components/form/demo/validate-template.md create mode 100644 components/form/demo/validate-template.ts diff --git a/components/core/addon/string_template_outlet.ts b/components/core/addon/string_template_outlet.ts index 973df63311..87e491f1ff 100644 --- a/components/core/addon/string_template_outlet.ts +++ b/components/core/addon/string_template_outlet.ts @@ -6,50 +6,114 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { + Directive, + EmbeddedViewRef, + Input, + OnChanges, + SimpleChange, + SimpleChanges, + TemplateRef, + ViewContainerRef +} from '@angular/core'; @Directive({ selector: '[nzStringTemplateOutlet]', exportAs: 'nzStringTemplateOutlet' }) -export class NzStringTemplateOutletDirective { +export class NzStringTemplateOutletDirective implements OnChanges { private isTemplate: boolean; - private inputTemplate: TemplateRef | null = null; + // tslint:disable-next-line:no-any + private inputTemplate: TemplateRef | null = null; private inputViewRef: EmbeddedViewRef | null = null; private defaultViewRef: EmbeddedViewRef | null = null; - constructor(private viewContainer: ViewContainerRef, private defaultTemplate: TemplateRef) {} + // tslint:disable-next-line:no-any + @Input() nzStringTemplateOutletContext: any | null = null; @Input() - set nzStringTemplateOutlet(value: string | TemplateRef) { + // tslint:disable-next-line:no-any + set nzStringTemplateOutlet(value: string | TemplateRef) { if (value instanceof TemplateRef) { this.isTemplate = true; this.inputTemplate = value; } else { this.isTemplate = false; } - this.updateView(); } - updateView(): void { + recreateView(): void { if (!this.isTemplate) { /** use default template when input is string **/ if (!this.defaultViewRef) { - this.viewContainer.clear(); - this.inputViewRef = null; if (this.defaultTemplate) { - this.defaultViewRef = this.viewContainer.createEmbeddedView(this.defaultTemplate); + this.defaultViewRef = this.viewContainer.createEmbeddedView( + this.defaultTemplate, + this.nzStringTemplateOutletContext + ); } } } else { /** use input template when input is templateRef **/ if (!this.inputViewRef) { - this.viewContainer.clear(); - this.defaultViewRef = null; if (this.inputTemplate) { - this.inputViewRef = this.viewContainer.createEmbeddedView(this.inputTemplate); + this.inputViewRef = this.viewContainer.createEmbeddedView( + this.inputTemplate, + this.nzStringTemplateOutletContext + ); } } } } + + private shouldRecreateView(changes: SimpleChanges): boolean { + const { nzStringTemplateOutletContext, nzStringTemplateOutlet } = changes; + return ( + !!nzStringTemplateOutlet || + (nzStringTemplateOutletContext && this.hasContextShapeChanged(nzStringTemplateOutletContext)) + ); + } + + private hasContextShapeChanged(ctxChange: SimpleChange): boolean { + const prevCtxKeys = Object.keys(ctxChange.previousValue || {}); + const currCtxKeys = Object.keys(ctxChange.currentValue || {}); + + if (prevCtxKeys.length === currCtxKeys.length) { + for (const propName of currCtxKeys) { + if (prevCtxKeys.indexOf(propName) === -1) { + return true; + } + } + return false; + } else { + return true; + } + } + + // tslint:disable-next-line:no-any + private updateExistingContext(ctx: any): void { + for (const propName of Object.keys(ctx)) { + // tslint:disable-next-line:no-any + (this.inputViewRef!.context as any)[propName] = this.nzStringTemplateOutletContext[propName]; + } + } + + constructor(private viewContainer: ViewContainerRef, private defaultTemplate: TemplateRef) {} + + ngOnChanges(changes: SimpleChanges): void { + const recreateView = this.shouldRecreateView(changes); + + if (recreateView) { + if (this.viewContainer) { + this.viewContainer.clear(); + this.defaultViewRef = null; + this.inputViewRef = null; + } + this.recreateView(); + } else { + if (this.inputViewRef && this.nzStringTemplateOutletContext) { + this.updateExistingContext(this.nzStringTemplateOutletContext); + } + } + } } diff --git a/components/form/demo/advanced-search.ts b/components/form/demo/advanced-search.ts index 7d25e34811..86b3e452fa 100644 --- a/components/form/demo/advanced-search.ts +++ b/components/form/demo/advanced-search.ts @@ -6,7 +6,7 @@ import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; template: `
-
+
Field {{ control.index }} @@ -21,10 +21,10 @@ import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
-
+
- + Collapse @@ -62,6 +62,15 @@ import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; button { margin-left: 8px; } + + .collapse { + margin-left: 8px; + font-size: 12px; + } + + .search-area { + text-align: right; + } ` ] }) diff --git a/components/form/demo/coordinated.md b/components/form/demo/coordinated.md index d7fdcc559b..3a935c0586 100644 --- a/components/form/demo/coordinated.md +++ b/components/form/demo/coordinated.md @@ -1,5 +1,5 @@ --- -order: 11 +order: 12 title: zh-CN: 表单联动 en-US: Coordinated Controls diff --git a/components/form/demo/coordinated.ts b/components/form/demo/coordinated.ts index 249041d993..f438167abd 100644 --- a/components/form/demo/coordinated.ts +++ b/components/form/demo/coordinated.ts @@ -7,16 +7,13 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; Note - + - Please input your username! Gender - + - Please select your gender! diff --git a/components/form/demo/dynamic-form-item.ts b/components/form/demo/dynamic-form-item.ts index 4250f19a31..c887563960 100644 --- a/components/form/demo/dynamic-form-item.ts +++ b/components/form/demo/dynamic-form-item.ts @@ -5,33 +5,31 @@ import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from selector: 'nz-demo-form-dynamic-form-item', template: ` - + PassengersPassengers + + - - - Please input passenger's name or delete this field. - - @@ -58,40 +56,49 @@ import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from color: #777; } + .passenger-input { + width: 60%; + margin-right: 8px; + } + [nz-form] { max-width: 600px; } + + .add-button { + width: 60%; + } ` ] }) export class NzDemoFormDynamicFormItemComponent implements OnInit { validateForm: FormGroup; - controlArray: Array<{ id: number; controlInstance: string }> = []; + listOfControl: Array<{ id: number; controlInstance: string }> = []; addField(e?: MouseEvent): void { if (e) { e.preventDefault(); } - const id = this.controlArray.length > 0 ? this.controlArray[this.controlArray.length - 1].id + 1 : 0; + const id = this.listOfControl.length > 0 ? this.listOfControl[this.listOfControl.length - 1].id + 1 : 0; const control = { id, controlInstance: `passenger${id}` }; - const index = this.controlArray.push(control); - console.log(this.controlArray[this.controlArray.length - 1]); + const index = this.listOfControl.push(control); + console.log(this.listOfControl[this.listOfControl.length - 1]); this.validateForm.addControl( - this.controlArray[index - 1].controlInstance, + this.listOfControl[index - 1].controlInstance, new FormControl(null, Validators.required) ); } removeField(i: { id: number; controlInstance: string }, e: MouseEvent): void { e.preventDefault(); - if (this.controlArray.length > 1) { - const index = this.controlArray.indexOf(i); - this.controlArray.splice(index, 1); - console.log(this.controlArray); + if (this.listOfControl.length > 1) { + const index = this.listOfControl.indexOf(i); + this.listOfControl.splice(index, 1); + console.log(this.listOfControl); this.validateForm.removeControl(i.controlInstance); } } diff --git a/components/form/demo/dynamic-rule.ts b/components/form/demo/dynamic-rule.ts index 22525b2f66..7d862b8e84 100644 --- a/components/form/demo/dynamic-rule.ts +++ b/components/form/demo/dynamic-rule.ts @@ -7,22 +7,16 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; Name - + - Please input your name Nickname - + - Please input your nickname diff --git a/components/form/demo/horizontal-login.ts b/components/form/demo/horizontal-login.ts index 588ed503c7..1650649eb5 100644 --- a/components/form/demo/horizontal-login.ts +++ b/components/form/demo/horizontal-login.ts @@ -6,23 +6,17 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; template: ` - - + + - Please input your username! - - + + - Please input your Password! @@ -31,8 +25,6 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; - - ` }) export class NzDemoFormHorizontalLoginComponent implements OnInit { diff --git a/components/form/demo/layout.ts b/components/form/demo/layout.ts index d35fd92087..742b487c34 100644 --- a/components/form/demo/layout.ts +++ b/components/form/demo/layout.ts @@ -22,20 +22,14 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; Field A - + - Please input your username! Field B - + - Please input your Password! diff --git a/components/form/demo/normal-login.ts b/components/form/demo/normal-login.ts index 22a8040576..a9ebdcc351 100644 --- a/components/form/demo/normal-login.ts +++ b/components/form/demo/normal-login.ts @@ -6,23 +6,17 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; template: ` - - `, styles: [ ` diff --git a/components/form/demo/register.ts b/components/form/demo/register.ts index b10ae793b8..c0a3d3742e 100644 --- a/components/form/demo/register.ts +++ b/components/form/demo/register.ts @@ -7,16 +7,13 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'
E-mail - + - - The input is not valid E-mail! - Password - + - Please input your password! Confirm Password - + - - + + Please confirm your password! - + Two passwords that you enter is inconsistent! - + @@ -58,42 +50,43 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms' > - + - Please input your nickname! Phone Number - + - + - Please input your phone number! Website - + - Please input website! Captcha - +
@@ -102,20 +95,16 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'
- Please input the captcha you got! - We must make sure that your are a human.
- + - + @@ -128,6 +117,14 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms' [nz-form] { max-width: 600px; } + + .phone-select { + width: 70px; + } + + .register-are { + margin-bottom: 8px; + } ` ] }) diff --git a/components/form/demo/validate-reactive.md b/components/form/demo/validate-reactive.md index 8e3ab7a483..ee9d19c867 100644 --- a/components/form/demo/validate-reactive.md +++ b/components/form/demo/validate-reactive.md @@ -11,7 +11,8 @@ title: 1. `nzValidateStatus`: 校验状态,默认自动从 `nz-form-control` 中的 `NgControl` 获得校验状态,也可以手动指定为特定的 `NgControl`。 2. `nzHasFeedback`:用于给输入框添加反馈图标。 -3. `nz-form-explain`:设置校验文案。 +3. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:设置不同状态校验文案。 +> 当同一种状态下存在多种提示情况时,`nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip` 均支持传入 `TemplateRef<{ $implicit: FormControl }` 类型,可以通过[模板变量]((https://www.angular.cn/guide/template-syntax))导出 `FormControl` 后用于切换不同的提示信息。 ## en-US @@ -19,4 +20,5 @@ We provide properties like `nzValidateStatus` `nzHasFeedback` in `nz-form-contro 1. `nzValidateStatus`: validate status of form components, the default status comes from the `NgControl` in `nz-form-control`, you can set other `NgControl` to it. 2. `nzHasFeedback`: display feed icon of input control -3. `nz-form-explain`: display validate message. +3. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:display validate message。 +> When there are multiple tips in the same state, `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip` supports the passing `TemplateRef<{ $implicit: FormControl }` type, which can be used to switch tips after exporting `FormControl` via the [template syntax](https://angular.io/guide/template-syntax). \ No newline at end of file diff --git a/components/form/demo/validate-reactive.ts b/components/form/demo/validate-reactive.ts index d9452f73e8..6c2f663680 100644 --- a/components/form/demo/validate-reactive.ts +++ b/components/form/demo/validate-reactive.ts @@ -6,77 +6,61 @@ import { Observable, Observer } from 'rxjs'; @Component({ selector: 'nz-demo-form-validate-reactive', template: ` - + Username - + - - + + Please input your username! - + The username is redundant! - - Validating... - - + E-mail - + - - + + The input is not valid E-mail! - + Please input your E-mail! - + Password
- + - Please input your password!
Confirm Password - + - - + + Please confirm your password! - + Password is inconsistent! - + Comment - + - Please write something here! @@ -102,14 +86,14 @@ import { Observable, Observer } from 'rxjs'; }) export class NzDemoFormValidateReactiveComponent { validateForm: FormGroup; - submitForm = ($event: any, value: any) => { - $event.preventDefault(); + + submitForm(value: any): void { for (const key in this.validateForm.controls) { this.validateForm.controls[key].markAsDirty(); this.validateForm.controls[key].updateValueAndValidity(); } console.log(value); - }; + } resetForm(e: MouseEvent): void { e.preventDefault(); @@ -128,6 +112,7 @@ export class NzDemoFormValidateReactiveComponent { new Observable((observer: Observer) => { setTimeout(() => { if (control.value === 'JasonWood') { + // you have to return `{error: true}` to mark it as an error event observer.next({ error: true, duplicated: true }); } else { observer.next(null); @@ -138,7 +123,7 @@ export class NzDemoFormValidateReactiveComponent { confirmValidator = (control: FormControl): { [s: string]: boolean } => { if (!control.value) { - return { required: true }; + return { error: true, required: true }; } else if (control.value !== this.validateForm.controls.password.value) { return { confirm: true, error: true }; } diff --git a/components/form/demo/validate-static.md b/components/form/demo/validate-static.md index 0a2a921bf9..ebc9bd8bcb 100644 --- a/components/form/demo/validate-static.md +++ b/components/form/demo/validate-static.md @@ -1,22 +1,22 @@ --- -order: 10 +order: 11 title: - zh-CN: 模板驱动表单验证 - en-US: Template-driven Forms Validation + zh-CN: 手动指定表单状态 + en-US: Manual Set Validation Status --- ## zh-CN -我们在 `nz-form-control` 上 提供了 `nzValidateStatus` `nzHasFeedback` 等属性,当使用[模板驱动表单](https://angular.io/guide/forms#template-driven-forms)时,可以自己定义校验的时机和内容。 +用户可以在通过 `nz-form-control` 的 `nzValidateStatus` 属性直接设定表单的状态。 1. `nzValidateStatus`: 校验状态,可选 'success', 'warning', 'error', 'validating'。 2. `nzHasFeedback`:用于给输入框添加反馈图标。 -3. `nz-form-explain`:设置校验文案。 +3. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:设置不同状态校验文案。 ## en-US -We provide properties like `nzValidateStatus` `nzHasFeedback` in `nz-form-control` to customize your own validate status and message, when using [template-driven forms](https://angular.io/guide/forms#template-driven-forms). +You can set the form status directly via the `nzValidateStatus` on `nz-form-control`. 1. `nzValidateStatus`: validate status of form components which could be 'success', 'warning', 'error', 'validating'. 2. `nzHasFeedback`: display feed icon of input control -3. `nz-form-explain`: display validate message. +3. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:display validate message。 diff --git a/components/form/demo/validate-static.ts b/components/form/demo/validate-static.ts index 64d2a66a3c..8bd975a99f 100644 --- a/components/form/demo/validate-static.ts +++ b/components/form/demo/validate-static.ts @@ -6,9 +6,12 @@ import { Component } from '@angular/core'; Fail - + - Should be combination of numbers & alphabets @@ -19,9 +22,13 @@ import { Component } from '@angular/core'; Validating - + - I'm validating the content @@ -32,28 +39,36 @@ import { Component } from '@angular/core'; Warning - + - Should be combination of numbers & alphabets Fail - + - Should be combination of numbers & alphabets Success - + Warning - + @@ -87,9 +102,15 @@ import { Component } from '@angular/core'; [nz-form] { max-width: 600px; } + nz-date-picker ::ng-deep .ant-calendar-picker { width: 100%; } + + nz-date-picker, + nz-time-picker { + width: 100%; + } ` ] }) diff --git a/components/form/demo/validate-template.md b/components/form/demo/validate-template.md new file mode 100644 index 0000000000..0b413c4feb --- /dev/null +++ b/components/form/demo/validate-template.md @@ -0,0 +1,23 @@ +--- +order: 10 +title: + zh-CN: 模板驱动表单验证 + en-US: Template-driven Forms Validation +--- + +## zh-CN + +当使用[模板驱动表单](https://angular.io/guide/forms#template-driven-forms)时,模板可以根据模板设定自动进行校验。 + +1. `nzHasFeedback`:用于给输入框添加反馈图标。 +2. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:设置不同状态校验文案。 +> 当同一种状态下存在多种提示情况时,`nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip` 均支持传入 `TemplateRef<{ $implicit: NgModel }` 类型,可以通过[模板变量]((https://www.angular.cn/guide/template-syntax))导出 `NgModel` 后用于切换不同的提示信息。 + + +## en-US + +When using [template-driven forms](https://angular.io/guide/forms#template-driven-forms), the form could change its status via the template setting. + +1. `nzHasFeedback`: display feed icon of input control +2. `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip`:display validate message。 +> When there are multiple tips in the same state, `nzSuccessTip` `nzWarningTip` `nzErrorTip` `nzValidatingTip` supports the passing `TemplateRef<{ $implicit: NgModel }` type, which can be used to switch tips after exporting `NgModel` via the [template syntax](https://angular.io/guide/template-syntax). \ No newline at end of file diff --git a/components/form/demo/validate-template.ts b/components/form/demo/validate-template.ts new file mode 100644 index 0000000000..2d31e611be --- /dev/null +++ b/components/form/demo/validate-template.ts @@ -0,0 +1,65 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'nz-demo-form-validate-template', + template: ` + + + Required + + + + + + MaxLength + + + + + + MinLength + + + + + + Email + + + + + + Pattern + + + + + + Mix + + + + MaxLength is 12 + MinLength is 6 + Input is required + + + + + `, + styles: [ + ` + [nz-form] { + max-width: 600px; + } + ` + ] +}) +export class NzDemoFormValidateTemplateComponent {} diff --git a/components/form/doc/index.en-US.md b/components/form/doc/index.en-US.md index f03babfff6..5cfa625db6 100644 --- a/components/form/doc/index.en-US.md +++ b/components/form/doc/index.en-US.md @@ -94,20 +94,13 @@ A form consists of one or more form fields whose type includes input, textarea, | Property | Description | Type | Default Value | | --- | --- | --- | --- | -| `[nzValidateStatus]` | Reactive Forms: Will generate status based on FormControl | `FormControl` | first `FormControl` in `nz-form-control` | -| `[nzValidateStatus]` | Template-driven Forms: The validation status | `'success'|'warning'|'error'|'validating'` | - | +| `[nzValidateStatus]` | Will generate status based on the input `FormControl`, `NgModel` or string, the default value is the first `FormControl` or `NgModel` in `nz-form-control` | `'success'|'warning'|'error'|'validating' | FormControl | NgModel` | first `FormControl` or `NgModel` in `nz-form-control` | | `[nzHasFeedback]`| Used with `nzValidateStatus`, this option specifies the validation status icon. Recommended to be used only with `Input`. | `boolean` | `false` | - -From `7.3.0` version, `nz-form-control` provide `status` variable, it will switch between `'success'|'warning'|'error'|'validating'` automatically according to `[nzValidateStatus]` passed in, user can get it from template reference variables. - -### nz-form-explain - -Validation messages - - -### nz-form-extra - -The extra prompt message. It is similar to help. +| `[nzExtra]`| The extra prompt message | `string | TemplateRef` | - | +| `[nzSuccessTip]`| Tip display when validate success | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzWarningTip]`| Tip display when validate warning | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzErrorTip]`| Tip display when validate error | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzValidatingTip]`| Tip display when validating | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | ### nz-form-split diff --git a/components/form/doc/index.zh-CN.md b/components/form/doc/index.zh-CN.md index c844daf998..8c15aba597 100644 --- a/components/form/doc/index.zh-CN.md +++ b/components/form/doc/index.zh-CN.md @@ -93,21 +93,14 @@ import { NzFormModule } from 'ng-zorro-antd'; | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| `[nzValidateStatus]` | Reactive Forms:会根据 FormControl 的状态自动生成校验状态 | `FormControl` | `nz-form-control` 中包裹的第一个 `FormControl` | -| `[nzValidateStatus]` | Template-driven Forms:校验状态 | `'success'|'warning'|'error'|'validating'` | - | +| `[nzValidateStatus]` | 会根据传入的 `FormControl` 或 `NgModel` 自动生成校验状态,也可以直接指定状态,不传入时默认值为 `nz-form-control` 中包裹的第一个 `FormControl` 或 `NgModel` | `'success'|'warning'|'error'|'validating' | FormControl | NgModel` | `nz-form-control` 中包裹的第一个 `FormControl` 或 `NgModel` | | `[nzHasFeedback]`| 配合 `nzValidateStatus` 属性使用,展示校验状态图标 | `boolean` | `false`| +| `[nzExtra]`| 用于显示表单额外提示信息 | `string | TemplateRef` | - | +| `[nzSuccessTip]`| 校验状态 success 时提示信息 | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzWarningTip]`| 校验状态 warning 时提示信息 | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzErrorTip]`| 校验状态 error 时提示信息 | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | +| `[nzValidatingTip]`| 正在校验时提示信息 | `string | TemplateRef<{ $implicit: FormControl | NgModel }>` | - | -从 7.3.0 版本开始,`nz-form-control` 提供了 `status` 变量用于指示校验状态,`status` 会自动根据 `[nzValidateStatus]` 在 `'success'|'warning'|'error'|'validating'` 中自动切换,用户可以通过模板变量导出 `status` 用于切换提示信息。 - -### nz-form-explain - -用于显示提示信息,会自动根据当前的 nzValidateStatus 显示不同的颜色 - -> 注意:每个 `nz-form-item` 下最多只能有一个 `nz-form-explain` 。 - -### nz-form-extra - -用于显示表单额外提示信息 ### nz-form-split diff --git a/components/form/nz-form-control.component.html b/components/form/nz-form-control.component.html index 77815f1be4..f3ec3a810e 100644 --- a/components/form/nz-form-control.component.html +++ b/components/form/nz-form-control.component.html @@ -5,5 +5,25 @@ - +
+
+ + {{ nzSuccessTip }} + + + {{ nzWarningTip }} + + + {{ nzErrorTip }} + + + {{ nzValidatingTip }} + +
+
+ + +
+ {{ nzExtra }} +
\ No newline at end of file diff --git a/components/form/nz-form-control.component.ts b/components/form/nz-form-control.component.ts index 7a9ff919ff..e7730b9088 100644 --- a/components/form/nz-form-control.component.ts +++ b/components/form/nz-form-control.component.ts @@ -20,21 +20,24 @@ import { OnInit, Optional, Renderer2, + TemplateRef, ViewEncapsulation } from '@angular/core'; import { FormControl, FormControlName, NgControl, NgModel } from '@angular/forms'; import { Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; -import { toBoolean, NgClassType, NzUpdateHostClassService } from 'ng-zorro-antd/core'; +import { helpMotion, toBoolean, NgClassType, NzUpdateHostClassService } from 'ng-zorro-antd/core'; import { NzColDirective, NzRowDirective } from 'ng-zorro-antd/grid'; - import { NzFormItemComponent } from './nz-form-item.component'; +export type NzFormControlStatusType = 'warning' | 'validating' | 'error' | 'success' | null; + @Component({ selector: 'nz-form-control', exportAs: 'nzFormControl', preserveWhitespaces: false, + animations: [helpMotion], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [NzUpdateHostClassService], @@ -50,13 +53,18 @@ import { NzFormItemComponent } from './nz-form-item.component'; export class NzFormControlComponent extends NzColDirective implements OnDestroy, OnInit, AfterContentInit, AfterViewInit, OnDestroy { private _hasFeedback = false; - validateChanges: Subscription | null; - validateString: string | null; - status: 'warning' | 'validating' | 'error' | 'success' | 'init'; + private validateChanges: Subscription = Subscription.EMPTY; + private validateString: string | null; + validateControl: FormControl | NgModel | null; + status: NzFormControlStatusType = null; controlClassMap: NgClassType = {}; iconType: string; - validateControl: FormControl | NgModel | null; @ContentChild(NgControl, { static: false }) defaultValidateControl: FormControlName; + @Input() nzSuccessTip: string | TemplateRef<{ $implicit: FormControl | NgModel }>; + @Input() nzWarningTip: string | TemplateRef<{ $implicit: FormControl | NgModel }>; + @Input() nzErrorTip: string | TemplateRef<{ $implicit: FormControl | NgModel }>; + @Input() nzValidatingTip: string | TemplateRef<{ $implicit: FormControl | NgModel }>; + @Input() nzExtra: string | TemplateRef; @Input() set nzHasFeedback(value: boolean) { @@ -86,10 +94,7 @@ export class NzFormControlComponent extends NzColDirective } removeSubscribe(): void { - if (this.validateChanges) { - this.validateChanges.unsubscribe(); - this.validateChanges = null; - } + this.validateChanges.unsubscribe(); } watchControl(): void { @@ -127,9 +132,12 @@ export class NzFormControlComponent extends NzColDirective this.status = 'success'; this.iconType = 'check-circle-fill'; } else { - this.status = 'init'; + this.status = null; this.iconType = ''; } + if (this.hasTips) { + this.nzFormItemComponent.withHelpClass = this.showInnerTip; + } this.controlClassMap = { [`has-warning`]: this.status === 'warning', [`is-validating`]: this.status === 'validating', @@ -139,10 +147,34 @@ export class NzFormControlComponent extends NzColDirective }; } + get hasTips(): boolean { + return !!(this.nzSuccessTip || this.nzWarningTip || this.nzErrorTip || this.nzValidatingTip); + } + + get showSuccessTip(): boolean { + return this.status === 'success' && !!this.nzSuccessTip; + } + + get showWarningTip(): boolean { + return this.status === 'warning' && !!this.nzWarningTip; + } + + get showErrorTip(): boolean { + return this.status === 'error' && !!this.nzErrorTip; + } + + get showValidatingTip(): boolean { + return this.status === 'validating' && !!this.nzValidatingTip; + } + + get showInnerTip(): boolean { + return this.showSuccessTip || this.showWarningTip || this.showErrorTip || this.showValidatingTip; + } + constructor( nzUpdateHostClassService: NzUpdateHostClassService, elementRef: ElementRef, - @Optional() @Host() nzFormItemComponent: NzFormItemComponent, + @Optional() @Host() private nzFormItemComponent: NzFormItemComponent, @Optional() @Host() nzRowDirective: NzRowDirective, private cdr: ChangeDetectorRef, renderer: Renderer2 diff --git a/components/form/nz-form-explain.component.ts b/components/form/nz-form-explain.component.ts index 152d0e0179..33724b81e2 100644 --- a/components/form/nz-form-explain.component.ts +++ b/components/form/nz-form-explain.component.ts @@ -7,7 +7,7 @@ */ import { ChangeDetectionStrategy, Component, ElementRef, Renderer2, ViewEncapsulation } from '@angular/core'; -import { helpMotion } from 'ng-zorro-antd/core'; +import { helpMotion, warnDeprecation } from 'ng-zorro-antd/core'; @Component({ selector: 'nz-form-explain', @@ -25,8 +25,14 @@ import { helpMotion } from 'ng-zorro-antd/core'; ` ] }) +/** + * @deprecated Use `[nzSuccessTip] | [nzWarningTip] | [nzErrorTip] | [nzValidatingTip]` in `NzFormControlComponent` instead, will remove in 9.0.0. + */ export class NzFormExplainComponent { constructor(public elementRef: ElementRef, private renderer: Renderer2) { this.renderer.addClass(this.elementRef.nativeElement, 'ant-form-explain'); + warnDeprecation( + `'nz-form-explain' is going to be removed in 9.0.0. Use [nzSuccessTip] | [nzWarningTip] | [nzErrorTip] | [nzValidatingTip] in nz-form-control instead. Read https://ng.ant.design/components/form/en` + ); } } diff --git a/components/form/nz-form-extra.component.ts b/components/form/nz-form-extra.component.ts index c81e15d135..9971cff075 100644 --- a/components/form/nz-form-extra.component.ts +++ b/components/form/nz-form-extra.component.ts @@ -7,6 +7,7 @@ */ import { ChangeDetectionStrategy, Component, ElementRef, Renderer2, ViewEncapsulation } from '@angular/core'; +import { warnDeprecation } from 'ng-zorro-antd/core'; @Component({ selector: 'nz-form-extra', @@ -23,8 +24,14 @@ import { ChangeDetectionStrategy, Component, ElementRef, Renderer2, ViewEncapsul ` ] }) +/** + * @deprecated Use `[nzExtra]` in `NzFormControlComponent` instead, will remove in 9.0.0. + */ export class NzFormExtraComponent { constructor(public elementRef: ElementRef, private renderer: Renderer2) { this.renderer.addClass(this.elementRef.nativeElement, 'ant-form-extra'); + warnDeprecation( + `'nz-form-extra' is going to be removed in 9.0.0. Use [nzExtra] in nz-form-control instead. Read https://ng.ant.design/components/form/en` + ); } } diff --git a/components/form/nz-form-item.component.ts b/components/form/nz-form-item.component.ts index 332ee5ff11..9b988bc298 100644 --- a/components/form/nz-form-item.component.ts +++ b/components/form/nz-form-item.component.ts @@ -25,7 +25,7 @@ import { SimpleChanges, ViewEncapsulation } from '@angular/core'; -import { takeUntil } from 'rxjs/operators'; +import { startWith, takeUntil } from 'rxjs/operators'; import { InputBoolean, NzUpdateHostClassService } from 'ng-zorro-antd/core'; import { NzRowDirective } from 'ng-zorro-antd/grid'; @@ -42,7 +42,7 @@ import { NzFormExplainComponent } from './nz-form-explain.component'; providers: [NzUpdateHostClassService], templateUrl: './nz-form-item.component.html', host: { - '[class.ant-form-item-with-help]': 'listOfNzFormExplainComponent && (listOfNzFormExplainComponent.length > 0)' + '[class.ant-form-item-with-help]': 'withHelpClass' }, styles: [ ` @@ -55,9 +55,9 @@ import { NzFormExplainComponent } from './nz-form-explain.component'; export class NzFormItemComponent extends NzRowDirective implements AfterContentInit, OnDestroy, OnChanges, OnInit, OnDestroy { @Input() @InputBoolean() nzFlex: boolean = false; - @ContentChildren(NzFormExplainComponent, { descendants: true }) listOfNzFormExplainComponent: QueryList; + withHelpClass = false; updateFlexStyle(): void { if (this.nzFlex) { @@ -81,11 +81,15 @@ export class NzFormItemComponent extends NzRowDirective } ngAfterContentInit(): void { - if (this.listOfNzFormExplainComponent) { - this.listOfNzFormExplainComponent.changes.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.listOfNzFormExplainComponent.changes + .pipe( + startWith(true), + takeUntil(this.destroy$) + ) + .subscribe(() => { + this.withHelpClass = this.listOfNzFormExplainComponent && this.listOfNzFormExplainComponent.length > 0; this.cdr.markForCheck(); }); - } } ngOnInit(): void { diff --git a/components/form/nz-form.module.ts b/components/form/nz-form.module.ts index 8ef035784b..eb733b14ca 100644 --- a/components/form/nz-form.module.ts +++ b/components/form/nz-form.module.ts @@ -14,6 +14,7 @@ import { NgModule } from '@angular/core'; import { NzGridModule } from 'ng-zorro-antd/grid'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzAddOnModule } from 'ng-zorro-antd/core'; import { NzFormControlComponent } from './nz-form-control.component'; import { NzFormExplainComponent } from './nz-form-explain.component'; import { NzFormExtraComponent } from './nz-form-extra.component'; @@ -44,6 +45,6 @@ import { NzFormDirective } from './nz-form.directive'; NzFormTextComponent, NzFormSplitComponent ], - imports: [CommonModule, NzGridModule, NzIconModule, LayoutModule, PlatformModule] + imports: [CommonModule, NzGridModule, NzIconModule, LayoutModule, PlatformModule, NzAddOnModule] }) export class NzFormModule {}