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

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

问题描述

我正在研究 Angular 反应式表单验证.我有一个输入,上面实现了谷歌自动完成:

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:

现在,我想做的是验证地址.地址需要包含邮政编码 - 只有这样才能有效.因此,一旦您输入内容并选择其中一项建议,就应该触发验证.When chosen suggestion contains zip code is valid, when not - is invalid.

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 的格式化地址填充.我还从谷歌地点 API 获取有关地址组件的信息,我将这些信息存储在全局变量(zipCodecountryNamecityNamestreetName):

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

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

Question:
Is it possible to validate the form like that - with the conditions actually out of the form (since I do not pass this zipCode value to any form control directly)?

推荐答案

答案是,可以验证表单之外的条件,而且通常它与我非常接近.

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

首先我搞砸了验证方法.Observable 我使用的是 async-validation.所以在修复之后,该方法将只返回对象,而不是 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天全站免登陆