在Angular中,如何创建用于验证http请求的自定义验证器?

问题描述

我正在使用gitLab Issues显示图表的应用程序。要使用api进行身份验证,我想使用一个访问令牌,该令牌将附加到get请求中,如下所示:

https://gitlab.de/api/v4/issues?private_token=*********** *

我有一个表单,用户可以在其中输入个人访问令牌。我想使用自定义输入验证器来验证令牌,并在输入字段下方添加错误消息(我使用的是材质角度)。

我使用服务发出http请求:

  private makeGetRequest(endpoint: string,params: HttpParams) {
    return this.http.get<Issue[]>(this.buildURL(endpoint),{params}).pipe(catchError(this.handleError));
  }

  public getIssues(): Observable<Issue[]> {
    let params = new HttpParams().set('private_token',this.access_token);
    return this.makeGetRequest('/issues',params)
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:',error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong.
      console.error(
        `Backend returned code ${error.status},` +
        `body was: ${error.error}`);
    }
    // Return an observable with a user-facing error message.
    return throwError(
      'Something bad happened; please try again later.');
  }

在组件中,我添加一个验证器函数。我正在尝试对api进行一些调用,然后检查它是否有效。

  // Form group with validation
  authForm = new FormGroup({
    url: new FormControl('gitlab.de'),token: new FormControl('',[this.validatetoken])
  });


  // Add error message below input fields
  getErrorMessage() {
    if (this.authForm.controls.token.hasError('required')) {
      return 'You must enter a token';
    }
    return this.authForm.controls.token.hasError('tokenInvalid') ? 'Not a valid token' : '';
  }

// Check if token is valid using api
  validatetoken(control: AbstractControl): { [key: string]: any } | null {
    if (control.dirty || control.touched) {
      this.apiService.getIssues().subscribe((response) => {},(error) => {return {'tokenInvalid': true}})
    } else {
      return null
    }
  }

有几本教程,但是我不能全神贯注。 当我在输入中键入内容时,我只是在控制台中得到以下输出错误TypeError:这是未定义的

解决方法

validator函数是由Angular执行的,而不是我们的代码,这就是Validator函数中的'this'关键字未指向您的类的原因。

您可以像这样简单地将您的validateToken函数绑定到Validators数组中:

token: new FormControl('',[this.validateToken.bind(this)])

此外,如果您要通过返回一个promise / observable在这样的验证器函数中执行异步操作,我建议您使用自定义的异步验证器函数:

usernameValidator(): AsyncValidatorFn {
  return (control: AbstractControl): Observable<ValidationErrors | null> => {
    return this.checkIfUsernameExists(control.value).pipe(
      map(res => {
        // if res is true,username exists,return true
        return res ? { usernameExists: true } : null;
        // NB: Return null if there is no error
      })
    );
  };
}
,

您不需要创建服务,可以通过depenceny注入访问http模块。首先设置http模块:

在app.module.ts中:

import { HttpClientModule } from '@angular/common/http';
@NgModule({
  declarations: [AppComponent],imports: [BrowserModule,HttpClientModule],providers: [],bootstrap: [AppComponent],})

创建一个类以编写自定义异步验证器:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { AsyncValidator,FormControl } from '@angular/forms';
import { map,catchError } from 'rxjs/operators';
import { of } from 'rxjs';

// this class needs to use the dependency injection to reach the http client to make an api request
// we can only access to http client with dependecny injection system
// now we need to decorate this class with Injectable
@Injectable({
  providedIn: 'root',})
export class HttpRequestValidation implements AsyncValidator {
  // with this code inside the constructor,we access to "http"
  constructor(private http: HttpClient) {}
  
  // validate() will be called by the component that implments the 
     form which has a  different context. "this" refers to the context 
     that calls the function. so in other context,"this" will refer 
     to that context,so "this.http" will be undefined in that context 
     cause that context does not have http.
 // that is why we use arrow function here. Because,wherever you use 
    the arrow function,"this" inside arrow will always refer to the 
    object that it was created in. in this case "this" will always 
    refer to the current class (HttpRequestValidation). so "this.http" 
    will work.
  validate = (control: FormControl) => {
    // if this validator would be used by the FormGroup,you could use "FormGroup" type.
    //if you are not sure you can  use "control: AbstractControl)"
    // looks like,in your case you need for the FormControl
    const { value } = control;
    return this.http
      .post<any>('https://domain/',{
      //looks like you are checking for the token
        token: value,})
      .pipe(
        //   errors skip the map(). if we return null,means we got 200 response code
        map(() => {
          return null;
        }),catchError((err) => {
          //check the err obj to see its properties
          console.log(err);
          if (err.error.token) {
          //catchError has to return a new Observable and "of" is a shortcut
            return of({ write a meaningful obj});
          }
          return of({ write a meaningful obj});
        })
      );
  };
}

现在我们在单独的类中编写了此代码,是时候将其用于表单了。

authForm = new FormGroup({
    url: new FormControl('gitlab.plri.de'),token: new FormControl('',[arrays of sync validators],asyncValidator)
  });

FormControl()有3个参数。第一个是初始值,第二个是同步验证器数组,第三个是异步验证器。由于实现异步验证器非常昂贵,因此angular首先会解决同步验证器。如果所有同步验证器都验证了输入,然后将启动异步验证。

import { Component,OnInit } from '@angular/core';
import { FormGroup,FormControl,Validators } from '@angular/forms';
import { HttpRequestValidation } from '../validators/unique-username';

export class SignupComponent implements OnInit {
authForm = new FormGroup({
    url: new FormControl('gitlab.plri.de'),[ 
                             Validators.required,Validators.minLength(3)],this.httpRequestValidation.validate)
  });
constructor(
    private httpRequestValidation: HttpRequestValidation
  ) {}

  ngOnInit(): void {}
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...