@ng-simplicity/forms-core 1.1.3 → 1.1.4
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 +10 -2
- package/fesm2022/ng-simplicity-forms-core.mjs +230 -66
- package/fesm2022/ng-simplicity-forms-core.mjs.map +1 -1
- package/index.d.ts +115 -39
- package/package.json +1 -1
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.
|
|
18
|
+
| `@ng-simplicity/forms-core` | `1.1.4` | `17.x`–`22.x` |
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -397,10 +397,17 @@ import { NgsFormsFormArrayRemoveItemComponent } from '@ng-simplicity/forms-core'
|
|
|
397
397
|
const removeContactBtn = NgsFormsFormArrayRemoveItemComponent.create({
|
|
398
398
|
buttonText: 'Delete',
|
|
399
399
|
buttonClass: 'btn btn-danger',
|
|
400
|
-
buttonIcon: 'trash-icon'
|
|
400
|
+
buttonIcon: 'trash-icon',
|
|
401
|
+
// Optional accessible name. Defaults to `${buttonText} item ${index + 1}`, for example "Delete item 2".
|
|
402
|
+
ariaLabel: (index) => `Delete contact ${index + 1}`,
|
|
401
403
|
});
|
|
402
404
|
```
|
|
403
405
|
|
|
406
|
+
The button always removes its own row, even after earlier rows have been removed. When the removed row held the focus, focus moves to the Remove button of the row that takes its place. Custom components inside a row can inject `NGS_FORMS_ARRAY_ROW` (the row's `UntypedFormGroup`) and use `formArray.controls.indexOf(row)` to find the row's current index; `NGS_FORMS_ITEM_INDEX` is the index the row had when it was created.
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
```
|
|
410
|
+
|
|
404
411
|
### 5. Section (`section`)
|
|
405
412
|
A visual layout grouping with a title and subtitle.
|
|
406
413
|
- **Component**: `NgsFormsFormSectionComponent`
|
|
@@ -413,6 +420,7 @@ import { NgsFormsFormSectionComponent } from '@ng-simplicity/forms-core';
|
|
|
413
420
|
const personalSection = NgsFormsFormSectionComponent.create({
|
|
414
421
|
title: 'Personal Information',
|
|
415
422
|
titleClass: 'custom-title-class', // Optional, defaults to 'h3'
|
|
423
|
+
headingLevel: 2, // Optional heading level announced by screen readers, defaults to 3
|
|
416
424
|
subtitle: 'Tell us a bit about yourself',
|
|
417
425
|
subtitleClass: 'custom-subtitle-class', // Optional, defaults to 'lead'
|
|
418
426
|
items: [
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Subject, takeUntil, BehaviorSubject, map,
|
|
1
|
+
import { Subject, takeUntil, BehaviorSubject, switchMap, defer, map, distinctUntilChanged, merge, of } from 'rxjs';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { Directive, InjectionToken, inject, Injectable, ChangeDetectorRef, Injector, ViewContainerRef, Input, computed, ChangeDetectionStrategy, Component, ElementRef, Sanitizer, SecurityContext, NgModule } from '@angular/core';
|
|
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
5
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { UntypedFormGroup, FormControl, FormGroup, FormArray, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
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. */
|
|
@@ -32,8 +32,18 @@ const NGS_FORMS_ITEM_DATA = new InjectionToken('ngs-forms-item-data');
|
|
|
32
32
|
* @internal
|
|
33
33
|
*/
|
|
34
34
|
const NGS_FORMS_CONTROL_ADD_REMOVE_FN = new InjectionToken('ngs-form-control-remove-fn');
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Injection token for the row index when a component is rendered inside a form array list.
|
|
37
|
+
*
|
|
38
|
+
* @remarks This is the index the row had when the component was created. Rows before it can be removed later,
|
|
39
|
+
* so to find a row's current position use `NGS_FORMS_ARRAY_ROW` with `formArray.controls.indexOf(row)`.
|
|
40
|
+
*/
|
|
36
41
|
const NGS_FORMS_ITEM_INDEX = new InjectionToken('ngs-forms-item-index');
|
|
42
|
+
/**
|
|
43
|
+
* Injection token for the `UntypedFormGroup` of the form array row a component is rendered in. Unlike
|
|
44
|
+
* `NGS_FORMS_ITEM_INDEX`, it stays correct when earlier rows are removed.
|
|
45
|
+
*/
|
|
46
|
+
const NGS_FORMS_ARRAY_ROW = new InjectionToken('ngs-forms-array-row');
|
|
37
47
|
/**
|
|
38
48
|
* @internal
|
|
39
49
|
*/
|
|
@@ -43,10 +53,12 @@ const NgsFormsFormControlAddRemoveFunctions = {
|
|
|
43
53
|
remove: (control, controlName) => formGroup.removeControl(controlName),
|
|
44
54
|
}),
|
|
45
55
|
formArray: (formArray) => ({
|
|
46
|
-
add: (control
|
|
47
|
-
remove: (control
|
|
56
|
+
add: (control) => formArray.push(control),
|
|
57
|
+
remove: (control) => {
|
|
48
58
|
const index = formArray.controls.indexOf(control);
|
|
49
|
-
|
|
59
|
+
if (index >= 0) {
|
|
60
|
+
formArray.removeAt(index);
|
|
61
|
+
}
|
|
50
62
|
},
|
|
51
63
|
}),
|
|
52
64
|
};
|
|
@@ -148,10 +160,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
148
160
|
* ```
|
|
149
161
|
*/
|
|
150
162
|
class NgsFormsService {
|
|
151
|
-
/**
|
|
152
|
-
* @internal
|
|
153
|
-
*/
|
|
154
|
-
attempt = 0;
|
|
155
163
|
/**
|
|
156
164
|
* Emits the root form group's value every time it changes. Combine with `toSignal()` to read it in a template.
|
|
157
165
|
*
|
|
@@ -161,8 +169,12 @@ class NgsFormsService {
|
|
|
161
169
|
/** The latest value emitted by `formValue$`. */
|
|
162
170
|
formValue = {};
|
|
163
171
|
internalFormService;
|
|
164
|
-
|
|
172
|
+
formGroupBinding;
|
|
165
173
|
formGroup = new UntypedFormGroup({});
|
|
174
|
+
/** The config from the latest `setFormConfig()` call, re-applied when `<ngs-form>` (re)connects. */
|
|
175
|
+
lastConfig;
|
|
176
|
+
missingFormTimer;
|
|
177
|
+
destroyed = false;
|
|
166
178
|
/** `true` once the user has changed any control in the form. */
|
|
167
179
|
get dirty() {
|
|
168
180
|
return this.formGroup?.dirty ?? false;
|
|
@@ -180,27 +192,48 @@ class NgsFormsService {
|
|
|
180
192
|
/**
|
|
181
193
|
* Renders a form from `formConfig`, replacing any form that is already shown and resetting the submitted state.
|
|
182
194
|
*
|
|
183
|
-
* @remarks It is safe to call before `<ngs-form>` has initialised (for example in `ngOnInit`): the
|
|
184
|
-
*
|
|
195
|
+
* @remarks It is safe to call before `<ngs-form>` has initialised (for example in `ngOnInit`): the latest config
|
|
196
|
+
* is kept and applied as soon as the form component connects, and also when it is destroyed and created again
|
|
197
|
+
* (inside an `@if` or a tab, for example). An error is logged if no form has connected after half a second.
|
|
185
198
|
*/
|
|
186
199
|
setFormConfig(formConfig) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
this.attempt++;
|
|
193
|
-
setTimeout(() => this.setFormConfig(formConfig), 25);
|
|
200
|
+
this.lastConfig = formConfig;
|
|
201
|
+
if (this.internalServiceIsSet) {
|
|
202
|
+
this.internalFormService.setFormData(formConfig);
|
|
194
203
|
return;
|
|
195
204
|
}
|
|
196
|
-
this.
|
|
205
|
+
if (this.missingFormTimer === undefined && !this.destroyed) {
|
|
206
|
+
this.missingFormTimer = setTimeout(() => {
|
|
207
|
+
this.missingFormTimer = undefined;
|
|
208
|
+
if (!this.internalServiceIsSet && !this.destroyed) {
|
|
209
|
+
console.error('Internal form service is unavailable. Is the ngs-form root tag added to the page?');
|
|
210
|
+
}
|
|
211
|
+
}, 500);
|
|
212
|
+
}
|
|
197
213
|
}
|
|
198
214
|
/**
|
|
199
215
|
* @internal
|
|
200
216
|
*/
|
|
201
217
|
setInternalService(internalFormsService) {
|
|
218
|
+
this.formGroupBinding?.unsubscribe();
|
|
202
219
|
this.internalFormService = internalFormsService;
|
|
203
220
|
this.bindFormGroup();
|
|
221
|
+
if (this.lastConfig) {
|
|
222
|
+
internalFormsService.setFormData(this.lastConfig);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* @internal
|
|
227
|
+
* Disconnects `internalFormsService` when its `<ngs-form>` is destroyed. Does nothing if another form has
|
|
228
|
+
* connected since.
|
|
229
|
+
*/
|
|
230
|
+
clearInternalService(internalFormsService) {
|
|
231
|
+
if (this.internalFormService !== internalFormsService)
|
|
232
|
+
return;
|
|
233
|
+
this.formGroupBinding?.unsubscribe();
|
|
234
|
+
this.formGroupBinding = undefined;
|
|
235
|
+
this.internalFormService = undefined;
|
|
236
|
+
this.formGroup = new UntypedFormGroup({});
|
|
204
237
|
}
|
|
205
238
|
/**
|
|
206
239
|
* Marks the form as submitted (or not). While submitted, every invalid control shows its error message even if
|
|
@@ -212,13 +245,14 @@ class NgsFormsService {
|
|
|
212
245
|
this.internalFormService.setIsSubmitted(isSubmitted);
|
|
213
246
|
}
|
|
214
247
|
bindFormGroup() {
|
|
215
|
-
this.internalFormService.formGroup
|
|
248
|
+
this.formGroupBinding = this.internalFormService.formGroup$
|
|
249
|
+
.pipe(switchMap((fg) => {
|
|
216
250
|
this.formGroup = fg;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
251
|
+
return fg.valueChanges;
|
|
252
|
+
}))
|
|
253
|
+
.subscribe((v) => {
|
|
254
|
+
this.formValue = v;
|
|
255
|
+
this.formValue$.next(v);
|
|
222
256
|
});
|
|
223
257
|
}
|
|
224
258
|
/**
|
|
@@ -233,7 +267,11 @@ class NgsFormsService {
|
|
|
233
267
|
this.internalFormService?.updateGlobalState(state);
|
|
234
268
|
}
|
|
235
269
|
ngOnDestroy() {
|
|
236
|
-
this.
|
|
270
|
+
this.destroyed = true;
|
|
271
|
+
clearTimeout(this.missingFormTimer);
|
|
272
|
+
this.missingFormTimer = undefined;
|
|
273
|
+
this.formGroupBinding?.unsubscribe();
|
|
274
|
+
this.formValue$.complete();
|
|
237
275
|
}
|
|
238
276
|
/**
|
|
239
277
|
* Patches values into the form, using the same shape as `formValue$` (including the root group name).
|
|
@@ -265,10 +303,12 @@ class NgsFormsInternalService {
|
|
|
265
303
|
setIsSubmitted(isSubmitted) {
|
|
266
304
|
this.isSubmitted$.next(isSubmitted);
|
|
267
305
|
}
|
|
306
|
+
/** `true` when no rendered control fails its validators. A form whose controls are all disabled counts as valid. */
|
|
268
307
|
checkIsValid() {
|
|
269
|
-
|
|
308
|
+
const formGroup = this.formGroup$.value;
|
|
309
|
+
if (!formGroup)
|
|
270
310
|
return false;
|
|
271
|
-
return
|
|
311
|
+
return formGroup.valid || formGroup.disabled === true;
|
|
272
312
|
}
|
|
273
313
|
setFormData(formConfig) {
|
|
274
314
|
const state = { ...(formConfig.initialState ?? {}) };
|
|
@@ -302,18 +342,13 @@ class NgsFormsInternalService {
|
|
|
302
342
|
*/
|
|
303
343
|
subscribeToState(uuid, name, initialState) {
|
|
304
344
|
this.seedComponentState(uuid, initialState);
|
|
305
|
-
|
|
306
|
-
return this.state.pipe(map((allState) => {
|
|
345
|
+
// `defer` gives every subscriber its own comparison, so each one receives the current state first.
|
|
346
|
+
return defer(() => this.state.pipe(map((allState) => {
|
|
307
347
|
const stateByUuid = allState[uuid] || {};
|
|
308
348
|
const stateByName = name ? (allState[name] || {}) : {};
|
|
309
349
|
const globalState = allState['global'] || {};
|
|
310
350
|
return ngsDefaults(stateByUuid, ngsDefaults(stateByName, globalState));
|
|
311
|
-
}),
|
|
312
|
-
const stringifiedState = JSON.stringify(currentState);
|
|
313
|
-
const isUpdated = stringifiedState !== last;
|
|
314
|
-
last = stringifiedState;
|
|
315
|
-
return isUpdated;
|
|
316
|
-
}));
|
|
351
|
+
}), distinctUntilChanged((previous, current) => JSON.stringify(previous) === JSON.stringify(current))));
|
|
317
352
|
}
|
|
318
353
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsInternalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
319
354
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsInternalService });
|
|
@@ -326,6 +361,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
326
361
|
* Base class for items that own a form control. Creates the control (seeded with `value` when the config has one),
|
|
327
362
|
* applies `validators` / `validators$` and `disabled` / `disabled$`, registers it with the parent group under
|
|
328
363
|
* `config.name` and keeps `errorMessage` up to date.
|
|
364
|
+
*
|
|
365
|
+
* @typeParam T - The item's config type.
|
|
366
|
+
* @typeParam C - The type of `control`. Inputs use the default `UntypedFormControl`; groups and arrays set it in
|
|
367
|
+
* their constructor to an `UntypedFormGroup` or `UntypedFormArray`.
|
|
329
368
|
*/
|
|
330
369
|
class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassFormComponent {
|
|
331
370
|
/** The item's form control. Bind it with `[formControl]`. */
|
|
@@ -347,15 +386,24 @@ class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassForm
|
|
|
347
386
|
const initialValue = this.config.value;
|
|
348
387
|
this.control = new FormControl(initialValue, { validators: [] });
|
|
349
388
|
}
|
|
389
|
+
const control = this.control;
|
|
350
390
|
this.bindValidators();
|
|
351
|
-
this.formAddRemoveFns.add(
|
|
391
|
+
this.formAddRemoveFns.add(control, this.config.name);
|
|
352
392
|
this.bindDisabled();
|
|
353
|
-
//
|
|
354
|
-
|
|
393
|
+
// Recompute the error message on value, status and touched changes, and when the form is submitted.
|
|
394
|
+
// `control.events` (Angular 18+) includes touched changes, so blurring an untouched field shows its error.
|
|
395
|
+
// Angular 17 has no `events`, so there the message updates on value and status changes only.
|
|
396
|
+
const controlEvents$ = control.events ?? merge(control.valueChanges, control.statusChanges);
|
|
397
|
+
this.subscribe(merge(controlEvents$, this.privateService.isSubmitted$), () => {
|
|
355
398
|
this.updateErrorMessage();
|
|
356
399
|
this.changeDetectorRef.markForCheck();
|
|
357
400
|
});
|
|
358
401
|
}
|
|
402
|
+
/** Whether the control currently has `Validators.required` or `Validators.requiredTrue`. */
|
|
403
|
+
get isRequired() {
|
|
404
|
+
const control = this.control;
|
|
405
|
+
return !!control && (control.hasValidator(Validators.required) || control.hasValidator(Validators.requiredTrue));
|
|
406
|
+
}
|
|
359
407
|
updateErrorMessage() {
|
|
360
408
|
if (!this.control) {
|
|
361
409
|
this.errorMessage = '';
|
|
@@ -382,12 +430,18 @@ class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassForm
|
|
|
382
430
|
bindValidators() {
|
|
383
431
|
if (!this.config.validators && !this.config.validators$)
|
|
384
432
|
return;
|
|
433
|
+
const control = this.control;
|
|
385
434
|
if (this.config.validators) {
|
|
386
|
-
|
|
435
|
+
control.setValidators(this.config.validators);
|
|
436
|
+
// A group or array computed its status when it was created, before these validators were set.
|
|
437
|
+
control.updateValueAndValidity({ emitEvent: false });
|
|
387
438
|
return;
|
|
388
439
|
}
|
|
389
440
|
if (this.config.validators$) {
|
|
390
|
-
this.subscribe(this.config.validators$, (validators) =>
|
|
441
|
+
this.subscribe(this.config.validators$, (validators) => {
|
|
442
|
+
control.setValidators(validators);
|
|
443
|
+
control.updateValueAndValidity();
|
|
444
|
+
});
|
|
391
445
|
}
|
|
392
446
|
}
|
|
393
447
|
bindDisabled() {
|
|
@@ -399,14 +453,21 @@ class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassForm
|
|
|
399
453
|
this.disabledFromConfig = !!disabled;
|
|
400
454
|
this.applyDisabled(control);
|
|
401
455
|
});
|
|
456
|
+
const reapply = () => {
|
|
457
|
+
if (this.disabledFromConfig && control.enabled) {
|
|
458
|
+
control.disable();
|
|
459
|
+
}
|
|
460
|
+
};
|
|
402
461
|
// Child controls are added to groups and arrays after the container has been disabled, and adding an enabled
|
|
403
462
|
// child re-enables its parent. Re-apply the configured state whenever a container's status changes.
|
|
404
463
|
if (control instanceof FormGroup || control instanceof FormArray) {
|
|
405
|
-
this.subscribe(control.statusChanges,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
464
|
+
this.subscribe(control.statusChanges, reapply);
|
|
465
|
+
}
|
|
466
|
+
// Enabling a parent group or array enables every child in it. The parent emits its status after its children
|
|
467
|
+
// are enabled, so re-apply the configured state then. (Doing it on the child's own status change would be undone
|
|
468
|
+
// by the end of the child's `enable()`, which re-enables the bound DOM element.)
|
|
469
|
+
if (control.parent) {
|
|
470
|
+
this.subscribe(control.parent.statusChanges, reapply);
|
|
410
471
|
}
|
|
411
472
|
}
|
|
412
473
|
applyDisabled(control) {
|
|
@@ -485,10 +546,13 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
485
546
|
addRemoveControlFn;
|
|
486
547
|
index;
|
|
487
548
|
changeDetectorRef;
|
|
549
|
+
arrayRow;
|
|
488
550
|
componentRef;
|
|
489
|
-
parentVisibility = new BehaviorSubject(false);
|
|
490
551
|
lastVisible;
|
|
491
|
-
|
|
552
|
+
/** Incremented whenever a scheduled create becomes stale (hidden again, or destroyed). */
|
|
553
|
+
createToken = 0;
|
|
554
|
+
destroyed = false;
|
|
555
|
+
constructor(injector, viewContainerRef, itemData, addRemoveControlFn, index, changeDetectorRef, arrayRow) {
|
|
492
556
|
super();
|
|
493
557
|
this.injector = injector;
|
|
494
558
|
this.viewContainerRef = viewContainerRef;
|
|
@@ -496,9 +560,12 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
496
560
|
this.addRemoveControlFn = addRemoveControlFn;
|
|
497
561
|
this.index = index;
|
|
498
562
|
this.changeDetectorRef = changeDetectorRef;
|
|
563
|
+
this.arrayRow = arrayRow;
|
|
499
564
|
this.bindVisibility();
|
|
500
565
|
}
|
|
501
566
|
destroy() {
|
|
567
|
+
this.destroyed = true;
|
|
568
|
+
this.createToken++;
|
|
502
569
|
this.detach();
|
|
503
570
|
super.ngOnDestroy();
|
|
504
571
|
}
|
|
@@ -506,7 +573,7 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
506
573
|
const config = this.itemData.config;
|
|
507
574
|
const visible$ = config.visible$ ?? (config.visible !== undefined ? of(config.visible) : undefined);
|
|
508
575
|
if (!visible$) {
|
|
509
|
-
|
|
576
|
+
this.scheduleCreate();
|
|
510
577
|
return;
|
|
511
578
|
}
|
|
512
579
|
this.subscribe(visible$, (visible) => {
|
|
@@ -514,17 +581,37 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
514
581
|
return;
|
|
515
582
|
this.lastVisible = visible;
|
|
516
583
|
if (visible) {
|
|
517
|
-
|
|
584
|
+
this.scheduleCreate();
|
|
518
585
|
return;
|
|
519
586
|
}
|
|
587
|
+
// Cancels a create that was scheduled but has not run yet.
|
|
588
|
+
this.createToken++;
|
|
520
589
|
this.detach();
|
|
521
590
|
});
|
|
522
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Creates the component in a microtask. A create that is overtaken by a later visibility change or by
|
|
594
|
+
* `destroy()` before it runs is dropped.
|
|
595
|
+
*/
|
|
596
|
+
scheduleCreate() {
|
|
597
|
+
const token = ++this.createToken;
|
|
598
|
+
Promise.resolve().then(() => {
|
|
599
|
+
if (this.destroyed || token !== this.createToken)
|
|
600
|
+
return;
|
|
601
|
+
this.create();
|
|
602
|
+
});
|
|
603
|
+
}
|
|
523
604
|
create() {
|
|
605
|
+
if (this.componentRef && !this.componentRef.hostView.destroyed) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
524
608
|
const providers = [{ provide: NGS_FORMS_ITEM_DATA, useValue: this.itemData }];
|
|
525
609
|
if (this.index !== undefined) {
|
|
526
610
|
providers.push({ provide: NGS_FORMS_ITEM_INDEX, useValue: this.index });
|
|
527
611
|
}
|
|
612
|
+
if (this.arrayRow) {
|
|
613
|
+
providers.push({ provide: NGS_FORMS_ARRAY_ROW, useValue: this.arrayRow });
|
|
614
|
+
}
|
|
528
615
|
if (this.addRemoveControlFn) {
|
|
529
616
|
providers.push({ provide: NGS_FORMS_CONTROL_ADD_REMOVE_FN, useValue: this.addRemoveControlFn });
|
|
530
617
|
}
|
|
@@ -540,11 +627,10 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
540
627
|
this.componentRef = this.viewContainerRef.createComponent(componentType, { injector: myInjector });
|
|
541
628
|
this.componentRef.changeDetectorRef?.detectChanges();
|
|
542
629
|
this.changeDetectorRef?.markForCheck();
|
|
543
|
-
this.parentVisibility.next(true);
|
|
544
630
|
}
|
|
545
631
|
detach() {
|
|
546
|
-
this.parentVisibility.next(false);
|
|
547
632
|
this.componentRef?.destroy();
|
|
633
|
+
this.componentRef = undefined;
|
|
548
634
|
this.changeDetectorRef?.markForCheck();
|
|
549
635
|
}
|
|
550
636
|
}
|
|
@@ -563,7 +649,8 @@ class NgsFormsFormItemDirective {
|
|
|
563
649
|
/** Form array that the item's control is added to. */
|
|
564
650
|
formArray;
|
|
565
651
|
/**
|
|
566
|
-
* Index of the item inside a form array row, provided to children as `NGS_FORMS_ITEM_INDEX`.
|
|
652
|
+
* Index of the item inside a form array row, provided to children as `NGS_FORMS_ITEM_INDEX`. When it is set, the
|
|
653
|
+
* `ngs-form-group` is also provided to children as `NGS_FORMS_ARRAY_ROW`.
|
|
567
654
|
*/
|
|
568
655
|
index;
|
|
569
656
|
controller;
|
|
@@ -571,7 +658,24 @@ class NgsFormsFormItemDirective {
|
|
|
571
658
|
injector = inject(Injector);
|
|
572
659
|
changeDetectorRef = inject(ChangeDetectorRef);
|
|
573
660
|
formComponentRegistryService = inject(NgsFormsComponentRegistryService);
|
|
661
|
+
initialized = false;
|
|
574
662
|
ngOnInit() {
|
|
663
|
+
this.initialized = true;
|
|
664
|
+
this.build();
|
|
665
|
+
}
|
|
666
|
+
ngOnChanges(changes) {
|
|
667
|
+
if (!this.initialized) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
// A new item, group or array means a different control, so the component is rebuilt. An index change on its own
|
|
671
|
+
// keeps the component: the row it belongs to is still the same, and `NGS_FORMS_ARRAY_ROW` follows it.
|
|
672
|
+
if (changes['itemData'] || changes['formGroup'] || changes['formArray']) {
|
|
673
|
+
this.controller?.destroy();
|
|
674
|
+
this.controller = undefined;
|
|
675
|
+
this.build();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
build() {
|
|
575
679
|
if (!this.itemData) {
|
|
576
680
|
return console.error('Form item without form data');
|
|
577
681
|
}
|
|
@@ -582,13 +686,13 @@ class NgsFormsFormItemDirective {
|
|
|
582
686
|
if (this.formGroup) {
|
|
583
687
|
addRemoveFn = NgsFormsFormControlAddRemoveFunctions.formGroup(this.formGroup);
|
|
584
688
|
}
|
|
585
|
-
this.controller = new NgsFormItemController(this.injector, this.viewContainerRef, this.itemData, addRemoveFn, this.index, this.changeDetectorRef);
|
|
689
|
+
this.controller = new NgsFormItemController(this.injector, this.viewContainerRef, this.itemData, addRemoveFn, this.index, this.changeDetectorRef, this.index !== undefined ? this.formGroup : undefined);
|
|
586
690
|
}
|
|
587
691
|
ngOnDestroy() {
|
|
588
692
|
this.controller?.destroy();
|
|
589
693
|
}
|
|
590
694
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
591
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsFormItemDirective, isStandalone: true, selector: "[ngs-form-item]", inputs: { itemData: ["ngs-form-item", "itemData"], formGroup: ["ngs-form-group", "formGroup"], formArray: ["ngs-form-array", "formArray"], index: ["ngs-form-index", "index"] }, ngImport: i0 });
|
|
695
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsFormItemDirective, isStandalone: true, selector: "[ngs-form-item]", inputs: { itemData: ["ngs-form-item", "itemData"], formGroup: ["ngs-form-group", "formGroup"], formArray: ["ngs-form-array", "formArray"], index: ["ngs-form-index", "index"] }, usesOnChanges: true, ngImport: i0 });
|
|
592
696
|
}
|
|
593
697
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormItemDirective, decorators: [{
|
|
594
698
|
type: Directive,
|
|
@@ -633,6 +737,9 @@ class NgsFormComponent {
|
|
|
633
737
|
constructor() {
|
|
634
738
|
this.ngsFormsService.setInternalService(this.ngsInternalFormsService);
|
|
635
739
|
}
|
|
740
|
+
ngOnDestroy() {
|
|
741
|
+
this.ngsFormsService.clearInternalService(this.ngsInternalFormsService);
|
|
742
|
+
}
|
|
636
743
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
637
744
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormComponent, isStandalone: true, selector: "ngs-form", providers: [NgsFormsInternalService], ngImport: i0, template: "<!-- Tracking by form group re-creates the tree whenever setFormConfig() is called again, since each call creates a new group. -->\n@for (entry of rendered(); track entry.group) {\n <ng-container [ngs-form-group]=\"entry.group\" [ngs-form-item]=\"entry.root\"></ng-container>\n}\n", dependencies: [{ kind: "directive", type: NgsFormsFormItemDirective, selector: "[ngs-form-item]", inputs: ["ngs-form-item", "ngs-form-group", "ngs-form-array", "ngs-form-index"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
638
745
|
}
|
|
@@ -756,6 +863,8 @@ class NgsFormsFormArrayInternalService {
|
|
|
756
863
|
formArray = new UntypedFormArray([]);
|
|
757
864
|
/** Emits after a row is added or removed. */
|
|
758
865
|
itemsChanged = new Subject();
|
|
866
|
+
/** Emits the index of a row right after it is removed. */
|
|
867
|
+
rowRemoved = new Subject();
|
|
759
868
|
constructor() {
|
|
760
869
|
if (this.config.initialItemCount) {
|
|
761
870
|
for (let i = 0; i < this.config.initialItemCount; i++) {
|
|
@@ -764,16 +873,19 @@ class NgsFormsFormArrayInternalService {
|
|
|
764
873
|
}
|
|
765
874
|
}
|
|
766
875
|
ngOnDestroy() {
|
|
876
|
+
this.itemsChanged.complete();
|
|
877
|
+
this.rowRemoved.complete();
|
|
767
878
|
}
|
|
768
879
|
/** Removes the row at `index`, unless that would go below `minItems`. */
|
|
769
880
|
removeAt(index) {
|
|
770
881
|
if (this.config?.minItems && this.formArray.controls.length <= this.config.minItems) {
|
|
771
882
|
return;
|
|
772
883
|
}
|
|
773
|
-
if (
|
|
884
|
+
if (index < 0 || index >= this.formArray.controls.length) {
|
|
774
885
|
return;
|
|
775
886
|
}
|
|
776
887
|
this.formArray.removeAt(index);
|
|
888
|
+
this.rowRemoved.next(index);
|
|
777
889
|
this.itemsChanged.next();
|
|
778
890
|
}
|
|
779
891
|
/** Appends an empty row, unless the array already has `maxItems` rows. */
|
|
@@ -859,10 +971,33 @@ class NgsFormsFormArrayListComponent extends NgsFormsBaseClassFormComponent {
|
|
|
859
971
|
internalArrayService = inject(NgsFormsFormArrayInternalService);
|
|
860
972
|
myFormArray = this.internalArrayService.formArray;
|
|
861
973
|
changeDetectorRef = inject(ChangeDetectorRef);
|
|
974
|
+
elementRef = inject(ElementRef);
|
|
975
|
+
injector = inject(Injector);
|
|
862
976
|
ngOnInit() {
|
|
863
977
|
this.subscribe(this.internalArrayService.itemsChanged, () => {
|
|
864
978
|
this.changeDetectorRef.markForCheck();
|
|
865
979
|
});
|
|
980
|
+
this.subscribe(this.internalArrayService.rowRemoved, (index) => this.restoreFocusAfterRemove(index));
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* When the focused element (usually the row's Remove button) was removed with its row, moves focus to the
|
|
984
|
+
* same kind of control in the row that took its place, so keyboard users are not sent back to the top of the page.
|
|
985
|
+
*/
|
|
986
|
+
restoreFocusAfterRemove(index) {
|
|
987
|
+
afterNextRender(() => {
|
|
988
|
+
const active = document.activeElement;
|
|
989
|
+
if (active && active !== document.body) {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
const host = this.elementRef.nativeElement;
|
|
993
|
+
const rows = Array.from(host.querySelectorAll(':scope > [data-index]'));
|
|
994
|
+
const row = rows[Math.min(index, rows.length - 1)];
|
|
995
|
+
const focusable = 'button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])';
|
|
996
|
+
const target = row?.querySelector('ngs-forms-form-array-remove-item button:not([disabled])') ??
|
|
997
|
+
row?.querySelector(focusable) ??
|
|
998
|
+
host.closest('ngs-forms-form-array')?.querySelector(focusable);
|
|
999
|
+
target?.focus();
|
|
1000
|
+
}, { injector: this.injector });
|
|
866
1001
|
}
|
|
867
1002
|
/** Creates a `form-array-list` item. */
|
|
868
1003
|
static create(config) {
|
|
@@ -894,8 +1029,26 @@ class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponen
|
|
|
894
1029
|
internalArrayService = inject(NgsFormsFormArrayInternalService);
|
|
895
1030
|
elementRef = inject(ElementRef);
|
|
896
1031
|
indexFromDi = inject(NGS_FORMS_ITEM_INDEX, { optional: true });
|
|
897
|
-
|
|
1032
|
+
row = inject(NGS_FORMS_ARRAY_ROW, { optional: true });
|
|
1033
|
+
changeDetectorRef = inject(ChangeDetectorRef);
|
|
1034
|
+
/** Accessible name of the button, from `ariaLabel` or `${buttonText} item ${index + 1}`. */
|
|
1035
|
+
get ariaLabel() {
|
|
1036
|
+
const index = this.myIndex;
|
|
1037
|
+
const label = this.config.ariaLabel;
|
|
1038
|
+
if (typeof label === 'function')
|
|
1039
|
+
return label(index);
|
|
1040
|
+
if (label)
|
|
1041
|
+
return label;
|
|
1042
|
+
return `${this.config.buttonText || 'Remove'} item ${index + 1}`;
|
|
1043
|
+
}
|
|
1044
|
+
/** Current index of the row this button belongs to. */
|
|
898
1045
|
get myIndex() {
|
|
1046
|
+
if (this.row) {
|
|
1047
|
+
const index = this.internalArrayService.formArray.controls.indexOf(this.row);
|
|
1048
|
+
if (index >= 0) {
|
|
1049
|
+
return index;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
899
1052
|
if (this.indexFromDi !== null && this.indexFromDi !== undefined) {
|
|
900
1053
|
return this.indexFromDi;
|
|
901
1054
|
}
|
|
@@ -912,6 +1065,10 @@ class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponen
|
|
|
912
1065
|
constructor() {
|
|
913
1066
|
super();
|
|
914
1067
|
}
|
|
1068
|
+
ngOnInit() {
|
|
1069
|
+
// The row's position changes when earlier rows are removed, which changes the default accessible name.
|
|
1070
|
+
this.subscribe(this.internalArrayService.itemsChanged, () => this.changeDetectorRef.markForCheck());
|
|
1071
|
+
}
|
|
915
1072
|
/** Creates a `form-array-remove-item` item. */
|
|
916
1073
|
static create(config) {
|
|
917
1074
|
return {
|
|
@@ -924,11 +1081,11 @@ class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponen
|
|
|
924
1081
|
this.internalArrayService.removeAt(this.myIndex);
|
|
925
1082
|
}
|
|
926
1083
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormArrayRemoveItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
927
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormsFormArrayRemoveItemComponent, isStandalone: true, selector: "ngs-forms-form-array-remove-item", usesInheritance: true, ngImport: i0, template: "<button\n (click)=\"removeItem()\"\n [class]=\"config.buttonClass || 'btn btn-outline-danger btn-sm'\"\n type=\"button\">\n @if (config.buttonIcon) {\n <i [class]=\"config.buttonIcon\" aria-hidden=\"true\"></i>\n }\n {{ config.buttonText || 'Remove' }}\n</button>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1084
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormsFormArrayRemoveItemComponent, isStandalone: true, selector: "ngs-forms-form-array-remove-item", usesInheritance: true, ngImport: i0, template: "<button\n (click)=\"removeItem()\"\n [class]=\"config.buttonClass || 'btn btn-outline-danger btn-sm'\"\n [attr.aria-label]=\"ariaLabel\"\n type=\"button\">\n @if (config.buttonIcon) {\n <i [class]=\"config.buttonIcon\" aria-hidden=\"true\"></i>\n }\n {{ config.buttonText || 'Remove' }}\n</button>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
928
1085
|
}
|
|
929
1086
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormArrayRemoveItemComponent, decorators: [{
|
|
930
1087
|
type: Component,
|
|
931
|
-
args: [{ selector: 'ngs-forms-form-array-remove-item', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"removeItem()\"\n [class]=\"config.buttonClass || 'btn btn-outline-danger btn-sm'\"\n type=\"button\">\n @if (config.buttonIcon) {\n <i [class]=\"config.buttonIcon\" aria-hidden=\"true\"></i>\n }\n {{ config.buttonText || 'Remove' }}\n</button>\n" }]
|
|
1088
|
+
args: [{ selector: 'ngs-forms-form-array-remove-item', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"removeItem()\"\n [class]=\"config.buttonClass || 'btn btn-outline-danger btn-sm'\"\n [attr.aria-label]=\"ariaLabel\"\n type=\"button\">\n @if (config.buttonIcon) {\n <i [class]=\"config.buttonIcon\" aria-hidden=\"true\"></i>\n }\n {{ config.buttonText || 'Remove' }}\n</button>\n" }]
|
|
932
1089
|
}], ctorParameters: () => [] });
|
|
933
1090
|
|
|
934
1091
|
/**
|
|
@@ -963,6 +1120,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
963
1120
|
args: [{ selector: 'ngs-form-component-text-div', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"{{ config.classes }}\">{{ config.text }}</div>\n" }]
|
|
964
1121
|
}] });
|
|
965
1122
|
|
|
1123
|
+
;
|
|
1124
|
+
|
|
966
1125
|
/**
|
|
967
1126
|
* Renders an HTML string inside the form. The HTML is passed through Angular's sanitizer.
|
|
968
1127
|
*
|
|
@@ -996,8 +1155,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
996
1155
|
args: [{ selector: 'ngs-form-component-html-content', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [innerHTML]=\"html\"></div>\n" }]
|
|
997
1156
|
}], ctorParameters: () => [] });
|
|
998
1157
|
|
|
1158
|
+
/** @internal */
|
|
999
1159
|
const sectionConfigDefaults = {
|
|
1000
1160
|
titleClass: 'h3',
|
|
1161
|
+
headingLevel: 3,
|
|
1001
1162
|
subtitleClass: 'lead',
|
|
1002
1163
|
};
|
|
1003
1164
|
|
|
@@ -1006,10 +1167,13 @@ const sectionConfigDefaults = {
|
|
|
1006
1167
|
* parent group.
|
|
1007
1168
|
*
|
|
1008
1169
|
* @remarks Registry key: `section`. Config: `title`, `subtitle`, `titleClass` (default `h3`),
|
|
1009
|
-
* `subtitleClass` (default `lead`) and `items`.
|
|
1170
|
+
* `headingLevel` (default 3), `subtitleClass` (default `lead`) and `items`. The section is exposed to assistive
|
|
1171
|
+
* technology as a group named by its title.
|
|
1010
1172
|
*/
|
|
1011
1173
|
class NgsFormsFormSectionComponent extends NgsFormsBaseClassItemsContainerBase {
|
|
1012
1174
|
static key = 'section';
|
|
1175
|
+
/** Element id of the title, used to name the section's group. */
|
|
1176
|
+
titleId = `${this.itemData.uuid || v4()}-section-title`;
|
|
1013
1177
|
/** Creates a `section` item. */
|
|
1014
1178
|
static create(config) {
|
|
1015
1179
|
return {
|
|
@@ -1022,11 +1186,11 @@ class NgsFormsFormSectionComponent extends NgsFormsBaseClassItemsContainerBase {
|
|
|
1022
1186
|
this.config = ngsDefaults(this.config, sectionConfigDefaults);
|
|
1023
1187
|
}
|
|
1024
1188
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormSectionComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1025
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormsFormSectionComponent, isStandalone: true, selector: "ngs-forms-form-section", usesInheritance: true, ngImport: i0, template: "<div class=\"form-section\">\n <div class=\"form-section-title {{config.titleClass}}\">{{ config.title }}</div>\n @if (config.subtitle) {\n <div class=\"form-section-subtitle {{config.subtitleClass}}\">{{ config.subtitle }}</div>\n }\n @if (config.items.length) {\n <div class=\"form-section-items\">\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgsFormsFormItemDirective, selector: "[ngs-form-item]", inputs: ["ngs-form-item", "ngs-form-group", "ngs-form-array", "ngs-form-index"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1189
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormsFormSectionComponent, isStandalone: true, selector: "ngs-forms-form-section", usesInheritance: true, ngImport: i0, template: "<div class=\"form-section\" role=\"group\" [attr.aria-labelledby]=\"titleId\">\n <div class=\"form-section-title {{config.titleClass}}\" role=\"heading\" [attr.aria-level]=\"config.headingLevel\" [id]=\"titleId\">{{ config.title }}</div>\n @if (config.subtitle) {\n <div class=\"form-section-subtitle {{config.subtitleClass}}\">{{ config.subtitle }}</div>\n }\n @if (config.items.length) {\n <div class=\"form-section-items\">\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgsFormsFormItemDirective, selector: "[ngs-form-item]", inputs: ["ngs-form-item", "ngs-form-group", "ngs-form-array", "ngs-form-index"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1026
1190
|
}
|
|
1027
1191
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormSectionComponent, decorators: [{
|
|
1028
1192
|
type: Component,
|
|
1029
|
-
args: [{ selector: 'ngs-forms-form-section', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"form-section\">\n <div class=\"form-section-title {{config.titleClass}}\">{{ config.title }}</div>\n @if (config.subtitle) {\n <div class=\"form-section-subtitle {{config.subtitleClass}}\">{{ config.subtitle }}</div>\n }\n @if (config.items.length) {\n <div class=\"form-section-items\">\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n }\n </div>\n }\n</div>\n" }]
|
|
1193
|
+
args: [{ selector: 'ngs-forms-form-section', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"form-section\" role=\"group\" [attr.aria-labelledby]=\"titleId\">\n <div class=\"form-section-title {{config.titleClass}}\" role=\"heading\" [attr.aria-level]=\"config.headingLevel\" [id]=\"titleId\">{{ config.title }}</div>\n @if (config.subtitle) {\n <div class=\"form-section-subtitle {{config.subtitleClass}}\">{{ config.subtitle }}</div>\n }\n @if (config.items.length) {\n <div class=\"form-section-items\">\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n }\n </div>\n }\n</div>\n" }]
|
|
1030
1194
|
}] });
|
|
1031
1195
|
|
|
1032
1196
|
// --
|
|
@@ -1066,5 +1230,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
1066
1230
|
* Generated bundle index. Do not edit.
|
|
1067
1231
|
*/
|
|
1068
1232
|
|
|
1069
|
-
export { 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 };
|
|
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 };
|
|
1070
1234
|
//# sourceMappingURL=ng-simplicity-forms-core.mjs.map
|