Angular 5异步验证器指令调用及错误获取问题求助
Let's break down and fix your two main issues: getting the API call to trigger correctly in your validator, and accessing the validation errors properly in the template.
1. Fixing the Async Validator Implementation
Your original validator was using the standard Validator interface, but for async operations (like API calls), you need to use Angular's AsyncValidator and register it with NG_ASYNC_VALIDATORS instead of NG_VALIDATORS. This tells Angular to handle it as an asynchronous validation task, so you don't need to manually subscribe to anything—Angular takes care of that for you.
Here's the corrected validator code:
import { Directive } from '@angular/core'; import { AsyncValidator, AbstractControl, NG_ASYNC_VALIDATORS, ValidationErrors } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { ApplicationService } from '../services/application.service'; @Directive({ selector: '[applicationNameAlreadyExists][ngModel]', providers: [ { provide: NG_ASYNC_VALIDATORS, useExisting: ApplicationNameAlreadyExistsValidator, multi: true } ] }) export class ApplicationNameAlreadyExistsValidator implements AsyncValidator { constructor(private applicationService: ApplicationService) { } validate(control: AbstractControl): Observable<ValidationErrors | null> { // Skip API call if input is empty to avoid unnecessary requests const trimmedValue = control.value?.trim(); if (!trimmedValue) { return of(null); } return this.applicationService.doesNameAlreadyExist(trimmedValue).pipe( // Map API result to validation error or null map(nameExists => nameExists ? { applicationNameDuplicate: true } : null), // Handle API errors gracefully so validation doesn't get stuck catchError(() => of(null)) ); } }
Key improvements here:
- Switched to
AsyncValidatorinterface andNG_ASYNC_VALIDATORSprovider - Added a check for empty input to skip unnecessary API calls
- Used
mapto convert the API's boolean response into a validation error object (ornullfor valid) - Added
catchErrorto ensure validation doesn't hang if the API fails
2. Accessing Validation Errors Correctly
The { "__zone_symbol__state": null, "__zone_symbol__value": [] } you're seeing is Angular's way of showing that an asynchronous validation task is still pending. The errors object will only update to the actual error (or null) once the API call completes.
Update your template to handle the pending state and access the error properly:
<input id="applicationName" name="applicationName" class="form-control" required maxlength="100" #applicationName="ngModel" applicationNameAlreadyExists [(ngModel)]="application.applicationName" /> <!-- Show loading state while validation is in progress --> <div *ngIf="applicationName.pending" class="text-muted"> Checking if this name is available... </div> <!-- Show error message if validation fails --> <div *ngIf="applicationName.errors?.applicationNameDuplicate" class="text-danger"> This application name already exists! </div> <!-- Debug: View full errors object (only shows final state after pending) --> <pre>{{ applicationName.errors | json }}</pre>
Notes on Your Service Code
Your doesNameAlreadyExist method looks good, but make sure your API returns a proper boolean response. If your API uses HTTP status codes (e.g., 200 for exists, 404 for not exists), adjust the service to handle that:
doesNameAlreadyExist(name: string): Observable<boolean> { const url = `/exists?name=${name}`; return this.http.get<boolean>(url, httpOptions).pipe( debounceTime(2000), tap(_ => this.log(`checked application name=${name}`)), map(() => true), // 200 response means name exists catchError(() => of(false)) // 404 means name is available ); }
内容的提问来源于stack exchange,提问作者AndyKing

