mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(forms): type built-in getError results
Add overloads for built-in validation error keys so callers get precise error payload types from getError.
This enables signal forms template patterns like:
```html
@if (login.getError('minLength'); as minLengthError) {
<div>Login should be {{ minLengthError.minLength }} characters</div>
}
```
(cherry picked from commit 45e8fb5d6c)
This commit is contained in:
committed by
Pawel Kozlowski
parent
e3e25b5a53
commit
e81c7e8466
@@ -11,6 +11,7 @@ import { FormControlStatus } from '@angular/forms';
|
||||
import * as i0 from '@angular/core';
|
||||
import { Injector } from '@angular/core';
|
||||
import { Signal } from '@angular/core';
|
||||
import { StandardSchemaV1 } from '@standard-schema/spec';
|
||||
import { WritableSignal } from '@angular/core';
|
||||
|
||||
// @public
|
||||
|
||||
@@ -138,6 +138,10 @@ export type FieldContext<TValue, TPathKind extends PathKind = PathKind.Root> = T
|
||||
export interface FieldState<TValue, TKey extends string | number = string | number> extends ReadonlyFieldState<TValue, TKey> {
|
||||
readonly controlValue: WritableSignal<TValue>;
|
||||
readonly fieldTree: FieldTree<unknown, TKey>;
|
||||
getError<K extends NgValidationError['kind']>(kind: K): (Extract<NgValidationError, {
|
||||
kind: K;
|
||||
}> & ValidationError.WithFieldTree) | undefined;
|
||||
// (undocumented)
|
||||
getError(kind: string): ValidationError.WithFieldTree | undefined;
|
||||
markAsDirty(): void;
|
||||
markAsTouched(options?: MarkAsTouchedOptions): void;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import {Injector, Signal, WritableSignal} from '@angular/core';
|
||||
import {AbstractControl} from '@angular/forms';
|
||||
import type {FormField} from '../directive/form_field';
|
||||
import type {MetadataKey, ValidationError} from './rules';
|
||||
import type {MetadataKey, NgValidationError, ValidationError} from './rules';
|
||||
|
||||
/**
|
||||
* Symbol used to retain generic type information when it would otherwise be lost.
|
||||
@@ -545,6 +545,9 @@ export interface FieldState<
|
||||
* @param kind The kind of error (e.g. 'required', 'min').
|
||||
* @returns The first matching error, or `undefined` if none.
|
||||
*/
|
||||
getError<K extends NgValidationError['kind']>(
|
||||
kind: K,
|
||||
): (Extract<NgValidationError, {kind: K}> & ValidationError.WithFieldTree) | undefined;
|
||||
getError(kind: string): ValidationError.WithFieldTree | undefined;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
REQUIRED,
|
||||
IS_ASYNC_VALIDATION_RESOURCE,
|
||||
} from '../api/rules/metadata';
|
||||
import type {ValidationError} from '../api/rules/validation/validation_errors';
|
||||
import type {NgValidationError, ValidationError} from '../api/rules/validation/validation_errors';
|
||||
import type {
|
||||
DisabledReason,
|
||||
FieldContext,
|
||||
@@ -258,6 +258,10 @@ export class FieldNode implements FieldState<unknown> {
|
||||
return this.metadataState.get(key);
|
||||
}
|
||||
|
||||
getError<K extends NgValidationError['kind']>(
|
||||
kind: K,
|
||||
): (Extract<NgValidationError, {kind: K}> & ValidationError.WithFieldTree) | undefined;
|
||||
getError(kind: string): ValidationError.WithFieldTree | undefined;
|
||||
getError(kind: string): ValidationError.WithFieldTree | undefined {
|
||||
return this.errors().find((e) => e.kind === kind);
|
||||
}
|
||||
|
||||
@@ -14,17 +14,28 @@ import {
|
||||
applyEach,
|
||||
debounce,
|
||||
disabled,
|
||||
EmailValidationError,
|
||||
form,
|
||||
FormField,
|
||||
hidden,
|
||||
MaxDateValidationError,
|
||||
MaxLengthValidationError,
|
||||
MaxValidationError,
|
||||
MinDateValidationError,
|
||||
MinLengthValidationError,
|
||||
MinValidationError,
|
||||
NativeInputParseError,
|
||||
PatternValidationError,
|
||||
readonly,
|
||||
required,
|
||||
requiredError,
|
||||
RequiredValidationError,
|
||||
Schema,
|
||||
schema,
|
||||
SchemaOrSchemaFn,
|
||||
SchemaPath,
|
||||
SchemaPathTree,
|
||||
StandardSchemaValidationError,
|
||||
validate,
|
||||
validateTree,
|
||||
ValidationError,
|
||||
@@ -79,6 +90,60 @@ describe('FieldNode', () => {
|
||||
expect(f.c).toBeUndefined();
|
||||
});
|
||||
|
||||
it('infers built-in error types from getError', () => {
|
||||
const f = form(signal({a: 1}), {injector: TestBed.inject(Injector)});
|
||||
const field = f.a();
|
||||
|
||||
{
|
||||
const error = field.getError('required');
|
||||
let t: (RequiredValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('min');
|
||||
let t: (MinValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('minDate');
|
||||
let t: (MinDateValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('max');
|
||||
let t: (MaxValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('maxDate');
|
||||
let t: (MaxDateValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('minLength');
|
||||
let t: (MinLengthValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('maxLength');
|
||||
let t: (MaxLengthValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('pattern');
|
||||
let t: (PatternValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('email');
|
||||
let t: (EmailValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('standardSchema');
|
||||
let t: (StandardSchemaValidationError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('parse');
|
||||
let t: (NativeInputParseError & ValidationError.WithFieldTree) | undefined = error;
|
||||
}
|
||||
{
|
||||
const error = field.getError('custom');
|
||||
let t: ValidationError.WithFieldTree | undefined = error;
|
||||
}
|
||||
});
|
||||
|
||||
it('can get a child inside of a computed', () => {
|
||||
const f = form(
|
||||
signal({
|
||||
|
||||
Reference in New Issue
Block a user