Angular反应形式是否可以在形式之外的条件下进行验证? [英] Is it possible in Angular reactive forms to validate on conditions out of the form?

查看:72
本文介绍了Angular反应形式是否可以在形式之外的条件下进行验证?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究Angular反应形式验证.我在其上实现了Google自动完成功能的输入:

I'm working on Angular reactive forms validation. I have an input with google autocomplete implemented on it:

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

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

It is standard implementation, everytime you enter some keyword, you get places suggestions:

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

Now, what I would like to do is to validate the address. The address needs to contain zip code - only then should be valid. So once you type something and choose one of the suggestions, the validation should be fired. When chosen suggestion contains zip code is valid, when not - is invalid.

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

As you can see there is only one form control for the whole address (and this should stay like that), which I fill with formatted address from google API. I'm getting also the information about the address components from google places API, which I store in global variables (zipCode, countryName, cityName, streetName):

    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 创建表单的方法中,我使用自定义验证程序:

In the method which creates the form with FormBuilder I use custom validator:

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

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

With below custom validation method I would like to get the error when the zipCode is missing in address:

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

但是该表单未正确验证,因为无论我获得的地址是否包含邮政编码-它始终有效:

However the form it is not correctly validated, because no matter I get the address with or without zip code - it is always valid:

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

If I use as a condition form control related value, the validation it's working. So updating the validation method to the below state generates an error when there is nothing entered to the input:

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

问题:

推荐答案

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

The answer is, it is possible to validate on conditions out of the form and as usually it was very close to me.

首先,我搞砸了验证方法.我使用的 Observable 用于异步验证.因此,在修复它之后,该方法将仅返回对象,而不返回Observable:

First I've messed up with the validation method. Observable I've used is for async-validation. So after fixing it, the method will return the object only, not the 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);
因此,我将其移到了我要查找的邮政编码所在部分的后面,并且有效:

Then I've started to get the validation done, but for the previous state of the zipCode variable. I thought because maybe I need to use the async-validation, but it was even simpler. I've set the value of address form control too early: this.form.get('address').setValue(place.formatted_address);
So I've moved it behind the part where I'm looking for the zip code and it worked:

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);
    });
  });
});

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

There is also question about what if I want to fire the validation in other moment, not from beginning. Then just need to set it in the right place and update the validators:

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

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

Also if there is a need to remove it at some point, here is the easy way:

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

这篇关于Angular反应形式是否可以在形式之外的条件下进行验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆