【问题标题】:Angular2 custom validator with dependency具有依赖关系的 Angular2 自定义验证器
【发布时间】:2017-06-01 14:01:27
【问题描述】:

在 Angular 2 中,我正在尝试创建自己的自定义验证器。

我创建了自己的 CustomValidators 类,它实现了验证器接口。

import { FormControl } from "@angular/forms";
import { MyhttpService } from "./myhttp.service";
import { Response } from "@angular/http";
import { Injectable, Directive } from '@angular/core';


@Injectable()
export class CustomValidators{

constructor(private _http : MyhttpService){

}

public api(c : FormControl)
{
    // Run 
    this._http.get("/expenses/email?email=" + c.value).subscribe((res:Response) => {
        if(res.status === 200){
            // email exists
            return null;
        } else {
            return {"email" : true}
        }
    });
}

如果我将api设为静态方法,那么我可以成功使用该类。

this._formDetails = fb.group({
  "managerEmail" : ["", [Validators.required, CustomValidators.api] ]
});

当然,这是一个静态方法,因此我无法访问任何构造函数值,因为构造函数尚未运行。

因此我找不到一种方法来实现具有依赖关系的自定义验证器,必须有一种方法。

我尝试将 CustomValidators 列为提供者,以便我的类接收该类的实例化对象。

“typeof CustomValidators”类型上不存在属性“api”

我是否以正确的方式提供课程?为什么方法不存在?

【问题讨论】:

  • 我现在也遇到了同样的问题你找到解决办法了吗?

标签: angular dependency-injection dependencies custom-validators


【解决方案1】:

我在使用 Angular v5.2.9 时遇到了类似的情况,并验证了我的数据库中不存在用户名。我的用例有点不同——我使用的是一个很容易缓存的小型用户列表,我的数据是使用 @ngrx 库集中的,但我希望它能有所帮助。

从 Validator 类开始,构造函数负责发出 fetch 请求并将结果缓存在可观察的静态列表中;这个列表 observable 将被实际的验证方法用来查看用户名是否已经在使用中。

import { Injectable } from '@angular/core'
import { FormControl } from '@angular/forms'

import { Store } from '@ngrx/store'

import { Observable } from 'rxjs/observable'
import 'rxjs/add/operator/take'
import 'rxjs/add/operator/map'

import { myActions } from '../@ngrx/actions/some.actions'
import { State, selectIds } from '../@ngrx/reducers/some.reducer'


@Injectable()
export class CustomValidators {

  static ids_in_use$: Observable<string[]>;

  constructor(
    private store: Store<State>
  ) {
    this.store.dispatch({ type: myActions.FETCH_REQUEST })
    CustomValidators.ids_in_use$ = this.store
      .select( selectIds )
      .map( ( id_list: string[] ) => id_list.map( id => id.toLowerCase() ) )
  }

  static api( control: FormControl ) {
    return new Promise(
      ( resolve ) => {
        CustomValidators.ids_in_use$
          .take( 1 )
          .subscribe(
            id_list => {
              if( id_list.indexOf( control.value.toLowerCase() ) === -1 ) 
                resolve( null )
              else resolve({ 'email-in-use': true })
            })
      })
}

为了规避静态方法中无法访问实例属性,验证器的构造函数负责设置静态属性。因为这个类是用@Injectable()装饰的,所以它可以依赖注入到使用它的组件的构造函数中:

constructor(
  ...,
  private fb: FormBuilder,
  private customValidators: CustomValidators
) { }

这就是我能够确保执行验证器构造函数中的代码的方式,尽管主要验证逻辑是静态方法。同样,我想您可以使用此实例来使用您在验证之前特别需要的任何实例属性/方法 - 在您的情况下,发出 http 请求。然后我可以在 FormBuilder 组中使用静态验证方法(请记住,除非你调用它,否则你的 tslint 会警告你 'customValidators' is declared but its value is never read

this._formDetails = fb.group({
  'managerEmail': [ '', Validators.required, CustomValidators.api ]
})

最后,必须为可注入服务声明一个提供者,这可以在@Component 装饰器中完成:

@Component({
  ...,
  providers: [ CustomValidators ]
})

【讨论】:

    【解决方案2】:

    试试这个:

    this._formDetails = fb.group({
      "managerEmail" : ["", Validators.required, CustomValidators.api]
    });
    

    Validators.required 是同步验证器,但您的 CustomValidators.api 是异步验证器。

    根据the official documentation,每个表单控件都应指定状态、同步验证器,然后是异步验证器。

    【讨论】:

    • 您的回答包含错误:this._formDetails = fb.group({ "managerEmail" : ["", Validators.required], [CustomValidators.api] }); 就是您的意思。但这不是他问题的答案。
    猜你喜欢
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多