Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support auto error tips in reactive form / 表单增强:根据错误类型自动化的显示错误提示 #4523

Closed
danranVm opened this issue Dec 8, 2019 · 4 comments

Comments

@danranVm
Copy link
Member

danranVm commented Dec 8, 2019

What problem does this feature solve?

zorro8.x 的版本中,nzFormControl 支持了 nzErrorTip 属性。每次表单验证错误的时候,自动显示错误提示信息,这是一个很棒的功能,但是它还可以变得更加方便!
那就是 autoErrorTip ,它主要解决了目前 nzErrorTip 还遗留的两个痛点。

  • 重复定义错误提示:也就是我每个用 nzFormControl 的地方,都要写上一句 Input is required 又或者是 Email is not valid.

  • 处理多种错误类型的时候不是很方便,示例:

    <nz-form-control [nzSpan]="12" nzHasFeedback [nzErrorTip]="emailErrorTpl">
      <input nz-input formControlName="email" placeholder="email" type="email" />
      <ng-template #emailErrorTpl let-control>
        <ng-container *ngIf="control.hasError('email')">
          The input is not valid E-mail!
        </ng-container>
        <ng-container *ngIf="control.hasError('required')">
          Please input your E-mail!
        </ng-container>
      </ng-template>
    </nz-form-control>

另外它还可以带来一个额外的好处,就是标准化错误提示,可以让我们全局的错误提示保持统一,也方便替换,否则你设想一下:

  • 假如有一天我们的产品经理厌倦了 Email is not valid , 要求你把所有这个提示改成 The input is not valid E-mail!。 这个时候,你就只能全局去替换了。

What does the proposed API look like?

<form nz-form [formGroup]="formGroup">
  <nz-form-item>
    <nz-form-control autoErrorTip>
      <input type="text" nz-input formControlName="userName" required />
    </nz-form-control>
  </nz-form-item>
</form>

已经实现了一个 demo: https://stackblitz.com/edit/ng-zorro-antd-auto-error-tip?file=src%2Fapp%2Fauto-error-tip.directive.ts

// 核心代码如下
this.ngControl.statusChanges.pipe(filter(status => status === 'INVALID')).subscribe(() => {
  const errors = this.ngControl.errors || {}
  Object.entries(errors).some(([key, value]) => {
    const errorTip = value[this.errorTipKey]
    if (!!errorTip) {
      this.nzFormControl.nzErrorTip = errorTip
    } else if (this.errorTipMap[key]) {
      this.nzFormControl.nzErrorTip = this.errorTipMap[key]
    }
    return !!errorTip
  })
  this.cdr.markForCheck()
})

实现思路其实挺简单的,就是比根据 NgControl 的错误类型,使用约定好的 errorTipKey 找到对应的错误提示 errorTip ,然后赋值给 nzFormControlnzErrorTip:

对于使用者,需要预先约定好两个数据: errorTipKeyerrorTipMap, 优先级如下:

  • 通过 @Input 的方式设置 errorTipKeyerrorTipMap
  • 通过依赖注入(如果在 zorro 内实现,可以换成全局配置)的方式设置 errorTipKeyerrorTipMap
  • 给定默认的一个 errorTipKey 和一组 errorTipMap

对于 Angular 官方的 Validators, 我们有两种处理方式:

  • errorTipMap 中根据错误类型声明对应的 errorTip ,示例:

    const autoErrorTipMap: Record<string, string> = { email: 'The input is not valid email' }
    providers: [{ provide: AUTO_ERROR_TIP_MAP, useValue: autoErrorTip }]
  • 自定义一个 MyValidators extends Validators ,示例:

    // 约定使用 `errorTip` 约束所有验证器的实现
    export type MyErrorsOptions = { errorTip: string } & Record<string, any>
    
    export type MyValidationErrors = Record<string, MyErrorsOptions>
    
    export class MyValidators extends Validators {
      static maxLength(maxLength: number): ValidatorFn {
        return (control: AbstractControl): MyValidationErrors | null => {
          const length: number = control.value ? control.value.length : 0
          return length > maxLength
            ? { maxlength: { errorTip: `MaxLength is ${maxLength}`, requiredLength: maxLength, actualLength: length } }
            : null
        }
      }
    
      static mobile(control: AbstractControl): MyValidationErrors | null {
        const value = control.value
    
        if (isEmptyInputValue(value)) {
          return null
        }
    
        return isMobile(value) ? null : { mobile: { errorTip: 'Mobile phone number is not valid', actual: value } }
      }
    }
@zorro-bot
Copy link

zorro-bot bot commented Dec 8, 2019

Translation of this issue:

Form Enhancements: according to the type of error automated display an error message

What problem does this feature solve?

Zorro in 8.x version, nzFormControl support the nzErrorTip property. Every form validation errors, automatic error message is displayed, which is a great feature, but it can also become more convenient!
That is autoErrorTip, it solves two major pain points currently nzErrorTip still remaining.

  • duplicate definition error: that is with me every place nzFormControl, should write a Input is required or is Email is not valid.
  • when dealing with multiple types of error is not very convenient, example:

   `Html   <Nz-form-control [nzSpan] = "12" nzHasFeedback [nzErrorTip] = "emailErrorTpl">     <Input nz-input formControlName = "email" placeholder = "email" type = "email" />     <Ng-template #emailErrorTpl let-control>       <Ng-container * ngIf = "control.hasError ( 'email')">         The input is not valid E-mail!       </ Ng-container>       <Ng-container * ngIf = "control.hasError ( 'required')">         Please input your E-mail!       </ Ng-container>     </ Ng-template>   </ Nz-form-control>    `

It also brings an added benefit is that standardized error, allows us to maintain the unity of the global error, but also easy to replace, otherwise you imagine:

  • If one day our product managers are tired of Email is not valid, ask you to change all this promptThe input is not valid Email!. This time, you can only go global replaced.

What does the proposed API look like?

`` `Html

                       `` `

It has achieved a demo: https://stackblitz.com/edit/ng-zorro-antd-auto-error-tip?file=src%2Fapp%2Fauto-error-tip.directive.ts
Realization of ideas actually quite simple, according to the type of error is more than NgControl using an appointment to errorTipKey find error corresponding errorTip, and then assigned to nzFormControl of nzErrorTip:

For users, it is necessary a good agreement two data: errorTipKey and errorTipMap, priority is as follows:

  • Set errorTipKey and errorTipMap way through @ Input
  • errorTipKey dependency injection setting and errorTipMap (if implemented within zorro, can be replaced by global configuration) manner
  • Given a default of errorTipKey and a set of errorTipMap

For Angular official Validators, we have two approaches:

  • errorTipMap corresponding in according to the error type declaration errorTip, examples:

   `Ts   const autoErrorTipMap: Record <string, string> = {email: 'The input is not valid email'}   providers: [{provide: AUTO_ERROR_TIP_MAP, useValue: autoErrorTip}]    `

  • a custom MyValidators extends Validators, examples:

  `` Ts   // agreed to use achieve errorTip` bound by all validators
  export type MyErrorsOptions = {errorTip: string} & Record <string, any>

  export type MyValidationErrors = Record <string, MyErrorsOptions>

  export class MyValidators extends Validators {
    static maxLength (maxLength: number): ValidatorFn {
      return (control: AbstractControl): MyValidationErrors | null => {
        const length: number = control.value control.value.length:? 0
        return length> maxLength
          ? {Maxlength: {errorTip: MaxLength is $ {maxLength}, requiredLength: maxLength, actualLength: length}}
          : Null
      }
    }

    static mobile (control: AbstractControl): MyValidationErrors | null {
      const value = control.value

      if (isEmptyInputValue (value)) {
        return null
      }

      ? Return isMobile (value) null: {mobile: {errorTip: 'Mobile phone number is not valid', actual: value}}
    }
  }
  `` `

@danranVm
Copy link
Member Author

danranVm commented Dec 8, 2019

关联 #4411 ,看看有没有办法在 autoArrorTip 中一并解决。

@CK110
Copy link
Member

CK110 commented Dec 9, 2019

确实感觉在模板中写出所有错误信息很麻烦。

所以我们项目中也是通过自定义Validators来实现类似的效果。

export class ZonValidators {
 // 获取一个错误
  static getErrorExplain(formGroup: FormGroup) {
    let errorMsg;
    for (const item of Object.keys(formGroup.controls)) {
      const itemControl: any = formGroup.controls[item];
      if (itemControl.invalid) {
        if (itemControl.controls) {
          errorMsg = this.getErrorExplain(itemControl);
        } else {
          errorMsg = itemControl.errors.explain;
        }
        if (errorMsg) {
          return errorMsg;
        }
      }
    }
  }

// 校验方法
  static required(name?: string): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any } => {
      if (!this.isPresent(Validators.required(control))) {
        return null;
      }
      if (!name) {
        return {required: true, error: true, explain: '请输入'};
      } else {
        return {required: true, error: true, explain: name + ':为必填项'};
      }
    };
  }
.....
}

这其中有个name属性,是接受label值的,由于移动端toast提示error,需要这个name属性,pc端则可以不需要。

我觉得如果这个 autoErrorTip 能可以加个属性 name或者其他方法能指明当前的label值就更好了。

@vthinkxie
Copy link
Member

vthinkxie commented Dec 9, 2019

很好的设计,提几个建议

  1. defaultErrorTipMap 可以不需要(基本无法覆盖用户所有的需求)
  2. Map 中需要支持国际化参数,errorTipMap 支持传入两种格式,非国际化和多语言,在多语言时需要根据 https://angular.io/api/core/LOCALE_ID 进行切换

具体的代码部分可以等 PR 提出来之后,我再详细review

@vthinkxie vthinkxie changed the title 表单增强:根据错误类型自动化的显示错误提示 support auto error tips in reactive form / 表单增强:根据错误类型自动化的显示错误提示 Dec 9, 2019
@wenqi73 wenqi73 added this to the 9.0.0 milestone Jan 7, 2020
@hsuanxyz hsuanxyz mentioned this issue Apr 15, 2020
hsuanxyz pushed a commit to hsuanxyz/ng-zorro-antd that referenced this issue Aug 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants