Angular反应形式是否可以在形式之外的条件下进行验证?

时间:2018-10-22 12:35:27

标签: angular typescript validation angular-reactive-forms

我正在研究Angular反应形式验证。我在上面输入了带有Google自动完成功能的输入:

<input autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="input-auto input" formControlName="address">

这是标准的实现方式,每当您输入一些关键字,您都会获得位置建议:

google-autocomplete

现在,我想做的就是验证地址。 该地址需要包含邮政编码-只有这样才能有效。因此,一旦您键入内容并选择建议之一,就应该触发验证。选择的建议中包含邮政编码有效,否则无效-无效。

如您所见,整个地址只有一个表单控件(应该保持这样),我用Google API中的格式化地址填充了该控件。我还从Google Places API获取有关地址组件的信息,这些信息存储在全局变量(zipCodecountryNamecityNamestreetName)中:

    this.mapsAPILoader.load().then(() => {
      const autocomplete = new window['google'].maps.places.Autocomplete(this.searchElementToRef.nativeElement, {
        types: ['address']
      });
      autocomplete.addListener('place_changed', () => {
        this.ngZone.run(() => {

          const place = autocomplete.getPlace();

          if (place.geometry === undefined || place.geometry === null) {
            return;
          }

          this.form.get('address').setValue(place.formatted_address);

          for (const addressComponent of place.address_components) {
            for (const addressComponentType of addressComponent.types) {
              switch (addressComponentType) {
                case 'postal_code':
                  this.zipCode = addressComponent.long_name;
                  break;
                case 'country':
                  this.countryName = addressComponent.long_name;
                  break;
                case 'locality':
                  this.cityName = addressComponent.long_name;
                  break;
                case 'route':
                  this.streetName = addressComponent.long_name;
                  break;
              }
            }
          }
        });
      });
    });

在使用FormBuilder创建表单的方法中,我使用了自定义验证程序:

  public createFormGroup(): FormGroup {
    return this.fb.group({
      address: [null, this.zipCodeValidator()]
    });
  }

使用以下自定义验证方法,当地址中缺少zipCode时,我会收到错误消息:

  public zipCodeValidator(): ValidatorFn {
    return (control: AbstractControl): Observable<{ [key: string]: boolean } | null> => {
      if (this.zipCode !== undefined) {
        return of({ zipCode: true });
      }
      return null;
    };
  }

然而,由于我无论得到的地址有无邮政编码,该表单均未正确验证,因此始终有效:

form print

如果我将条件表单控件的相关值用作条件,则验证有效。因此,当没有输入任何内容时,将验证方法更新为以下状态会产生错误:

  public zipCodeValidator(): ValidatorFn {
    return (control: AbstractControl): Observable<{ [key: string]: boolean } | null> => {
      if (control.value === null) {
        return of({ noValue: true });
      }
      return null;
    };
  }

问题:
是否有可能像这样验证表单-条件实际上不在表单范围内(因为我没有将此zipCode值直接传递给任何表单控件)?

1 个答案:

答案 0 :(得分:0)

答案是,可以根据表格之外的条件进行验证,而且通常情况下,该条件与我非常接近。

首先,我搞砸了验证方法。我使用过的Observable用于async-validation。因此,修复它之后,该方法将仅返回对象,而不返回Observable:

  public zipCodeValidator(): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
      if (control.value !== null) {
        // if value exists, we can add number of conditions
        if (this.zipCode !== undefined) {
          return { zipCode: true };
        }
      } else {
          // here we can react when there is no value entered - act as validator.required
          return { empty: true}
        }
      return null;
    };
  }

然后,我开始完成验证,但对于zipCode变量的先前状态。我想是因为也许我需要使用异步验证,但是它甚至更简单。我设置地址表单控件的值还为时过早:this.form.get('address').setValue(place.formatted_address);
因此,我将其移到了我要查找的邮政编码所在部分的后面,并且有效:

this.mapsAPILoader.load().then(() => {
  const autocomplete = new window['google'].maps.places.Autocomplete(this.searchElementToRef.nativeElement, {
    types: ['address']
  });
  autocomplete.addListener('place_changed', () => {
    this.ngZone.run(() => {

      const place = autocomplete.getPlace();

      if (place.geometry === undefined || place.geometry === null) {
        return;
      }

      for (const addressComponent of place.address_components) {
        for (const addressComponentType of addressComponent.types) {
          switch (addressComponentType) {
            case 'postal_code':
              this.zipCode = addressComponent.long_name;
              break;
            case 'country':
              this.countryName = addressComponent.long_name;
              break;
            case 'locality':
              this.cityName = addressComponent.long_name;
              break;
            case 'route':
              this.streetName = addressComponent.long_name;
              break;
          }
        }
      }

      this.form.get('address').setValue(place.formatted_address);
    });
  });
});

还有一个问题,就是我是否想在其他时候而不是从一开始就启动验证。然后只需将其设置在正确的位置并更新验证器:

this.form.get('address').setValidators(this.zipCodeValidator());
this.form.get('address').updateValueAndValidity();

如果需要在某个时候将其删除,这是简单的方法:

this.form.get('address').clearValidators();