ngx-api-forms 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +657 -0
- package/analog/index.d.ts +76 -0
- package/django/index.d.ts +56 -0
- package/express-validator/index.d.ts +60 -0
- package/fesm2022/ngx-api-forms-analog.mjs +195 -0
- package/fesm2022/ngx-api-forms-analog.mjs.map +1 -0
- package/fesm2022/ngx-api-forms-django.mjs +173 -0
- package/fesm2022/ngx-api-forms-django.mjs.map +1 -0
- package/fesm2022/ngx-api-forms-express-validator.mjs +202 -0
- package/fesm2022/ngx-api-forms-express-validator.mjs.map +1 -0
- package/fesm2022/ngx-api-forms-laravel.mjs +167 -0
- package/fesm2022/ngx-api-forms-laravel.mjs.map +1 -0
- package/fesm2022/ngx-api-forms-zod.mjs +226 -0
- package/fesm2022/ngx-api-forms-zod.mjs.map +1 -0
- package/fesm2022/ngx-api-forms.mjs +890 -0
- package/fesm2022/ngx-api-forms.mjs.map +1 -0
- package/index.d.ts +621 -0
- package/laravel/index.d.ts +49 -0
- package/package.json +85 -0
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.js +300 -0
- package/schematics/ng-add/index.spec.js +119 -0
- package/schematics/ng-add/schema.json +34 -0
- package/zod/index.d.ts +58 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
import * as _angular_forms from '@angular/forms';
|
|
2
|
+
import { FormGroup, ValidationErrors } from '@angular/forms';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Signal, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
|
5
|
+
import { HttpErrorResponse, HttpInterceptorFn, HttpContext, HttpContextToken } from '@angular/common/http';
|
|
6
|
+
import { Observable } from 'rxjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ngx-api-forms - Models & Interfaces
|
|
10
|
+
*
|
|
11
|
+
* Core types for bridging API validation errors to Angular Reactive Forms.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A single field-level validation error returned by the API.
|
|
16
|
+
*/
|
|
17
|
+
interface ApiFieldError {
|
|
18
|
+
/** The form control name / field property name */
|
|
19
|
+
field: string;
|
|
20
|
+
/** The validation constraint key (e.g. 'required', 'minLength', 'isEmail') */
|
|
21
|
+
constraint: string;
|
|
22
|
+
/** The human-readable error message */
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Raw error shape from class-validator / NestJS (ValidationPipe).
|
|
27
|
+
* This is the standard shape when `exceptionFactory` is not customized.
|
|
28
|
+
*/
|
|
29
|
+
interface ClassValidatorError {
|
|
30
|
+
property: string;
|
|
31
|
+
constraints?: Record<string, string>;
|
|
32
|
+
children?: ClassValidatorError[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Raw error shape from Laravel validation.
|
|
36
|
+
* Laravel returns `{ errors: { field: ['message1', 'message2'] } }`.
|
|
37
|
+
*/
|
|
38
|
+
type LaravelValidationErrors = Record<string, string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Raw error shape from Django REST Framework.
|
|
41
|
+
* DRF returns `{ field: ['message1', 'message2'] }`.
|
|
42
|
+
*/
|
|
43
|
+
type DjangoValidationErrors = Record<string, string[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Raw error shape from Zod (.flatten()).
|
|
46
|
+
* Zod returns `{ fieldErrors: { field: ['message1'] } }`.
|
|
47
|
+
*/
|
|
48
|
+
interface ZodFlatError {
|
|
49
|
+
formErrors: string[];
|
|
50
|
+
fieldErrors: Record<string, string[]>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sentinel field name used by presets to tag errors that are not bound to a
|
|
54
|
+
* specific form control (e.g. Django `non_field_errors`, Zod `formErrors`).
|
|
55
|
+
*
|
|
56
|
+
* FormBridge also routes any ApiFieldError whose field does not match a control
|
|
57
|
+
* to `globalErrorsSignal`, so custom presets do not need to use this constant -
|
|
58
|
+
* unmatched fields are captured automatically.
|
|
59
|
+
*/
|
|
60
|
+
declare const GLOBAL_ERROR_FIELD = "__global__";
|
|
61
|
+
/**
|
|
62
|
+
* An error that does not belong to any specific form control.
|
|
63
|
+
*
|
|
64
|
+
* Sources:
|
|
65
|
+
* - Django `non_field_errors` / `detail`
|
|
66
|
+
* - Zod `formErrors`
|
|
67
|
+
* - Any API error whose field name does not match a control in the form
|
|
68
|
+
*/
|
|
69
|
+
interface GlobalError {
|
|
70
|
+
/** The human-readable error message */
|
|
71
|
+
message: string;
|
|
72
|
+
/** The constraint key (e.g. 'serverError') */
|
|
73
|
+
constraint: string;
|
|
74
|
+
/** The original field name from the API response, if any */
|
|
75
|
+
originalField?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* An ErrorPreset knows how to parse a specific API error shape
|
|
79
|
+
* into a normalized array of `ApiFieldError`.
|
|
80
|
+
*
|
|
81
|
+
* This is the extension point for supporting any backend.
|
|
82
|
+
*/
|
|
83
|
+
interface ErrorPreset {
|
|
84
|
+
/** Unique name for identification (e.g. 'class-validator', 'laravel') */
|
|
85
|
+
readonly name: string;
|
|
86
|
+
/**
|
|
87
|
+
* Parse the raw API error body into normalized field errors.
|
|
88
|
+
* Should return an empty array if the format is not recognized.
|
|
89
|
+
*/
|
|
90
|
+
parse(error: unknown): ApiFieldError[];
|
|
91
|
+
/**
|
|
92
|
+
* Optional constraint map shipped with this preset.
|
|
93
|
+
* When provided, FormBridge merges it into its constraint resolution table
|
|
94
|
+
* automatically - no need to pass a `constraintMap` in the config.
|
|
95
|
+
*/
|
|
96
|
+
readonly constraintMap?: ConstraintMap;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Maps a constraint key to an Angular validation error key.
|
|
100
|
+
* For example: `{ isEmail: 'email', minLength: 'minlength' }`
|
|
101
|
+
*/
|
|
102
|
+
type ConstraintMap = Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* Configuration for i18n error message resolution.
|
|
105
|
+
*/
|
|
106
|
+
interface I18nConfig {
|
|
107
|
+
/**
|
|
108
|
+
* Prefix for i18n translation keys.
|
|
109
|
+
* Example: 'forms.errors' → key becomes 'forms.errors.email.required'
|
|
110
|
+
*/
|
|
111
|
+
prefix?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Custom resolver function for translating error keys.
|
|
114
|
+
* If provided, this is called instead of using a prefix.
|
|
115
|
+
*
|
|
116
|
+
* @param field - The form field name
|
|
117
|
+
* @param constraint - The constraint key
|
|
118
|
+
* @param message - The original API message
|
|
119
|
+
* @returns The translated message, or null to use the original
|
|
120
|
+
*/
|
|
121
|
+
resolver?: (field: string, constraint: string, message: string) => string | null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Configuration options for creating a FormBridge instance.
|
|
125
|
+
*/
|
|
126
|
+
interface FormBridgeConfig {
|
|
127
|
+
/**
|
|
128
|
+
* The error preset to use for parsing API errors.
|
|
129
|
+
* Can be a single preset or an array (tried in order).
|
|
130
|
+
*/
|
|
131
|
+
preset?: ErrorPreset | ErrorPreset[];
|
|
132
|
+
/**
|
|
133
|
+
* Custom constraint-to-Angular-error mapping.
|
|
134
|
+
* Overrides the defaults from the preset.
|
|
135
|
+
* Example: `{ isEmail: 'email', isNotEmpty: 'required' }`
|
|
136
|
+
*/
|
|
137
|
+
constraintMap?: ConstraintMap;
|
|
138
|
+
/**
|
|
139
|
+
* i18n configuration for error messages.
|
|
140
|
+
*/
|
|
141
|
+
i18n?: I18nConfig;
|
|
142
|
+
/**
|
|
143
|
+
* When true, unmapped API errors are set as `{ generic: message }` on the control.
|
|
144
|
+
* When false (default), unmapped errors are ignored.
|
|
145
|
+
*/
|
|
146
|
+
catchAll?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* When true, existing validation errors on the form are preserved
|
|
149
|
+
* when applying API errors (merged). When false (default), API errors
|
|
150
|
+
* replace existing errors on affected controls.
|
|
151
|
+
*/
|
|
152
|
+
mergeErrors?: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* When true, logs warnings to the console when:
|
|
155
|
+
* - No preset produces results for a given error payload.
|
|
156
|
+
* - A parsed error field does not match any form control.
|
|
157
|
+
*
|
|
158
|
+
* Useful during development. Should be disabled in production.
|
|
159
|
+
*/
|
|
160
|
+
debug?: boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Represents a resolved error on a specific form field.
|
|
164
|
+
*/
|
|
165
|
+
interface ResolvedFieldError {
|
|
166
|
+
/** The form control name */
|
|
167
|
+
field: string;
|
|
168
|
+
/** The Angular validation error key (e.g. 'required', 'email') */
|
|
169
|
+
errorKey: string;
|
|
170
|
+
/** The error message */
|
|
171
|
+
message: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Return type for `getFirstError()`.
|
|
175
|
+
*/
|
|
176
|
+
interface FirstError {
|
|
177
|
+
field: string;
|
|
178
|
+
errorKey: string;
|
|
179
|
+
message: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Options for `enableForm()`.
|
|
183
|
+
*/
|
|
184
|
+
interface EnableFormOptions {
|
|
185
|
+
/** Control names to exclude from enabling */
|
|
186
|
+
except?: string[];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Options for `disableForm()`.
|
|
190
|
+
*/
|
|
191
|
+
interface DisableFormOptions {
|
|
192
|
+
/** Control names to exclude from disabling */
|
|
193
|
+
except?: string[];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Extract control names from a typed FormGroup.
|
|
197
|
+
*/
|
|
198
|
+
type FormControlNames<T extends FormGroup> = T extends FormGroup<infer C> ? Extract<keyof C, string> : string;
|
|
199
|
+
/**
|
|
200
|
+
* Signature for a custom error handler that can intercept
|
|
201
|
+
* errors before they are applied to the form.
|
|
202
|
+
*/
|
|
203
|
+
type ErrorInterceptor = (errors: ApiFieldError[], form: FormGroup) => ApiFieldError[];
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* FormBridge - The core class of ngx-api-forms.
|
|
207
|
+
*
|
|
208
|
+
* Bridges API validation errors to Angular Reactive Forms with:
|
|
209
|
+
* - Automatic error parsing via presets (class-validator, Laravel, Django, Zod)
|
|
210
|
+
* - i18n-friendly error messages
|
|
211
|
+
* - Angular Signals support
|
|
212
|
+
* - SSR-safe operations
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* FormBridge wraps an Angular FormGroup and provides a clean API
|
|
217
|
+
* for bridging API validation errors and managing form state.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const bridge = new FormBridge(myForm, {
|
|
222
|
+
* preset: classValidatorPreset(),
|
|
223
|
+
* });
|
|
224
|
+
*
|
|
225
|
+
* // Apply API errors
|
|
226
|
+
* bridge.applyApiErrors(err.error);
|
|
227
|
+
*
|
|
228
|
+
* // Use signals in templates
|
|
229
|
+
* readonly errors = bridge.errorsSignal;
|
|
230
|
+
* readonly firstError = bridge.firstErrorSignal;
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare class FormBridge<T extends FormGroup = FormGroup> {
|
|
234
|
+
private readonly _form;
|
|
235
|
+
private readonly _presets;
|
|
236
|
+
private readonly _constraintMap;
|
|
237
|
+
private readonly _i18n;
|
|
238
|
+
private readonly _catchAll;
|
|
239
|
+
private readonly _mergeErrors;
|
|
240
|
+
private readonly _debug;
|
|
241
|
+
private _interceptors;
|
|
242
|
+
/** Signal containing the last set of resolved API errors */
|
|
243
|
+
private readonly _apiErrors;
|
|
244
|
+
/** Signal containing global (non-field) errors */
|
|
245
|
+
private readonly _globalErrors;
|
|
246
|
+
/** Tracks which error keys were set by the API on each control */
|
|
247
|
+
private _apiErrorKeys;
|
|
248
|
+
/** Reactive signal of all current API errors applied to the form */
|
|
249
|
+
readonly errorsSignal: Signal<ResolvedFieldError[]>;
|
|
250
|
+
/**
|
|
251
|
+
* Reactive signal of global (non-field) errors.
|
|
252
|
+
*
|
|
253
|
+
* Contains errors from:
|
|
254
|
+
* - Django `non_field_errors` / `detail`
|
|
255
|
+
* - Zod `formErrors`
|
|
256
|
+
* - Any API error whose field does not match a form control
|
|
257
|
+
*/
|
|
258
|
+
readonly globalErrorsSignal: Signal<GlobalError[]>;
|
|
259
|
+
/** Reactive signal of the first error (or null) */
|
|
260
|
+
readonly firstErrorSignal: Signal<FirstError | null>;
|
|
261
|
+
/** Whether any API errors are currently applied */
|
|
262
|
+
readonly hasErrorsSignal: Signal<boolean>;
|
|
263
|
+
constructor(form: T, config?: FormBridgeConfig);
|
|
264
|
+
/**
|
|
265
|
+
* Parse and apply API validation errors to the form.
|
|
266
|
+
*
|
|
267
|
+
* Tries each configured preset in order until one returns results.
|
|
268
|
+
* Errors are mapped to Angular form control errors.
|
|
269
|
+
*
|
|
270
|
+
* @param apiError - The raw error body from the API (e.g. `err.error`)
|
|
271
|
+
* @returns The array of resolved field errors that were applied
|
|
272
|
+
*/
|
|
273
|
+
applyApiErrors(apiError: unknown): ResolvedFieldError[];
|
|
274
|
+
/**
|
|
275
|
+
* Clear only the errors that were set by `applyApiErrors()`.
|
|
276
|
+
* Client-side validation errors (e.g. `Validators.required`) are preserved.
|
|
277
|
+
*/
|
|
278
|
+
clearApiErrors(): void;
|
|
279
|
+
/**
|
|
280
|
+
* Get the first error across all form controls.
|
|
281
|
+
*/
|
|
282
|
+
getFirstError(): FirstError | null;
|
|
283
|
+
/**
|
|
284
|
+
* Get all errors for a specific field.
|
|
285
|
+
*/
|
|
286
|
+
getFieldErrors(fieldName: string): ValidationErrors | null;
|
|
287
|
+
/**
|
|
288
|
+
* Register an error interceptor that can modify or filter errors
|
|
289
|
+
* before they are applied to the form.
|
|
290
|
+
* Returns a dispose function to remove the interceptor.
|
|
291
|
+
*/
|
|
292
|
+
addInterceptor(interceptor: ErrorInterceptor): () => void;
|
|
293
|
+
/**
|
|
294
|
+
* Access the underlying FormGroup.
|
|
295
|
+
*/
|
|
296
|
+
get form(): T;
|
|
297
|
+
private _resolvePresetConstraintMap;
|
|
298
|
+
private _applyErrors;
|
|
299
|
+
private _resolveNestedControl;
|
|
300
|
+
private _resolveErrorKey;
|
|
301
|
+
private _resolveMessage;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Factory function to create a FormBridge instance.
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* import { createFormBridge, classValidatorPreset } from 'ngx-api-forms';
|
|
309
|
+
*
|
|
310
|
+
* const bridge = createFormBridge(this.myForm, {
|
|
311
|
+
* preset: classValidatorPreset(),
|
|
312
|
+
* i18n: { prefix: 'validation' },
|
|
313
|
+
* });
|
|
314
|
+
*
|
|
315
|
+
* // In your API call error handler:
|
|
316
|
+
* bridge.applyApiErrors(err.error);
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
declare function createFormBridge<T extends FormGroup>(form: T, config: FormBridgeConfig): FormBridge<T>;
|
|
320
|
+
declare function createFormBridge<T extends FormGroup>(form: T): FormBridge<T>;
|
|
321
|
+
/**
|
|
322
|
+
* Alias for `createFormBridge`.
|
|
323
|
+
*
|
|
324
|
+
* Both functions are identical since FormBridge no longer holds internal
|
|
325
|
+
* subscriptions. Kept for API compatibility with existing code.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* @Component({ ... })
|
|
330
|
+
* export class MyComponent {
|
|
331
|
+
* private form = inject(FormBuilder).group({ email: [''] });
|
|
332
|
+
* private bridge = provideFormBridge(this.form, {
|
|
333
|
+
* preset: classValidatorPreset(),
|
|
334
|
+
* });
|
|
335
|
+
*
|
|
336
|
+
* onSubmit() {
|
|
337
|
+
* this.http.post('/api', this.form.value).subscribe({
|
|
338
|
+
* error: (err) => this.bridge.applyApiErrors(err.error),
|
|
339
|
+
* });
|
|
340
|
+
* }
|
|
341
|
+
* }
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
declare function provideFormBridge<T extends FormGroup>(form: T, config: FormBridgeConfig): FormBridge<T>;
|
|
345
|
+
declare function provideFormBridge<T extends FormGroup>(form: T): FormBridge<T>;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* class-validator / NestJS error preset.
|
|
349
|
+
*
|
|
350
|
+
* Parses the standard ValidationPipe error format:
|
|
351
|
+
* ```json
|
|
352
|
+
* {
|
|
353
|
+
* "statusCode": 400,
|
|
354
|
+
* "message": [
|
|
355
|
+
* { "property": "email", "constraints": { "isEmail": "email must be an email" } }
|
|
356
|
+
* ],
|
|
357
|
+
* "error": "Bad Request"
|
|
358
|
+
* }
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* Also handles flat string messages from NestJS:
|
|
362
|
+
* ```json
|
|
363
|
+
* { "statusCode": 400, "message": "email is already used" }
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Creates a class-validator / NestJS error preset.
|
|
369
|
+
*
|
|
370
|
+
* @param options.noInference - When true, skips constraint guessing for string messages.
|
|
371
|
+
* Structured `constraints` objects (e.g. `{ isEmail: 'message' }`) are always used as-is.
|
|
372
|
+
* Only affects the fallback string message parser.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```typescript
|
|
376
|
+
* import { classValidatorPreset } from 'ngx-api-forms';
|
|
377
|
+
*
|
|
378
|
+
* const bridge = createFormBridge(form, { preset: classValidatorPreset() });
|
|
379
|
+
*
|
|
380
|
+
* // No inference on string messages
|
|
381
|
+
* const bridge = createFormBridge(form, { preset: classValidatorPreset({ noInference: true }) });
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
declare function classValidatorPreset(options?: {
|
|
385
|
+
noInference?: boolean;
|
|
386
|
+
}): ErrorPreset;
|
|
387
|
+
/**
|
|
388
|
+
* Default constraint map for class-validator.
|
|
389
|
+
* Maps class-validator constraint names to Angular form error keys.
|
|
390
|
+
*/
|
|
391
|
+
declare const CLASS_VALIDATOR_CONSTRAINT_MAP: Record<string, string>;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* NgxFormError directive.
|
|
395
|
+
*
|
|
396
|
+
* Automatically displays the error message for a form control.
|
|
397
|
+
* Works with both client-side validators and API errors set by FormBridge.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```html
|
|
401
|
+
* <input formControlName="email" />
|
|
402
|
+
* <span ngxFormError="email" [form]="myForm"></span>
|
|
403
|
+
* <!-- Displays: "email must be a valid email" -->
|
|
404
|
+
*
|
|
405
|
+
* <!-- With custom error map -->
|
|
406
|
+
* <span ngxFormError="email"
|
|
407
|
+
* [form]="myForm"
|
|
408
|
+
* [errorMessages]="{ required: 'Email is required', email: 'Invalid email' }">
|
|
409
|
+
* </span>
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
declare class NgxFormErrorDirective implements OnInit, OnChanges {
|
|
414
|
+
private readonly el;
|
|
415
|
+
private readonly renderer;
|
|
416
|
+
private readonly destroyRef;
|
|
417
|
+
private readonly resubscribe$;
|
|
418
|
+
/** The form control name to observe */
|
|
419
|
+
controlName: string;
|
|
420
|
+
/** The parent FormGroup */
|
|
421
|
+
form: FormGroup;
|
|
422
|
+
/**
|
|
423
|
+
* Optional map of error keys to display messages.
|
|
424
|
+
* If not provided, the raw error value is used (which is the API message
|
|
425
|
+
* when set by FormBridge).
|
|
426
|
+
*/
|
|
427
|
+
errorMessages?: Record<string, string>;
|
|
428
|
+
/**
|
|
429
|
+
* CSS class applied to the host element when an error is displayed.
|
|
430
|
+
* Default: 'ngx-form-error-visible'
|
|
431
|
+
*/
|
|
432
|
+
errorClass: string;
|
|
433
|
+
/**
|
|
434
|
+
* Whether to show errors only when the control is touched.
|
|
435
|
+
* Default: true
|
|
436
|
+
*/
|
|
437
|
+
showOnTouched: boolean;
|
|
438
|
+
ngOnInit(): void;
|
|
439
|
+
ngOnChanges(changes: SimpleChanges): void;
|
|
440
|
+
private _subscribe;
|
|
441
|
+
private _updateDisplay;
|
|
442
|
+
private _show;
|
|
443
|
+
private _hide;
|
|
444
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NgxFormErrorDirective, never>;
|
|
445
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<NgxFormErrorDirective, "[ngxFormError]", never, { "controlName": { "alias": "ngxFormError"; "required": false; }; "form": { "alias": "form"; "required": false; }; "errorMessages": { "alias": "errorMessages"; "required": false; }; "errorClass": { "alias": "errorClass"; "required": false; }; "showOnTouched": { "alias": "showOnTouched"; "required": false; }; }, {}, never, never, true, never>;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* HttpContext token that carries a FormBridge reference on a request.
|
|
450
|
+
* Used by `withFormBridge()` to tag requests for automatic error handling.
|
|
451
|
+
*/
|
|
452
|
+
declare const FORM_BRIDGE: HttpContextToken<FormBridge<_angular_forms.FormGroup<any>> | null>;
|
|
453
|
+
/**
|
|
454
|
+
* Configuration for `apiErrorInterceptor`.
|
|
455
|
+
*/
|
|
456
|
+
interface ApiErrorInterceptorConfig {
|
|
457
|
+
/**
|
|
458
|
+
* Preset(s) used for standalone parsing when no FormBridge is attached
|
|
459
|
+
* but `onError` is provided. Ignored when a FormBridge is attached
|
|
460
|
+
* (the bridge uses its own presets).
|
|
461
|
+
* Defaults to `classValidatorPreset()`.
|
|
462
|
+
*/
|
|
463
|
+
preset?: ErrorPreset | ErrorPreset[];
|
|
464
|
+
/**
|
|
465
|
+
* HTTP status codes that trigger error handling.
|
|
466
|
+
* Defaults to `[422, 400]`.
|
|
467
|
+
*/
|
|
468
|
+
statusCodes?: number[];
|
|
469
|
+
/**
|
|
470
|
+
* Global callback invoked for every matching error response,
|
|
471
|
+
* whether or not a FormBridge is attached.
|
|
472
|
+
* Receives the parsed field errors and the raw HttpErrorResponse.
|
|
473
|
+
* When a FormBridge is attached, receives ResolvedFieldError[];
|
|
474
|
+
* otherwise receives ApiFieldError[] from standalone parsing.
|
|
475
|
+
*/
|
|
476
|
+
onError?: (errors: (ApiFieldError | ResolvedFieldError)[], response: HttpErrorResponse) => void;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Create an Angular functional HTTP interceptor that handles API validation errors.
|
|
480
|
+
*
|
|
481
|
+
* When a response matches one of the configured status codes (default: 422, 400):
|
|
482
|
+
* - If the request was tagged with `withFormBridge(bridge)`, errors are automatically
|
|
483
|
+
* applied to that bridge. Zero code needed in `subscribe()`.
|
|
484
|
+
* - If an `onError` callback is provided, it is called with the parsed errors for
|
|
485
|
+
* global handling (store, toast, logging, etc.).
|
|
486
|
+
* - The error is always re-thrown so downstream `catchError` or `error` callbacks
|
|
487
|
+
* still fire when needed.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* // app.config.ts
|
|
492
|
+
* import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
493
|
+
* import { apiErrorInterceptor } from 'ngx-api-forms';
|
|
494
|
+
*
|
|
495
|
+
* export const appConfig = {
|
|
496
|
+
* providers: [
|
|
497
|
+
* provideHttpClient(
|
|
498
|
+
* withInterceptors([apiErrorInterceptor()])
|
|
499
|
+
* ),
|
|
500
|
+
* ],
|
|
501
|
+
* };
|
|
502
|
+
*
|
|
503
|
+
* // component.ts -- zero error handling code
|
|
504
|
+
* this.http.post('/api/save', data, withFormBridge(this.bridge)).subscribe({
|
|
505
|
+
* next: () => this.router.navigate(['/done']),
|
|
506
|
+
* });
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
declare function apiErrorInterceptor(config?: ApiErrorInterceptorConfig): HttpInterceptorFn;
|
|
510
|
+
/**
|
|
511
|
+
* Attach a FormBridge to an HTTP request via HttpContext.
|
|
512
|
+
*
|
|
513
|
+
* When used with `apiErrorInterceptor`, the interceptor automatically calls
|
|
514
|
+
* `bridge.applyApiErrors()` on 422/400 responses. No error handling needed
|
|
515
|
+
* in `subscribe()`.
|
|
516
|
+
*
|
|
517
|
+
* @param bridge - The FormBridge instance to receive API errors
|
|
518
|
+
* @returns An options object to spread into HttpClient methods
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* ```typescript
|
|
522
|
+
* // Errors are applied automatically -- no subscribe error handler needed
|
|
523
|
+
* this.http.post('/api/save', data, withFormBridge(this.bridge)).subscribe();
|
|
524
|
+
*
|
|
525
|
+
* // Works with all HttpClient methods
|
|
526
|
+
* this.http.put('/api/users/1', data, withFormBridge(this.bridge)).subscribe();
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
declare function withFormBridge(bridge: FormBridge): {
|
|
530
|
+
context: HttpContext;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Standalone utility functions for common form operations.
|
|
535
|
+
* These are tree-shakeable and don't require DI.
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Parse raw API error body into normalized field errors without touching any form.
|
|
540
|
+
* Tries each preset in order until one returns results.
|
|
541
|
+
*
|
|
542
|
+
* Use this when you only need the parsing logic (e.g. in an HttpInterceptor,
|
|
543
|
+
* a store effect, or any context where you don't have a FormBridge).
|
|
544
|
+
*
|
|
545
|
+
* @param apiError - The raw error body from the API (e.g. `err.error`)
|
|
546
|
+
* @param preset - One or more presets to try. Defaults to class-validator.
|
|
547
|
+
* @param options - Optional settings. `debug: true` logs a warning when no preset matches.
|
|
548
|
+
* @returns Normalized array of field errors
|
|
549
|
+
*
|
|
550
|
+
* @example
|
|
551
|
+
* ```typescript
|
|
552
|
+
* import { parseApiErrors } from 'ngx-api-forms';
|
|
553
|
+
* import { laravelPreset } from 'ngx-api-forms/laravel';
|
|
554
|
+
*
|
|
555
|
+
* const errors = parseApiErrors(err.error, laravelPreset());
|
|
556
|
+
* // [{ field: 'email', constraint: 'required', message: 'The email field is required.' }]
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
declare function parseApiErrors(apiError: unknown, preset?: ErrorPreset | ErrorPreset[], options?: {
|
|
560
|
+
debug?: boolean;
|
|
561
|
+
}): ApiFieldError[];
|
|
562
|
+
/**
|
|
563
|
+
* Wrap an Observable with form submit lifecycle management.
|
|
564
|
+
*
|
|
565
|
+
* Standalone submit helper. Useful when you want disable/enable lifecycle
|
|
566
|
+
* without a full FormBridge instance.
|
|
567
|
+
*
|
|
568
|
+
* - Disables the form before subscribing
|
|
569
|
+
* - Re-enables the form on success or error
|
|
570
|
+
* - Returns the original Observable (errors are re-thrown)
|
|
571
|
+
*
|
|
572
|
+
* @param form - The FormGroup to manage
|
|
573
|
+
* @param source - The Observable to wrap (e.g. HttpClient call)
|
|
574
|
+
* @param options.onError - Callback invoked with the error before re-throwing
|
|
575
|
+
* @returns The wrapped Observable
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* ```typescript
|
|
579
|
+
* import { wrapSubmit } from 'ngx-api-forms';
|
|
580
|
+
*
|
|
581
|
+
* wrapSubmit(this.form, this.http.post('/api', data), {
|
|
582
|
+
* onError: (err) => this.bridge.applyApiErrors(err.error),
|
|
583
|
+
* }).subscribe();
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
declare function wrapSubmit<T>(form: FormGroup, source: Observable<T>, options?: {
|
|
587
|
+
onError?: (err: unknown) => void;
|
|
588
|
+
}): Observable<T>;
|
|
589
|
+
/**
|
|
590
|
+
* Convert a plain object to FormData.
|
|
591
|
+
* Handles Files, Blobs, Arrays, Dates, nested objects.
|
|
592
|
+
*/
|
|
593
|
+
declare function toFormData(data: Record<string, unknown>): FormData;
|
|
594
|
+
/**
|
|
595
|
+
* Enable all controls in a form, with optional exceptions.
|
|
596
|
+
*/
|
|
597
|
+
declare function enableForm(form: FormGroup, options?: EnableFormOptions): void;
|
|
598
|
+
/**
|
|
599
|
+
* Disable all controls in a form, with optional exceptions.
|
|
600
|
+
*/
|
|
601
|
+
declare function disableForm(form: FormGroup, options?: DisableFormOptions): void;
|
|
602
|
+
/**
|
|
603
|
+
* Reset all errors on a form's controls (client-side and API).
|
|
604
|
+
*/
|
|
605
|
+
declare function clearFormErrors(form: FormGroup): void;
|
|
606
|
+
/**
|
|
607
|
+
* Get a flat record of only the dirty (changed) fields.
|
|
608
|
+
*/
|
|
609
|
+
declare function getDirtyValues(form: FormGroup): Record<string, unknown>;
|
|
610
|
+
/**
|
|
611
|
+
* Check if any control in the form has a specific error key.
|
|
612
|
+
*/
|
|
613
|
+
declare function hasError(form: FormGroup, errorKey: string): boolean;
|
|
614
|
+
/**
|
|
615
|
+
* Get the error message for a specific field and error key.
|
|
616
|
+
* Returns the error value if it's a string, otherwise the error key.
|
|
617
|
+
*/
|
|
618
|
+
declare function getErrorMessage(form: FormGroup, fieldName: string, errorKey?: string): string | null;
|
|
619
|
+
|
|
620
|
+
export { CLASS_VALIDATOR_CONSTRAINT_MAP, FORM_BRIDGE, FormBridge, GLOBAL_ERROR_FIELD, NgxFormErrorDirective, apiErrorInterceptor, classValidatorPreset, clearFormErrors, createFormBridge, disableForm, enableForm, getDirtyValues, getErrorMessage, hasError, parseApiErrors, provideFormBridge, toFormData, withFormBridge, wrapSubmit };
|
|
621
|
+
export type { ApiErrorInterceptorConfig, ApiFieldError, ClassValidatorError, ConstraintMap, DisableFormOptions, DjangoValidationErrors, EnableFormOptions, ErrorInterceptor, ErrorPreset, FirstError, FormBridgeConfig, FormControlNames, GlobalError, I18nConfig, LaravelValidationErrors, ResolvedFieldError, ZodFlatError };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ErrorPreset, ConstraintMap } from 'ngx-api-forms';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Laravel validation error preset.
|
|
5
|
+
*
|
|
6
|
+
* Parses the standard Laravel validation error format:
|
|
7
|
+
* ```json
|
|
8
|
+
* {
|
|
9
|
+
* "message": "The given data was invalid.",
|
|
10
|
+
* "errors": {
|
|
11
|
+
* "email": ["The email field is required.", "The email must be a valid email address."],
|
|
12
|
+
* "name": ["The name must be at least 3 characters."]
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Default constraint map for Laravel.
|
|
20
|
+
*/
|
|
21
|
+
declare const LARAVEL_CONSTRAINT_MAP: ConstraintMap;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a Laravel validation error preset.
|
|
24
|
+
*
|
|
25
|
+
* @param options.noInference - When true, skips English-language constraint guessing.
|
|
26
|
+
* The raw error message is used directly and the constraint is set to `'serverError'`.
|
|
27
|
+
* Use this when your backend returns translated or custom messages.
|
|
28
|
+
* @param options.constraintPatterns - Custom regex patterns for constraint inference.
|
|
29
|
+
* Keys are constraint names, values are RegExp tested against the raw message.
|
|
30
|
+
* Checked before the built-in English patterns.
|
|
31
|
+
* Example: `{ required: /obligatoire/i, email: /courriel.*invalide/i }`
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { laravelPreset } from 'ngx-api-forms/laravel';
|
|
36
|
+
*
|
|
37
|
+
* // Default: infer constraint from English messages
|
|
38
|
+
* const bridge = createFormBridge(form, { preset: laravelPreset() });
|
|
39
|
+
*
|
|
40
|
+
* // No inference: raw messages, no guessing
|
|
41
|
+
* const bridge = createFormBridge(form, { preset: laravelPreset({ noInference: true }) });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function laravelPreset(options?: {
|
|
45
|
+
noInference?: boolean;
|
|
46
|
+
constraintPatterns?: Record<string, RegExp>;
|
|
47
|
+
}): ErrorPreset;
|
|
48
|
+
|
|
49
|
+
export { LARAVEL_CONSTRAINT_MAP, laravelPreset };
|