添加验证器但不清除(ngx-chips,angular)

Validator on add and without clearing (ngx-chips, angular)

我正在使用 ngx-chips 将电子邮件列表作为标签添加到输入中。验证器确保每个标签看起来像一封电子邮件。

我如何确保:

1) 验证器仅在添加标签时触发(即用户点击回车、space 或逗号)

2) 如果在点击 enter/space/comma 时电子邮件无效,则该值仍然存在(即,它不清除......以便用户可以修复它)

堆栈闪电战在这里:https://stackblitz.com/edit/ngx-chips-example-2qdudc

下面是我的电子邮件验证器:

public validators = [ this.must_be_email ];
  public errorMessages = {
      'must_be_email': 'Please be sure to use a valid email format'
  };
  private must_be_email(control: FormControl) {        
      var EMAIL_REGEXP = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}$/i;
      if (control.value.length != "" && !EMAIL_REGEXP.test(control.value)) {
          return { "must_be_email": true };
      }
      return null;
  }

下面是标签:

<tag-input [(ngModel)]='emails' 
name="emails" 
#email="ngModel" 
[errorMessages]="errorMessages"
[validators]="validators" 
[editable]='true' 
(onTagEdited)="onTagEdited($event)" 
[separatorKeyCodes]="[32,188,186,13,9]"
[placeholder]="'Add email'" 
[secondaryPlaceholder]="'Enter email address(es)'" 
[clearOnBlur]="true" 
[addOnPaste]="true"
[addOnBlur]="true"
[pasteSplitPattern]="splitPattern" 
theme='bootstrap' 
required >
</tag-input>

对于 2),我尝试在验证器中将 "return null" 更改为 control.value...但这没有用

ngx-chips 有一个事件 onAdding you can use to do additional checking. Inside the event handler you can check if the component is valid and cancel the adding if the control is invalid. Then the text will stay. For Enter key it cannot work because the Enter key is coded to submit the form and always clears the text. You can see it in the GitHub source code of the tag-input-form component 供标签输入内部​​使用。检查 onKeyDown 方法。

这是组件中 onAdding 处理程序的示例实现:

public onAdding(tag): Observable<string> {
  if (!this.hasErrors()) { // this is tricky the this here is actually the ngx-chips tag-input component
    return of(tag);
  } else {
    return EMPTY;
  }
}

正如我在代码的注释中提到的那样,由于事件处理程序的调用方式,函数内部的 this 实际上是 ngx-chips 标签输入组件,而不是您通常使用的组件预计。

使用此方法后,只需将其绑定到模板中即可。

<tag-input ... [onAdding]="onAdding">

我还使用此实现创建了一个 fork of your stackblitz

如果这对您不起作用,您可能需要联系组件的作者以获取更多详细信息或使用其他组件。例如,Angular material 组件包含一个类似的 chip input component

最终,此控件的笨拙实现使事情变得困难。如果它是一个模型包含当前输入和标签数组的 ControlValueAccessor,那么找到解决方案会简单得多。

与@AlesD 的回答一样,我采用了利用 onAdding 的解决方案。他提出的一个问题是使用 this 的问题。为了解决这个问题,我在必要时使用 bind() 函数。

为了实现您想要的行为,我做了三件事:

  1. 修改验证器函数,使其仅在字段 addFirstAttemptFailed 为真时 return 出错。这将阻止验证器执行。
  2. onAdding 添加回调。它验证标签,如果验证失败,将 addFirstAttemptFailed 设置为 true 并将 returns 设置为可观察到的错误(我升级到 rxjs 6)。抛出此错误会阻止添加标签。
  3. 成功添加项目后 addFirstAttemptFailed 设置回 false,以便下一个标签的行为可以再次开始。

不幸的是,在 onAdding 期间调用的方法有一些绑定。

  • 为了进行验证,我必须获得对芯片的 TagInputComponent 的引用并调用 setInputValue(),传递已设置的值。相信我,在我偶然发现这种副作用之前,我尝试了上千种变体。尝试调用组件上的 FormControl 实例或 TagInputComponent 中的 Form 和 FormControl 的各种实例上的 updateValueAndValidty() 从未完全奏效。
  • 为了防止添加(并且不清除输入),我必须 return 从 throwError() 观察到的错误。不幸的是,订阅在内部设置的方式是 TagInput 组件仅在其订阅回调函数上调用 catchError(),而不是在可观察到的源上。所以错误会显示在控制台中。再次 - 我尝试了很多不同的方法来解决它。

Relevant code

@ViewChild('tagInput')
tagInput: SourceTagInput;


public validators = [ this.must_be_email.bind(this) ];
public errorMessages = {
  'must_be_email': 'Please be sure to use a valid email format'
};

public readonly onAddedFunc = this.beforeAdd.bind(this);

private addFirstAttemptFailed = false;

private must_be_email(control: FormControl) {        
  if (this.addFirstAttemptFailed && !this.validateEmail(control.value)) {
    return { "must_be_email": true };
  }
  return null;
}

private beforeAdd(tag: string) {

  if (!this.validateEmail(tag)) {
    if (!this.addFirstAttemptFailed) {
      this.addFirstAttemptFailed = true;
      this.tagInput.setInputValue(tag);
    }
    return throwError(this.errorMessages['must_be_email']);
  }
  this.addFirstAttemptFailed = false;
  return of(tag);
}