@ng-simplicity/forms-core 1.1.4 → 1.1.5

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/README.md CHANGED
@@ -15,7 +15,7 @@ This core package is extended by styling integration packages that provide prebu
15
15
 
16
16
  | Package | Package version | Angular support |
17
17
  |---|---:|---|
18
- | `@ng-simplicity/forms-core` | `1.1.4` | `17.x`–`22.x` |
18
+ | `@ng-simplicity/forms-core` | `1.1.5` | `17.x`–`22.x` |
19
19
 
20
20
  ---
21
21
 
@@ -46,7 +46,8 @@ Each `registerAll…` helper also registers the core structural components, so i
46
46
  ### 1. The Public Boundary: `NgsFormsService`
47
47
  - Injected by the user's component page.
48
48
  - Exposes form values and statuses through Signals and observables (e.g. `formValue$`, `dirty`, `isValid`).
49
- - Provides entry methods like `setFormConfig(config)` and `patchValue(data)`.
49
+ - Provides entry methods like `setFormConfig(config)`, `patchValue(data)` and `setServerErrorMessages(errors)`.
50
+ - `patchValue(data)` can be called straight after `setFormConfig()`: it keeps the patch and re-applies it as controls render, including fields that the patched values reveal through `visible$`, until the form settles. Form arrays need enough rows (`initialItemCount`).
50
51
  - Implements a retry loading mechanism in case child elements compile asynchronously.
51
52
 
52
53
  ### 2. The Internal Coordinator: `NgsFormsInternalService`
@@ -217,6 +218,18 @@ Each form control component that extends `NgsFormsFormItemWithVisibleAndValidato
217
218
  }
218
219
  ```
219
220
  3. **Displaying Errors**: Within your custom component template, bind your error display element to the `errorMessage` property inherited from the base class. The core engine dynamically updates `errorMessage` based on whether the control is invalid, touched, dirty, or if the form has been submitted.
221
+ 4. **Server Errors**: Pass the server's errors to `NgsFormsService.setServerErrorMessages()` in the same shape as the form value. Each leaf is a key looked up in that field's `errorMessageMap` (a key with no entry is shown as text), and form array rows are matched by index. Each error clears when its field changes, and the call returns the paths that matched no control. `constructServerErrorMessageObject()` builds the object from a list of `{ jsonPath, validator }` pairs:
222
+ ```typescript
223
+ errorMessageMap: { taken: 'User Name Not Available' }
224
+
225
+ this.formsService.setServerErrorMessages({ registration: { userName: 'taken', contacts: [{}, {}, { phone: 'invalidPhone' }] } });
226
+
227
+ // Same thing from a flat list:
228
+ this.formsService.setServerErrorMessages(constructServerErrorMessageObject([
229
+ { jsonPath: 'registration.userName', validator: 'taken' },
230
+ { jsonPath: 'registration.contacts[2].phone', validator: 'invalidPhone' },
231
+ ]));
232
+ ```
220
233
 
221
234
  ---
222
235
 
@@ -2,8 +2,8 @@ import { Subject, takeUntil, BehaviorSubject, switchMap, defer, map, distinctUnt
2
2
  import * as i0 from '@angular/core';
3
3
  import { Directive, InjectionToken, inject, Injectable, ChangeDetectorRef, Injector, ViewContainerRef, Input, computed, ChangeDetectionStrategy, Component, ElementRef, afterNextRender, Sanitizer, SecurityContext, NgModule } from '@angular/core';
4
4
  import { v4 } from 'uuid';
5
+ import { FormArray, FormGroup, UntypedFormGroup, FormControl, Validators, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
5
6
  import { toSignal } from '@angular/core/rxjs-interop';
6
- import { UntypedFormGroup, FormControl, Validators, FormGroup, FormArray, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
7
7
  import { CommonModule } from '@angular/common';
8
8
 
9
9
  /** Base class that ties RxJS subscriptions to the component's lifetime. */
@@ -77,6 +77,202 @@ function ngsDefaults(target, ...sources) {
77
77
  return result;
78
78
  }
79
79
 
80
+ /**
81
+ * Builds the object for `NgsFormsService.setServerErrorMessages()` from a flat list of paths and error keys, for
82
+ * APIs that return their errors that way. Array indexes create arrays, and rows without errors are filled with `{}`.
83
+ * Two errors on the same path become an array of keys.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * constructServerErrorMessageObject([
88
+ * { jsonPath: 'registration.userName', validator: 'taken' },
89
+ * { jsonPath: 'registration.contacts[2].phone', validator: 'invalidPhone' },
90
+ * ]);
91
+ * // { registration: { userName: 'taken', contacts: [{}, {}, { phone: 'invalidPhone' }] } }
92
+ * ```
93
+ */
94
+ function constructServerErrorMessageObject(errors) {
95
+ const result = {};
96
+ for (const { jsonPath, validator } of errors) {
97
+ const segments = parseJsonPath(jsonPath);
98
+ if (!segments.length || !validator)
99
+ continue;
100
+ let node = result;
101
+ segments.forEach((segment, i) => {
102
+ const isLast = i === segments.length - 1;
103
+ if (Array.isArray(node) && typeof segment === 'number') {
104
+ while (node.length < segment)
105
+ node.push({});
106
+ }
107
+ if (isLast) {
108
+ const existing = node[segment];
109
+ if (typeof existing === 'string') {
110
+ node[segment] = existing === validator ? existing : [existing, validator];
111
+ }
112
+ else if (Array.isArray(existing) && existing.every((e) => typeof e === 'string')) {
113
+ if (!existing.includes(validator))
114
+ existing.push(validator);
115
+ }
116
+ else {
117
+ node[segment] = validator;
118
+ }
119
+ return;
120
+ }
121
+ const nextIsIndex = typeof segments[i + 1] === 'number';
122
+ const existing = node[segment];
123
+ if (existing === undefined || existing === null || typeof existing !== 'object') {
124
+ node[segment] = nextIsIndex ? [] : {};
125
+ }
126
+ else if (nextIsIndex && !Array.isArray(existing) && Object.keys(existing).length === 0) {
127
+ // A `{}` placeholder for an array row that is itself an array.
128
+ node[segment] = [];
129
+ }
130
+ node = node[segment];
131
+ });
132
+ }
133
+ return result;
134
+ }
135
+ /** Splits `a.b[2]['c d']` into `['a', 'b', 2, 'c d']`. */
136
+ function parseJsonPath(jsonPath) {
137
+ const path = (jsonPath ?? '').trim().replace(/^\$\.?/, '');
138
+ const segments = [];
139
+ const pattern = /\[\s*(\d+)\s*\]|\[\s*(['"])(.*?)\2\s*\]|([^.[\]]+)/g;
140
+ let match;
141
+ while ((match = pattern.exec(path)) !== null) {
142
+ if (match[1] !== undefined)
143
+ segments.push(Number(match[1]));
144
+ else if (match[3] !== undefined)
145
+ segments.push(match[3]);
146
+ else if (match[4] !== undefined)
147
+ segments.push(match[4]);
148
+ }
149
+ return segments;
150
+ }
151
+ /**
152
+ * Marker on the error value of a server error (`control.errors[key][NGS_FORMS_SERVER_ERROR] === true`). When the
153
+ * key has no entry in `errorMessageMap`, the key itself is shown as the message, so servers can send message text.
154
+ */
155
+ const NGS_FORMS_SERVER_ERROR = 'ngsServerError';
156
+ /**
157
+ * Applies server errors to a form and clears each one when its control's value changes.
158
+ *
159
+ * @internal Used by `NgsFormsService`.
160
+ */
161
+ class NgsFormsServerErrorRegistry {
162
+ entries = new Map();
163
+ /**
164
+ * Validator that reports a control's server errors. Form items include it whenever they set their validators,
165
+ * so the errors survive re-validation until the control's value changes.
166
+ */
167
+ validator = (control) => this.validate(control);
168
+ /** `true` while at least one server error is set. */
169
+ get hasErrors() {
170
+ return this.entries.size > 0;
171
+ }
172
+ /** Replaces every server error with `errors`. Returns the paths that matched no control. */
173
+ set(root, errors) {
174
+ this.clear();
175
+ const unmatched = [];
176
+ this.apply(root, errors, '', unmatched);
177
+ return unmatched;
178
+ }
179
+ /** Removes every server error. */
180
+ clear() {
181
+ if (!this.entries.size)
182
+ return;
183
+ const controls = Array.from(this.entries.keys());
184
+ this.entries.clear();
185
+ controls.forEach((control) => control.updateValueAndValidity());
186
+ }
187
+ /** Forgets every server error without re-validating, for when the controls are being thrown away. */
188
+ reset() {
189
+ this.entries.clear();
190
+ }
191
+ apply(control, errors, path, unmatched) {
192
+ if (errors === null || errors === undefined)
193
+ return;
194
+ if (typeof errors === 'string' || (Array.isArray(errors) && errors.length > 0 && errors.every((e) => typeof e === 'string'))) {
195
+ this.applyKeys(control, typeof errors === 'string' ? [errors] : errors);
196
+ return;
197
+ }
198
+ if (Array.isArray(errors)) {
199
+ if (!(control instanceof FormArray)) {
200
+ if (errors.some((e) => !isEmpty(e)))
201
+ unmatched.push(path);
202
+ return;
203
+ }
204
+ errors.forEach((rowErrors, index) => {
205
+ if (isEmpty(rowErrors))
206
+ return;
207
+ const row = control.at(index);
208
+ const rowPath = `${path}[${index}]`;
209
+ if (!row) {
210
+ unmatched.push(rowPath);
211
+ return;
212
+ }
213
+ this.apply(row, rowErrors, rowPath, unmatched);
214
+ });
215
+ return;
216
+ }
217
+ if (!(control instanceof FormGroup)) {
218
+ if (!isEmpty(errors))
219
+ unmatched.push(path);
220
+ return;
221
+ }
222
+ for (const [name, childErrors] of Object.entries(errors)) {
223
+ if (isEmpty(childErrors))
224
+ continue;
225
+ const childPath = path ? `${path}.${name}` : name;
226
+ const child = control.get([name]);
227
+ if (!child) {
228
+ unmatched.push(childPath);
229
+ continue;
230
+ }
231
+ this.apply(child, childErrors, childPath, unmatched);
232
+ }
233
+ }
234
+ applyKeys(control, keys) {
235
+ this.entries.set(control, { keys, value: control.value });
236
+ if (!control.hasValidator(this.validator)) {
237
+ control.addValidators(this.validator);
238
+ }
239
+ // Show the message straight away, without waiting for the user to touch the field.
240
+ control.markAsTouched();
241
+ control.updateValueAndValidity();
242
+ }
243
+ validate(control) {
244
+ const entry = this.entries.get(control);
245
+ if (!entry)
246
+ return null;
247
+ if (!sameValue(control.value, entry.value)) {
248
+ this.entries.delete(control);
249
+ return null;
250
+ }
251
+ return Object.fromEntries(entry.keys.map((key) => [key, { [NGS_FORMS_SERVER_ERROR]: true }]));
252
+ }
253
+ }
254
+ function isEmpty(errors) {
255
+ if (errors === null || errors === undefined || errors === '')
256
+ return true;
257
+ if (Array.isArray(errors))
258
+ return errors.every(isEmpty);
259
+ if (typeof errors === 'object')
260
+ return Object.values(errors).every(isEmpty);
261
+ return false;
262
+ }
263
+ function sameValue(a, b) {
264
+ if (Object.is(a, b))
265
+ return true;
266
+ if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
267
+ return false;
268
+ try {
269
+ return JSON.stringify(a) === JSON.stringify(b);
270
+ }
271
+ catch {
272
+ return false;
273
+ }
274
+ }
275
+
80
276
  /**
81
277
  * Root base class for every form item component. Reads the item's configuration from `NGS_FORMS_ITEM_DATA`.
82
278
  *
@@ -175,6 +371,14 @@ class NgsFormsService {
175
371
  lastConfig;
176
372
  missingFormTimer;
177
373
  destroyed = false;
374
+ /** Patches still being applied while the form renders, in call order. */
375
+ pendingPatches = [];
376
+ patchTimer;
377
+ /** The controls that existed when the pending patches were last applied. */
378
+ patchedStructure;
379
+ patchRounds = 0;
380
+ /** Render passes in a row in which no control was added or removed. */
381
+ stablePatchRounds = 0;
178
382
  /** `true` once the user has changed any control in the form. */
179
383
  get dirty() {
180
384
  return this.formGroup?.dirty ?? false;
@@ -198,6 +402,8 @@ class NgsFormsService {
198
402
  */
199
403
  setFormConfig(formConfig) {
200
404
  this.lastConfig = formConfig;
405
+ // Patches made for the previous form do not carry over. Patches made after this call are kept until it renders.
406
+ this.cancelPendingPatches();
201
407
  if (this.internalServiceIsSet) {
202
408
  this.internalFormService.setFormData(formConfig);
203
409
  return;
@@ -221,6 +427,9 @@ class NgsFormsService {
221
427
  if (this.lastConfig) {
222
428
  internalFormsService.setFormData(this.lastConfig);
223
429
  }
430
+ if (this.pendingPatches.length) {
431
+ this.schedulePatchRound();
432
+ }
224
433
  }
225
434
  /**
226
435
  * @internal
@@ -270,17 +479,103 @@ class NgsFormsService {
270
479
  this.destroyed = true;
271
480
  clearTimeout(this.missingFormTimer);
272
481
  this.missingFormTimer = undefined;
482
+ this.cancelPendingPatches();
273
483
  this.formGroupBinding?.unsubscribe();
274
484
  this.formValue$.complete();
275
485
  }
486
+ /**
487
+ * Shows errors from the server on the matching controls, replacing any server errors shown before.
488
+ *
489
+ * @param errors - An object shaped like the form value (including the root group name) whose leaves are error
490
+ * keys, for example `{ registration: { userName: 'taken', contacts: [{}, {}, { phone: 'invalid' }] } }`. Each key
491
+ * is looked up in that control's `errorMessageMap`; when it has no entry the key itself is shown, so the server
492
+ * can also send the message text. Use `constructServerErrorMessageObject()` to build it from a list of paths.
493
+ * @returns The paths that matched no rendered control (a hidden item or a missing array row, for example), so you
494
+ * can show those errors elsewhere.
495
+ *
496
+ * @remarks The controls are marked as touched so the messages show at once, and they stay invalid until their
497
+ * value changes. All server errors are cleared by the next call, by `clearServerErrorMessages()`, by
498
+ * `setFormConfig()`, and when rows are added to or removed from a form array, because rows are matched by index.
499
+ */
500
+ setServerErrorMessages(errors) {
501
+ if (!this.internalFormService) {
502
+ console.error('setServerErrorMessages() was called before <ngs-form> connected.');
503
+ return [];
504
+ }
505
+ return this.internalFormService.serverErrors.set(this.formGroup, errors);
506
+ }
507
+ /** Removes every error set with `setServerErrorMessages()`. */
508
+ clearServerErrorMessages() {
509
+ this.internalFormService?.serverErrors.clear();
510
+ }
276
511
  /**
277
512
  * Patches values into the form, using the same shape as `formValue$` (including the root group name).
278
513
  *
279
- * @remarks Only controls that are currently rendered receive values. Patch again after a conditional item becomes
280
- * visible, and make sure a form array has enough rows (see `initialItemCount`) before patching array values.
514
+ * @remarks It is safe to call straight after `setFormConfig()`, or before `<ngs-form>` has connected. The values
515
+ * are applied at once to the controls that exist, kept, and applied again each time new controls appear, until
516
+ * the form stops changing. That covers items that render later and items behind `visible$` that the patched values
517
+ * themselves reveal (and anything those reveal in turn). Later calls are applied after earlier ones. A form array
518
+ * only receives values for rows it has, so give it enough rows with `initialItemCount`. `setFormConfig()` drops
519
+ * patches that are still being applied to the previous form.
281
520
  */
282
521
  patchValue(entityData) {
283
- this.formGroup.patchValue(entityData);
522
+ this.pendingPatches.push(entityData);
523
+ this.applyPendingPatches();
524
+ this.patchRounds = 0;
525
+ this.stablePatchRounds = 0;
526
+ this.schedulePatchRound();
527
+ }
528
+ /** Maximum number of render passes a patch waits for new controls before it is dropped. */
529
+ static MAX_PATCH_ROUNDS = 20;
530
+ applyPendingPatches() {
531
+ for (const patch of this.pendingPatches) {
532
+ this.formGroup.patchValue(patch);
533
+ }
534
+ this.patchedStructure = this.internalServiceIsSet ? controlStructure(this.formGroup) : undefined;
535
+ }
536
+ /**
537
+ * Waits one render pass, then re-applies the pending patches if controls were added or removed. Stops once two
538
+ * passes in a row change nothing (rendering can take a pass to start), or after `MAX_PATCH_ROUNDS` passes. Waits
539
+ * without counting while no form is connected.
540
+ */
541
+ schedulePatchRound() {
542
+ clearTimeout(this.patchTimer);
543
+ if (this.destroyed || !this.pendingPatches.length)
544
+ return;
545
+ if (!this.internalServiceIsSet) {
546
+ // `setInternalService()` starts the rounds once <ngs-form> connects.
547
+ this.patchTimer = undefined;
548
+ return;
549
+ }
550
+ this.patchTimer = setTimeout(() => {
551
+ this.patchTimer = undefined;
552
+ if (this.destroyed || !this.internalServiceIsSet)
553
+ return;
554
+ const structure = controlStructure(this.formGroup);
555
+ if (structure === this.patchedStructure) {
556
+ if (++this.stablePatchRounds >= 2) {
557
+ this.cancelPendingPatches();
558
+ return;
559
+ }
560
+ this.schedulePatchRound();
561
+ return;
562
+ }
563
+ this.stablePatchRounds = 0;
564
+ this.applyPendingPatches();
565
+ if (++this.patchRounds >= NgsFormsService.MAX_PATCH_ROUNDS) {
566
+ this.cancelPendingPatches();
567
+ return;
568
+ }
569
+ this.schedulePatchRound();
570
+ });
571
+ }
572
+ cancelPendingPatches() {
573
+ clearTimeout(this.patchTimer);
574
+ this.patchTimer = undefined;
575
+ this.pendingPatches = [];
576
+ this.patchedStructure = undefined;
577
+ this.patchRounds = 0;
578
+ this.stablePatchRounds = 0;
284
579
  }
285
580
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
286
581
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsService });
@@ -288,6 +583,18 @@ class NgsFormsService {
288
583
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsService, decorators: [{
289
584
  type: Injectable
290
585
  }] });
586
+ /** A string listing the paths of every control under `control`, so a change in the form's controls can be seen. */
587
+ function controlStructure(control, path = '') {
588
+ if (control instanceof FormGroup) {
589
+ return Object.keys(control.controls)
590
+ .map((name) => controlStructure(control.controls[name], `${path}.${name}`))
591
+ .join('|') + `|${path}{}`;
592
+ }
593
+ if (control instanceof FormArray) {
594
+ return control.controls.map((row, i) => controlStructure(row, `${path}[${i}]`)).join('|') + `|${path}[]`;
595
+ }
596
+ return path;
597
+ }
291
598
 
292
599
  /**
293
600
  * Per form state holder that connects `<ngs-form>`, its items and `NgsFormsService`.
@@ -300,6 +607,8 @@ class NgsFormsInternalService {
300
607
  formConfig$ = new BehaviorSubject(undefined);
301
608
  isSubmitted$ = new BehaviorSubject(false);
302
609
  state = new BehaviorSubject({});
610
+ /** Server errors set with `NgsFormsService.setServerErrorMessages()`. */
611
+ serverErrors = new NgsFormsServerErrorRegistry();
303
612
  setIsSubmitted(isSubmitted) {
304
613
  this.isSubmitted$.next(isSubmitted);
305
614
  }
@@ -314,6 +623,7 @@ class NgsFormsInternalService {
314
623
  const state = { ...(formConfig.initialState ?? {}) };
315
624
  state['global'] = { ...(formConfig.globalState ?? {}) };
316
625
  this.state.next(state);
626
+ this.serverErrors.reset();
317
627
  this.formGroup$.next(new UntypedFormGroup({}));
318
628
  this.formConfig$.next(formConfig);
319
629
  this.isSubmitted$.next(false);
@@ -421,25 +731,32 @@ class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassForm
421
731
  }
422
732
  const firstErrorKey = Object.keys(errors)[0];
423
733
  const map = this.config.errorMessageMap;
424
- this.errorMessage = map?.[firstErrorKey] || `Field is invalid: ${firstErrorKey}`;
734
+ const isServerError = errors[firstErrorKey]?.[NGS_FORMS_SERVER_ERROR] === true;
735
+ // A server error without a mapped message shows the server's text.
736
+ this.errorMessage = map?.[firstErrorKey] || (isServerError ? firstErrorKey : `Field is invalid: ${firstErrorKey}`);
425
737
  }
426
738
  ngOnDestroy() {
427
739
  super.ngOnDestroy();
428
740
  this.formAddRemoveFns.remove(this.control, this.config.name);
429
741
  }
430
742
  bindValidators() {
431
- if (!this.config.validators && !this.config.validators$)
432
- return;
433
743
  const control = this.control;
744
+ // The server error validator is always kept, so server errors survive until the control's value changes.
745
+ // Optional so that tests which stub `NgsFormsInternalService` keep working.
746
+ const serverValidators = this.privateService.serverErrors ? [this.privateService.serverErrors.validator] : [];
747
+ if (!this.config.validators && !this.config.validators$) {
748
+ control.addValidators(serverValidators);
749
+ return;
750
+ }
434
751
  if (this.config.validators) {
435
- control.setValidators(this.config.validators);
752
+ control.setValidators([...this.config.validators, ...serverValidators]);
436
753
  // A group or array computed its status when it was created, before these validators were set.
437
754
  control.updateValueAndValidity({ emitEvent: false });
438
755
  return;
439
756
  }
440
757
  if (this.config.validators$) {
441
758
  this.subscribe(this.config.validators$, (validators) => {
442
- control.setValidators(validators);
759
+ control.setValidators([...validators, ...serverValidators]);
443
760
  control.updateValueAndValidity();
444
761
  });
445
762
  }
@@ -861,6 +1178,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
861
1178
  class NgsFormsFormArrayInternalService {
862
1179
  config = inject(NGS_FORMS_ITEM_DATA).config;
863
1180
  formArray = new UntypedFormArray([]);
1181
+ formsInternalService = inject(NgsFormsInternalService, { optional: true });
864
1182
  /** Emits after a row is added or removed. */
865
1183
  itemsChanged = new Subject();
866
1184
  /** Emits the index of a row right after it is removed. */
@@ -884,6 +1202,8 @@ class NgsFormsFormArrayInternalService {
884
1202
  if (index < 0 || index >= this.formArray.controls.length) {
885
1203
  return;
886
1204
  }
1205
+ // Server errors for rows are matched by index, so they are cleared once the rows move.
1206
+ this.formsInternalService?.serverErrors?.clear();
887
1207
  this.formArray.removeAt(index);
888
1208
  this.rowRemoved.next(index);
889
1209
  this.itemsChanged.next();
@@ -893,6 +1213,7 @@ class NgsFormsFormArrayInternalService {
893
1213
  if (this.config?.maxItems && this.formArray.controls.length >= this.config.maxItems) {
894
1214
  return;
895
1215
  }
1216
+ this.formsInternalService?.serverErrors?.clear();
896
1217
  this.formArray.push(new UntypedFormGroup({}));
897
1218
  this.itemsChanged.next();
898
1219
  }
@@ -1230,5 +1551,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
1230
1551
  * Generated bundle index. Do not edit.
1231
1552
  */
1232
1553
 
1233
- export { NGS_FORMS_ARRAY_ROW, NGS_FORMS_CONTROL_ADD_REMOVE_FN, NGS_FORMS_ITEM_DATA, NGS_FORMS_ITEM_INDEX, NgsFormComponent, NgsFormsBaseClassFormComponent, NgsFormsBaseClassFormInputComponent, NgsFormsBaseClassFormItemInputWithOptionsComponent, NgsFormsBaseClassItemsContainerBase, NgsFormsColumnComponent, NgsFormsComponentRegistryService, NgsFormsCoreModule, NgsFormsFormArrayAddItemComponent, NgsFormsFormArrayContainerComponent, NgsFormsFormArrayInternalService, NgsFormsFormArrayListComponent, NgsFormsFormArrayRemoveItemComponent, NgsFormsFormControlAddRemoveFunctions, NgsFormsFormGroupComponent, NgsFormsFormItemDirective, NgsFormsFormItemWithVisibleAndValidatorsBase, NgsFormsFormSectionComponent, NgsFormsHtmlContentComponent, NgsFormsInternalService, NgsFormsRowComponent, NgsFormsService, NgsFormsTextDivComponent, NgsSubscriber, ngsDefaults, sectionConfigDefaults };
1554
+ export { NGS_FORMS_ARRAY_ROW, NGS_FORMS_CONTROL_ADD_REMOVE_FN, NGS_FORMS_ITEM_DATA, NGS_FORMS_ITEM_INDEX, NGS_FORMS_SERVER_ERROR, NgsFormComponent, NgsFormsBaseClassFormComponent, NgsFormsBaseClassFormInputComponent, NgsFormsBaseClassFormItemInputWithOptionsComponent, NgsFormsBaseClassItemsContainerBase, NgsFormsColumnComponent, NgsFormsComponentRegistryService, NgsFormsCoreModule, NgsFormsFormArrayAddItemComponent, NgsFormsFormArrayContainerComponent, NgsFormsFormArrayInternalService, NgsFormsFormArrayListComponent, NgsFormsFormArrayRemoveItemComponent, NgsFormsFormControlAddRemoveFunctions, NgsFormsFormGroupComponent, NgsFormsFormItemDirective, NgsFormsFormItemWithVisibleAndValidatorsBase, NgsFormsFormSectionComponent, NgsFormsHtmlContentComponent, NgsFormsInternalService, NgsFormsRowComponent, NgsFormsServerErrorRegistry, NgsFormsService, NgsFormsTextDivComponent, NgsSubscriber, constructServerErrorMessageObject, ngsDefaults, sectionConfigDefaults };
1234
1555
  //# sourceMappingURL=ng-simplicity-forms-core.mjs.map