【问题标题】:Angular - Uniqueness validator in FormArrayAngular - FormArray 中的唯一性验证器
【发布时间】:2017-09-20 12:47:47
【问题描述】:

我创建了一个自定义验证器来验证我的FormArray 中的唯一性。当特定值已经在数组中时,我想显示错误。

问题是它没有按预期工作。

实际行为:

重现步骤:

  • 添加 3 个“输入” - 地址;
  • 填写输入1;
  • 用不同的值填充输入 2;
  • 用与输入 1 相同的值填充输入 3; (输入 1 和输入 3 均未出现错误)

预期行为

如果相同的值出现在“X 组”中,则它们的特定输入必须显示错误。

在上述情况下,错误应该出现在输入 1 和 3。

假设我有 4 个输入:

  1. 值:堆栈
  2. 值:溢出
  3. 值:堆栈
  4. 值:溢出

4 个输入必须显示错误,因为它们都是重复的。


static uniqueBy = (field: string, caseSensitive = true): ValidatorFn => {
  return (formArray: FormArray): { [key: string]: boolean } => {
    const controls = formArray.controls.filter(formGroup => {
      return isPresent(formGroup.get(field).value);
    });
    const uniqueObj = { uniqueBy: true };
    let found = false;

    if (controls.length > 1) {
      for (let i = 0; i < controls.length; i++) {
        const formGroup = controls[i];
        const mainControl = formGroup.get(field);
        const val = mainControl.value;    
        const mainValue = caseSensitive ? val.toLowerCase() :  val;

        controls.forEach((group, index) => {
          if (i === index) {
            // Same group
            return;
          }

          const currControl = group.get(field);
          const tempValue = currControl.value;
          const currValue = caseSensitive ? tempValue.toLowerCase() : tempValue;
          let newErrors;

          if ( mainValue === currValue) {
            if (isBlank(currControl.errors)) {
              newErrors = uniqueObj;
            } else {
              newErrors = Object.assign(currControl.errors, uniqueObj);
            }

            found = true;
          } else {
            newErrors = currControl.errors;

            if (isPresent(newErrors)) {
              // delete uniqueBy error
              delete newErrors['uniqueBy'];

              if (isBlank(newErrors)) {
                // {} to undefined/null
                newErrors = null;
              }
            }
          }

          // Add specific errors based on condition
          currControl.setErrors(newErrors);
        });
      }

      if (found) {
        // Set errors to whole formArray
        return uniqueObj;
      }
    }

    // Clean errors
    return null;
  };
}

你可以在这里查看DEMO

【问题讨论】:

  • 你可以试试ui-validate library
  • @yurzui 感谢您的演示。似乎它工作得很好。您可以将其添加为答案以便我检查吗?此外,如果您不介意,您可以提供一些解释,说明您为使其工作所做的工作。

标签: angular angular2-forms


【解决方案1】:

我无法添加评论,所以我只是将其作为新答案。我稍微修改了 yurzui 的 plunker,因为在更正具有重复值的字段后表单仍然无效。

如何在 yurzui 的 plunker 中重现错误:

  1. 正确填写名称
  2. 用值“x”填写街道
  3. 添加街道值为“x”的新地址
  4. 将第二条街道更改为“x”以外的其他内容

表单仍然无效,因为第一个“street”FormControl 的错误设置为“{}”而不是 null。我刚刚注意到 dev_054 在他的原始帖子中做到了,但无论如何,这里有一个改动:http://plnkr.co/edit/nQeQ01fjnTlcp3FgEnWL?p=preview

代码是:

controls.map(formGroup => formGroup.get(field)).forEach(x => x.errors && delete x.errors['uniqueBy']);

现在是代码:

controls.map(formGroup => formGroup.get(field)).forEach(x => {
    if (x.errors) {
        delete x.errors['unique-by'];
        if (isBlank(x.errors)) {
            x.setErrors(null);
        }
    }
});

在 plunker 中,我还修复了参数 caseSensitive 的解释方式。它现在可以正常工作了。

【讨论】:

  • 谢谢,除了主要答案之外,每个人都应该关注这个答案。
【解决方案2】:

在您的代码中使用嵌套 for 循环,您会在其中交错错误。

以下是每次迭代的验证状态:

  0      [null, "{"uniqueBy":true}", null]

  1      ["{"uniqueBy":true}", "{"uniqueBy":true}", null]

  2      [null, "{}", null]

http://plnkr.co/edit/MTjzQ9KiJHJ56DVAZ155?p=preview(添加三个地址并观察输出)

在下面的代码中,我只在 for 循环语句之前清除错误一次,不再删除错误。

controls.map(formGroup => formGroup.get(field)).forEach(x => x.errors && delete x.errors['uniqueBy']);
for (let i: number = 0; i < controls.length; i++) {
    const formGroup: FormGroup = controls[i] as FormGroup;
    const mainControl: AbstractControl = formGroup.get(field);
    const val: string = mainControl.value;

    const mainValue: string = caseSensitive ? val.toLowerCase() :  val;
    controls.forEach((group: FormGroup, index: number) => {
        if (i === index) {
            return;
        }

        const currControl: any = group.get(field);
        const tempValue: string = currControl.value;
        const currValue: string = caseSensitive ? tempValue.toLowerCase() : tempValue;
        let newErrors: any;

        if ( mainValue === currValue) {
            if (isBlank(currControl.errors)) {
                newErrors = uniqueObj;
            } else {
                newErrors = Object.assign(currControl.errors, uniqueObj);
            }
            currControl.setErrors(newErrors);
            find = true;
        }
    });
}

Plunker Example

【讨论】:

    猜你喜欢
    • 2019-05-28
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多