dynamic-forms / Interface

HttpCustomValidator

Generic types:TValue TResult

HTTP-based validator configuration for Angular's validateHttp API

Angular's validateHttp provides optimized HTTP validation with automatic request cancellation and integration with the resource API.

Benefits:

  • Automatic request cancellation when field value changes
  • Built-in integration with Angular's resource management
  • Simpler than AsyncCustomValidator for HTTP use cases

Structure:

  • request: Function that returns URL string or HttpResourceRequest
  • onSuccess: REQUIRED - Maps HTTP response to validation errors (inverted logic!)
  • onError: Optional handler for HTTP errors (network failures, 4xx/5xx)

Important: onSuccess uses inverted logic - it maps SUCCESSFUL HTTP responses to validation errors. For example, if the API returns { available: false }, your onSuccess should return { kind: 'usernameTaken' }.

Properties

NameTypeDescription
onError
r
((error: unknown, ctx: RootFieldContext<TValue>) => ValidationError | ValidationError[] | null) | undefined

Optional error handler for HTTP errors (network failures, 4xx/5xx status codes). Return null to ignore the error, or ValidationError to display it to the user.

Common pattern: Log the error and return null to avoid blocking form submission on network issues.

onSuccess
r
(result: TResult, ctx: RootFieldContext<TValue>) => TreeValidationResult

REQUIRED function to map successful HTTP response to validation errors.

Inverted Logic: This is called on successful HTTP responses. Return null if validation passes, or ValidationError/ValidationError[] if validation fails.

Example: API returns { available: false } → return { kind: 'usernameTaken' }

request
r
(ctx: RootFieldContext<TValue>, config?: Record<string, unknown> | undefined) => string | HttpResourceRequest | undefined

Function that returns the HTTP request configuration. Can return:

  • undefined to skip validation (e.g., if field is empty)
  • string for simple GET requests (just the URL)
  • HttpResourceRequest for full control (method, body, headers)

Example usage

Username Availability Check (GET)

const checkUsername: HttpCustomValidator<string> = {
  request: (ctx) => {
    const username = ctx.value();
    if (!username) return undefined; // Skip validation if empty
    return `/api/users/check-username?username=${encodeURIComponent(username)}`;
  },
  onSuccess: (response, ctx) => {
    // Inverted logic: successful response may indicate validation failure
    return response.available ? null : { kind: 'usernameTaken' };
  },
  onError: (error, ctx) => {
    console.error('Availability check failed:', error);
    return null; // Don't block form on network errors
  }
};

Address Validation (POST with Body)

const validateAddress: HttpCustomValidator = {
  request: (ctx) => {
    const zipCode = ctx.value();
    if (!zipCode) return undefined;

    return {
      url: '/api/validate-address',
      method: 'POST',
      body: {
        street: ctx.valueOf('street' as any),
        city: ctx.valueOf('city' as any),
        zipCode: zipCode
      },
      headers: { 'Content-Type': 'application/json' }
    };
  },
  onSuccess: (response) => {
    return response.valid ? null : { kind: 'invalidAddress' };
  }
};