Пользовательский валидатор Angular2 с зависимостью

В 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 в качестве поставщика, поэтому мой класс получает экземпляр объекта класса.

Свойство 'api' не существует для типа 'typeof CustomValidators'

Правильно ли я предоставляю класс? Почему метод не существует?


person Ed Byrne    schedule 17.01.2017    source источник
comment
У меня сейчас такая же проблема, вы нашли решение?   -  person Alex Onozor    schedule 01.02.2017


Ответы (2)


Я столкнулся с аналогичной ситуацией, используя Angular v5.2.9 с проверкой того, что имя пользователя еще не существует в моей базе данных. Мой вариант использования немного отличается. Я использую небольшой список пользователей, который легко кэшируется, а мои данные централизованы с помощью библиотеки @ngrx, но я надеюсь, что это может быть полезно.

Начиная с класса Validator, конструктор отвечает за создание запроса на выборку и кэширование результатов в статическом списке, который можно наблюдать; этот наблюдаемый список будет использоваться фактическим методом проверки, чтобы увидеть, используется ли уже имя пользователя.

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 ]
})
person Cole Cooper    schedule 22.03.2018

Попробуй это:

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

Validators.required — это синхронный валидатор, а ваш CustomValidators.api — асинхронный валидатор.

Согласно официальной документации, каждый элемент управления формой следует указать состояние, синхронный(е) валидатор(ы), затем асинхронный(е) валидатор(ы).

person Mike Airey    schedule 01.03.2017
comment
Ваш ответ содержит ошибку: this._formDetails = fb.group({ "managerEmail" : ["", Validators.required], [CustomValidators.api] }); это то, что вы имели в виду. Но это не ответ на его вопрос. - person Michael Trouw; 24.08.2017