Angular - FormArray

时间:2017-04-24 00:10:02

标签: angular angular2-forms

我创建了一个自定义验证程序来验证FormArray中的唯一性。我想在特定(s)值已经在数组中时显示错误。

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

实际行为:

重现的步骤:

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

预期行为

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

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

假设我有4个输入:

  1. value:stack
  2. value:overflow
  3. value:stack
  4. value:overflow
  5. 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

2 个答案:

答案 0 :(得分:9)

在你的代码中使用嵌套for循环来交错。

以下是验证状态查找每次迭代的方式:

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

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

  2      [null, "{}", null]

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

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

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

答案 1 :(得分:5)

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

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

  1. 正确填写姓名
  2. 使用值'x'
  3. 填写街道
  4. 添加街道值为'x'
  5. 的新地址
  6. 将第二条街道更改为“x”以外的其他街道
  7. 表单仍然无效,因为第一个“街道”FormControl的错误设置为“{}”而不是null。我只是注意到dev_054在他的原始帖子中做到了,但无论如何,这里有一个改变的plunker: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的方式。它现在只能作为例外。