@ng-simplicity/forms-core 1.1.1 → 1.1.3
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 +17 -1
- package/fesm2022/ng-simplicity-forms-core.mjs +325 -19
- package/fesm2022/ng-simplicity-forms-core.mjs.map +1 -1
- package/index.d.ts +511 -8
- package/package.json +10 -5
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Subject, takeUntil, BehaviorSubject, map, filter, merge, of } from 'rxjs';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { Directive, InjectionToken, inject, Injectable, ChangeDetectorRef, Injector, ViewContainerRef, Input, ChangeDetectionStrategy, Component, ElementRef, Sanitizer, SecurityContext, NgModule } from '@angular/core';
|
|
3
|
+
import { Directive, InjectionToken, inject, Injectable, ChangeDetectorRef, Injector, ViewContainerRef, Input, computed, ChangeDetectionStrategy, Component, ElementRef, 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, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
6
|
+
import { UntypedFormGroup, FormControl, FormGroup, FormArray, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
7
7
|
import { CommonModule } from '@angular/common';
|
|
8
8
|
|
|
9
|
+
/** Base class that ties RxJS subscriptions to the component's lifetime. */
|
|
9
10
|
class NgsSubscriber {
|
|
10
11
|
destroy$ = new Subject();
|
|
11
12
|
subscriptions = [];
|
|
13
|
+
/** Subscribes to `subscribeTo` until the component is destroyed. */
|
|
12
14
|
subscribe(subscribeTo, nextObserver) {
|
|
13
15
|
subscribeTo.pipe(takeUntil(this.destroy$)).subscribe(nextObserver);
|
|
14
16
|
}
|
|
@@ -24,9 +26,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24
26
|
type: Directive
|
|
25
27
|
}] });
|
|
26
28
|
|
|
29
|
+
/** Injection token for the `NgsFormsFormItem` a component is rendering. */
|
|
27
30
|
const NGS_FORMS_ITEM_DATA = new InjectionToken('ngs-forms-item-data');
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
28
34
|
const NGS_FORMS_CONTROL_ADD_REMOVE_FN = new InjectionToken('ngs-form-control-remove-fn');
|
|
35
|
+
/** Injection token for the row index when a component is rendered inside a form array list. */
|
|
29
36
|
const NGS_FORMS_ITEM_INDEX = new InjectionToken('ngs-forms-item-index');
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
30
40
|
const NgsFormsFormControlAddRemoveFunctions = {
|
|
31
41
|
formGroup: (formGroup) => ({
|
|
32
42
|
add: (control, controlName) => formGroup.addControl(controlName, control),
|
|
@@ -55,15 +65,29 @@ function ngsDefaults(target, ...sources) {
|
|
|
55
65
|
return result;
|
|
56
66
|
}
|
|
57
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Root base class for every form item component. Reads the item's configuration from `NGS_FORMS_ITEM_DATA`.
|
|
70
|
+
*
|
|
71
|
+
* @remarks Extend it for components that do not own a form control, such as static content or buttons. Declare a
|
|
72
|
+
* `static key` and a `static create(config)` factory so the component can be registered and used in configs.
|
|
73
|
+
*/
|
|
58
74
|
class NgsFormsBaseClassFormComponent extends NgsSubscriber {
|
|
75
|
+
/** Registry key the component is registered under. */
|
|
59
76
|
static key;
|
|
60
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Element id for this component, handy for `id` and `for` attributes. The config's `id` when it sets one,
|
|
79
|
+
* otherwise a generated unique id.
|
|
80
|
+
*/
|
|
81
|
+
id;
|
|
82
|
+
/** The item's configuration. */
|
|
61
83
|
config;
|
|
84
|
+
/** The full form item: `uuid`, `type` and `config`. */
|
|
62
85
|
itemData;
|
|
63
86
|
constructor() {
|
|
64
87
|
super();
|
|
65
88
|
this.itemData = inject(NGS_FORMS_ITEM_DATA);
|
|
66
89
|
this.config = this.itemData.config;
|
|
90
|
+
this.id = this.config.id || v4();
|
|
67
91
|
}
|
|
68
92
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsBaseClassFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
69
93
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsBaseClassFormComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
@@ -73,11 +97,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
73
97
|
args: [{}]
|
|
74
98
|
}], ctorParameters: () => [] });
|
|
75
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Maps registry keys (the `type` of an `NgsFormsFormItem`) to the Angular components that render them.
|
|
102
|
+
*
|
|
103
|
+
* @remarks The service is provided in the `platform` injector, so there is one registry per page. Registering a
|
|
104
|
+
* key again replaces the previous component, which is how a theme package or your own component can override a
|
|
105
|
+
* built-in one. Register components before the form renders, typically in the constructor of the component that
|
|
106
|
+
* hosts `<ngs-form>`.
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* constructor(registry: NgsFormsComponentRegistryService) {
|
|
110
|
+
* NgsFormsBootstrapModule.registerAllBootStrapComponents(registry);
|
|
111
|
+
* registry.register(MyRatingComponent.key, MyRatingComponent);
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
76
115
|
class NgsFormsComponentRegistryService {
|
|
77
116
|
itemComponentRegistry = {};
|
|
117
|
+
/** Returns the component registered for `key`, or `undefined` when nothing is registered. */
|
|
78
118
|
getComponentTypeForKey(key) {
|
|
79
119
|
return this.itemComponentRegistry[key];
|
|
80
120
|
}
|
|
121
|
+
/** Registers `component` under `key`, replacing any component already registered for it. */
|
|
81
122
|
register(key, component) {
|
|
82
123
|
this.itemComponentRegistry[key] = component;
|
|
83
124
|
}
|
|
@@ -89,24 +130,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
89
130
|
args: [{ providedIn: 'platform' }]
|
|
90
131
|
}] });
|
|
91
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The public API of a form. Provide it on the component that hosts `<ngs-form>` so each form gets its own instance.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* @Component({
|
|
139
|
+
* selector: 'app-profile',
|
|
140
|
+
* imports: [NgsFormComponent],
|
|
141
|
+
* providers: [NgsFormsService],
|
|
142
|
+
* template: `<ngs-form />`,
|
|
143
|
+
* })
|
|
144
|
+
* export class ProfileComponent {
|
|
145
|
+
* private readonly forms = inject(NgsFormsService);
|
|
146
|
+
* readonly value = toSignal(this.forms.formValue$);
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
92
150
|
class NgsFormsService {
|
|
151
|
+
/**
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
93
154
|
attempt = 0;
|
|
155
|
+
/**
|
|
156
|
+
* Emits the root form group's value every time it changes. Combine with `toSignal()` to read it in a template.
|
|
157
|
+
*
|
|
158
|
+
* @remarks The value is keyed by the root group's `name`, for example `{ profile: { firstName: 'Ada' } }`.
|
|
159
|
+
*/
|
|
94
160
|
formValue$ = new BehaviorSubject({});
|
|
161
|
+
/** The latest value emitted by `formValue$`. */
|
|
95
162
|
formValue = {};
|
|
96
163
|
internalFormService;
|
|
97
164
|
formGroupSubscriptions = [];
|
|
98
165
|
formGroup = new UntypedFormGroup({});
|
|
166
|
+
/** `true` once the user has changed any control in the form. */
|
|
99
167
|
get dirty() {
|
|
100
168
|
return this.formGroup?.dirty ?? false;
|
|
101
169
|
}
|
|
170
|
+
/** `true` when every rendered control passes its validators. Hidden items are not validated. */
|
|
102
171
|
get isValid() {
|
|
103
172
|
if (!this.internalServiceIsSet)
|
|
104
173
|
return false;
|
|
105
174
|
return this.internalFormService.checkIsValid();
|
|
106
175
|
}
|
|
176
|
+
/** `true` once `<ngs-form>` has connected to this service. */
|
|
107
177
|
get internalServiceIsSet() {
|
|
108
178
|
return !!this.internalFormService;
|
|
109
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Renders a form from `formConfig`, replacing any form that is already shown and resetting the submitted state.
|
|
182
|
+
*
|
|
183
|
+
* @remarks It is safe to call before `<ngs-form>` has initialised (for example in `ngOnInit`): the call is retried
|
|
184
|
+
* every 25 ms for about half a second until the form component is available.
|
|
185
|
+
*/
|
|
110
186
|
setFormConfig(formConfig) {
|
|
111
187
|
if (!this.internalServiceIsSet) {
|
|
112
188
|
if (this.attempt > 20) {
|
|
@@ -119,10 +195,17 @@ class NgsFormsService {
|
|
|
119
195
|
}
|
|
120
196
|
this.internalFormService.setFormData(formConfig);
|
|
121
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
122
201
|
setInternalService(internalFormsService) {
|
|
123
202
|
this.internalFormService = internalFormsService;
|
|
124
203
|
this.bindFormGroup();
|
|
125
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Marks the form as submitted (or not). While submitted, every invalid control shows its error message even if
|
|
207
|
+
* the user never touched it.
|
|
208
|
+
*/
|
|
126
209
|
setIsSubmitted(isSubmitted) {
|
|
127
210
|
if (!this.internalServiceIsSet)
|
|
128
211
|
return;
|
|
@@ -138,15 +221,26 @@ class NgsFormsService {
|
|
|
138
221
|
}));
|
|
139
222
|
});
|
|
140
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Merges `state` into the state of one component, identified by its `uuid` or control `name`.
|
|
226
|
+
* Components read the merged result through `commonState`.
|
|
227
|
+
*/
|
|
141
228
|
updateComponentState(componentKey, state) {
|
|
142
229
|
this.internalFormService?.updateComponentState(componentKey, state);
|
|
143
230
|
}
|
|
231
|
+
/** Merges `state` into the state shared by every component in the form. */
|
|
144
232
|
updateGlobalState(state) {
|
|
145
233
|
this.internalFormService?.updateGlobalState(state);
|
|
146
234
|
}
|
|
147
235
|
ngOnDestroy() {
|
|
148
236
|
this.formGroupSubscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
149
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Patches values into the form, using the same shape as `formValue$` (including the root group name).
|
|
240
|
+
*
|
|
241
|
+
* @remarks Only controls that are currently rendered receive values. Patch again after a conditional item becomes
|
|
242
|
+
* visible, and make sure a form array has enough rows (see `initialItemCount`) before patching array values.
|
|
243
|
+
*/
|
|
150
244
|
patchValue(entityData) {
|
|
151
245
|
this.formGroup.patchValue(entityData);
|
|
152
246
|
}
|
|
@@ -157,6 +251,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
157
251
|
type: Injectable
|
|
158
252
|
}] });
|
|
159
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Per form state holder that connects `<ngs-form>`, its items and `NgsFormsService`.
|
|
256
|
+
*
|
|
257
|
+
* @remarks Provided by `NgsFormComponent`. Application code should use `NgsFormsService` instead; this service is
|
|
258
|
+
* exported for authors of custom components and may change between minor versions.
|
|
259
|
+
*/
|
|
160
260
|
class NgsFormsInternalService {
|
|
161
261
|
formGroup$ = new BehaviorSubject(new UntypedFormGroup({}));
|
|
162
262
|
formConfig$ = new BehaviorSubject(undefined);
|
|
@@ -171,6 +271,9 @@ class NgsFormsInternalService {
|
|
|
171
271
|
return this.formGroup$.value.valid;
|
|
172
272
|
}
|
|
173
273
|
setFormData(formConfig) {
|
|
274
|
+
const state = { ...(formConfig.initialState ?? {}) };
|
|
275
|
+
state['global'] = { ...(formConfig.globalState ?? {}) };
|
|
276
|
+
this.state.next(state);
|
|
174
277
|
this.formGroup$.next(new UntypedFormGroup({}));
|
|
175
278
|
this.formConfig$.next(formConfig);
|
|
176
279
|
this.isSubmitted$.next(false);
|
|
@@ -181,13 +284,24 @@ class NgsFormsInternalService {
|
|
|
181
284
|
this.state.value[key] = updated;
|
|
182
285
|
this.state.next(this.state.value);
|
|
183
286
|
}
|
|
287
|
+
/** Sets a component's state from its config's `initialState`, unless state for `uuid` already exists. */
|
|
288
|
+
seedComponentState(uuid, initialState) {
|
|
289
|
+
if (!uuid || !initialState || this.state.value[uuid])
|
|
290
|
+
return;
|
|
291
|
+
this.mergeState(uuid, initialState);
|
|
292
|
+
}
|
|
184
293
|
updateGlobalState(state) {
|
|
185
294
|
this.mergeState('global', state);
|
|
186
295
|
}
|
|
187
296
|
updateComponentState(componentKey, state) {
|
|
188
297
|
this.mergeState(componentKey, state);
|
|
189
298
|
}
|
|
190
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Merged state for one component: its own (by `uuid`), then by `name`, then global.
|
|
301
|
+
* `initialState` seeds the component's own state unless state for `uuid` already exists.
|
|
302
|
+
*/
|
|
303
|
+
subscribeToState(uuid, name, initialState) {
|
|
304
|
+
this.seedComponentState(uuid, initialState);
|
|
191
305
|
let last = '';
|
|
192
306
|
return this.state.pipe(map((allState) => {
|
|
193
307
|
const stateByUuid = allState[uuid] || {};
|
|
@@ -208,21 +322,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
208
322
|
type: Injectable
|
|
209
323
|
}] });
|
|
210
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Base class for items that own a form control. Creates the control (seeded with `value` when the config has one),
|
|
327
|
+
* applies `validators` / `validators$` and `disabled` / `disabled$`, registers it with the parent group under
|
|
328
|
+
* `config.name` and keeps `errorMessage` up to date.
|
|
329
|
+
*/
|
|
211
330
|
class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassFormComponent {
|
|
331
|
+
/** The item's form control. Bind it with `[formControl]`. */
|
|
212
332
|
control;
|
|
333
|
+
/**
|
|
334
|
+
* Message for the control's first error, taken from `errorMessageMap`. Empty while the control is valid, or
|
|
335
|
+
* before it is touched, dirty or submitted.
|
|
336
|
+
*/
|
|
213
337
|
errorMessage = '';
|
|
214
338
|
formAddRemoveFns = inject(NGS_FORMS_CONTROL_ADD_REMOVE_FN);
|
|
215
339
|
privateService = inject(NgsFormsInternalService);
|
|
216
340
|
changeDetectorRef = inject(ChangeDetectorRef);
|
|
341
|
+
/** Whether the form has been marked as submitted with `NgsFormsService.setIsSubmitted()`. */
|
|
217
342
|
submitted = toSignal(this.privateService.isSubmitted$, { initialValue: false });
|
|
343
|
+
/** The latest value of `disabled` / `disabled$`, or `undefined` when the config sets neither. */
|
|
344
|
+
disabledFromConfig;
|
|
218
345
|
ngOnInit() {
|
|
219
346
|
if (!this.control) {
|
|
220
|
-
|
|
347
|
+
const initialValue = this.config.value;
|
|
348
|
+
this.control = new FormControl(initialValue, { validators: [] });
|
|
221
349
|
}
|
|
222
|
-
//this.bindVisible();
|
|
223
350
|
this.bindValidators();
|
|
224
|
-
//this.bindControlValidityChange();
|
|
225
351
|
this.formAddRemoveFns.add(this.control, this.config.name);
|
|
352
|
+
this.bindDisabled();
|
|
226
353
|
// Listen to changes and update error message dynamically
|
|
227
354
|
this.subscribe(merge(this.control.valueChanges, this.control.statusChanges, this.privateService.isSubmitted$), () => {
|
|
228
355
|
this.updateErrorMessage();
|
|
@@ -263,6 +390,34 @@ class NgsFormsFormItemWithVisibleAndValidatorsBase extends NgsFormsBaseClassForm
|
|
|
263
390
|
this.subscribe(this.config.validators$, (validators) => this.control.setValidators(validators));
|
|
264
391
|
}
|
|
265
392
|
}
|
|
393
|
+
bindDisabled() {
|
|
394
|
+
const disabled$ = this.config.disabled$ ?? (this.config.disabled !== undefined ? of(this.config.disabled) : undefined);
|
|
395
|
+
if (!disabled$)
|
|
396
|
+
return;
|
|
397
|
+
const control = this.control;
|
|
398
|
+
this.subscribe(disabled$, (disabled) => {
|
|
399
|
+
this.disabledFromConfig = !!disabled;
|
|
400
|
+
this.applyDisabled(control);
|
|
401
|
+
});
|
|
402
|
+
// Child controls are added to groups and arrays after the container has been disabled, and adding an enabled
|
|
403
|
+
// child re-enables its parent. Re-apply the configured state whenever a container's status changes.
|
|
404
|
+
if (control instanceof FormGroup || control instanceof FormArray) {
|
|
405
|
+
this.subscribe(control.statusChanges, () => {
|
|
406
|
+
if (this.disabledFromConfig && control.enabled) {
|
|
407
|
+
control.disable();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
applyDisabled(control) {
|
|
413
|
+
if (this.disabledFromConfig && !control.disabled) {
|
|
414
|
+
control.disable();
|
|
415
|
+
}
|
|
416
|
+
else if (!this.disabledFromConfig && control.disabled) {
|
|
417
|
+
control.enable();
|
|
418
|
+
}
|
|
419
|
+
this.changeDetectorRef.markForCheck();
|
|
420
|
+
}
|
|
266
421
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormItemWithVisibleAndValidatorsBase, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
267
422
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsFormItemWithVisibleAndValidatorsBase, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
268
423
|
}
|
|
@@ -271,11 +426,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
271
426
|
args: [{}]
|
|
272
427
|
}] });
|
|
273
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Base class for input components. Adds access to the form's `NgsFormsService` and the merged component state.
|
|
431
|
+
*
|
|
432
|
+
* @remarks This is the class to extend when you build your own input. See the Custom components guide.
|
|
433
|
+
*/
|
|
274
434
|
class NgsFormsBaseClassFormInputComponent extends NgsFormsFormItemWithVisibleAndValidatorsBase {
|
|
435
|
+
/** The `NgsFormsService` of the form this input belongs to. */
|
|
275
436
|
myFormService = inject(NgsFormsService);
|
|
276
437
|
internalService = inject(NgsFormsInternalService);
|
|
277
|
-
|
|
438
|
+
/** Merged state for this component: its own state, then state by name, then global state. */
|
|
439
|
+
commonState = toSignal(this.internalService.subscribeToState(this.itemData.uuid || '', this.config.name, this.config.initialState), { initialValue: {} });
|
|
440
|
+
/**
|
|
441
|
+
* Switches this component between `input` and `summary` display by updating its own state.
|
|
442
|
+
* With no argument it flips the current mode.
|
|
443
|
+
*/
|
|
278
444
|
toggleDisplayMode(setTo) {
|
|
445
|
+
const current = this.commonState().displayMode ?? 'input';
|
|
446
|
+
const displayMode = setTo ?? (current === 'summary' ? 'input' : 'summary');
|
|
447
|
+
this.internalService.updateComponentState(this.itemData.uuid || this.config.name, { displayMode });
|
|
279
448
|
}
|
|
280
449
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsBaseClassFormInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
281
450
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsBaseClassFormInputComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
@@ -285,6 +454,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
285
454
|
args: [{}]
|
|
286
455
|
}] });
|
|
287
456
|
|
|
457
|
+
/** Base class for container items that render `config.items` without owning a form control. */
|
|
288
458
|
class NgsFormsBaseClassItemsContainerBase extends NgsFormsBaseClassFormComponent {
|
|
289
459
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsBaseClassItemsContainerBase, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
290
460
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsBaseClassItemsContainerBase, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
@@ -294,7 +464,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
294
464
|
args: [{}]
|
|
295
465
|
}] });
|
|
296
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Base class for inputs with options. Exposes `options` as a signal that follows `options$` when given and falls
|
|
469
|
+
* back to the static `options`.
|
|
470
|
+
*/
|
|
297
471
|
class NgsFormsBaseClassFormItemInputWithOptionsComponent extends NgsFormsBaseClassFormInputComponent {
|
|
472
|
+
/** The current options. */
|
|
298
473
|
options = toSignal(this.config.options$ ?? of(this.config.options ?? []), { initialValue: this.config.options ?? [] });
|
|
299
474
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsBaseClassFormItemInputWithOptionsComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
300
475
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsBaseClassFormItemInputWithOptionsComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
@@ -328,11 +503,13 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
328
503
|
super.ngOnDestroy();
|
|
329
504
|
}
|
|
330
505
|
bindVisibility() {
|
|
331
|
-
|
|
506
|
+
const config = this.itemData.config;
|
|
507
|
+
const visible$ = config.visible$ ?? (config.visible !== undefined ? of(config.visible) : undefined);
|
|
508
|
+
if (!visible$) {
|
|
332
509
|
Promise.resolve().then(() => this.create());
|
|
333
510
|
return;
|
|
334
511
|
}
|
|
335
|
-
this.subscribe(
|
|
512
|
+
this.subscribe(visible$, (visible) => {
|
|
336
513
|
if (this.lastVisible === visible)
|
|
337
514
|
return;
|
|
338
515
|
this.lastVisible = visible;
|
|
@@ -372,10 +549,22 @@ class NgsFormItemController extends NgsSubscriber {
|
|
|
372
549
|
}
|
|
373
550
|
}
|
|
374
551
|
|
|
552
|
+
/**
|
|
553
|
+
* Structural building block that looks up a form item's component in the registry and renders it.
|
|
554
|
+
*
|
|
555
|
+
* @remarks Use it inside custom container components to render child items:
|
|
556
|
+
* `<ng-container [ngs-form-item]="item" [ngs-form-group]="group"></ng-container>`.
|
|
557
|
+
*/
|
|
375
558
|
class NgsFormsFormItemDirective {
|
|
559
|
+
/** The item to render. */
|
|
376
560
|
itemData = undefined;
|
|
561
|
+
/** Form group that the item's control is added to. */
|
|
377
562
|
formGroup;
|
|
563
|
+
/** Form array that the item's control is added to. */
|
|
378
564
|
formArray;
|
|
565
|
+
/**
|
|
566
|
+
* Index of the item inside a form array row, provided to children as `NGS_FORMS_ITEM_INDEX`.
|
|
567
|
+
*/
|
|
379
568
|
index;
|
|
380
569
|
controller;
|
|
381
570
|
viewContainerRef = inject(ViewContainerRef);
|
|
@@ -421,24 +610,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
421
610
|
args: ['ngs-form-index']
|
|
422
611
|
}] } });
|
|
423
612
|
|
|
613
|
+
/**
|
|
614
|
+
* Renders the form described by the `NgsFormsFormConfig` given to the nearest `NgsFormsService`.
|
|
615
|
+
*
|
|
616
|
+
* @remarks Place it in the template of the component that provides `NgsFormsService`. It takes no inputs.
|
|
617
|
+
* @example
|
|
618
|
+
* ```html
|
|
619
|
+
* <ngs-form></ngs-form>
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
424
622
|
class NgsFormComponent {
|
|
425
623
|
ngsInternalFormsService = inject(NgsFormsInternalService);
|
|
426
624
|
ngsFormsService = inject(NgsFormsService);
|
|
427
625
|
formGroup = toSignal(this.ngsInternalFormsService.formGroup$);
|
|
428
626
|
formConfig = toSignal(this.ngsInternalFormsService.formConfig$);
|
|
627
|
+
/** The root item to render with the form group it belongs to (empty until a config is set). */
|
|
628
|
+
rendered = computed(() => {
|
|
629
|
+
const group = this.formGroup();
|
|
630
|
+
const root = this.formConfig()?.root;
|
|
631
|
+
return group && root ? [{ group, root }] : [];
|
|
632
|
+
}, ...(ngDevMode ? [{ debugName: "rendered" }] : []));
|
|
429
633
|
constructor() {
|
|
430
634
|
this.ngsFormsService.setInternalService(this.ngsInternalFormsService);
|
|
431
635
|
}
|
|
432
636
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
433
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NgsFormComponent, isStandalone: true, selector: "ngs-form", providers: [NgsFormsInternalService], ngImport: i0, template: "@
|
|
637
|
+
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 });
|
|
434
638
|
}
|
|
435
639
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormComponent, decorators: [{
|
|
436
640
|
type: Component,
|
|
437
|
-
args: [{ selector: 'ngs-form', providers: [NgsFormsInternalService], imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "@
|
|
641
|
+
args: [{ selector: 'ngs-form', providers: [NgsFormsInternalService], imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, 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" }]
|
|
438
642
|
}], ctorParameters: () => [] });
|
|
439
643
|
|
|
644
|
+
/**
|
|
645
|
+
* Renders its items one after another without any wrapper markup. Useful as a grouping node inside a row.
|
|
646
|
+
*
|
|
647
|
+
* @remarks Registry key: `column`. Does not create a form control.
|
|
648
|
+
*/
|
|
440
649
|
class NgsFormsColumnComponent extends NgsFormsBaseClassItemsContainerBase {
|
|
441
650
|
static key = 'column';
|
|
651
|
+
/** Creates a `column` item. */
|
|
442
652
|
static create(config) {
|
|
443
653
|
return {
|
|
444
654
|
uuid: config.uuid || v4(),
|
|
@@ -454,8 +664,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
454
664
|
args: [{ selector: 'ngs-form-component-column', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n}\n" }]
|
|
455
665
|
}] });
|
|
456
666
|
|
|
457
|
-
|
|
458
|
-
|
|
667
|
+
/**
|
|
668
|
+
* Lays out items side by side. Each item is wrapped in a div whose class comes from `columnClass` (same for every
|
|
669
|
+
* item) or `columnClasses` (one per item), so it works with any CSS grid system.
|
|
670
|
+
*
|
|
671
|
+
* @remarks Registry key: `form-row`. Does not create a form control.
|
|
672
|
+
* @example
|
|
673
|
+
* ```ts
|
|
674
|
+
* NgsFormsRowComponent.create({
|
|
675
|
+
* containerClass: 'row g-3',
|
|
676
|
+
* columnClasses: ['col-md-6', 'col-md-6'],
|
|
677
|
+
* items: [firstName, lastName],
|
|
678
|
+
* })
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
459
681
|
class NgsFormsRowComponent extends NgsFormsBaseClassFormComponent {
|
|
460
682
|
static key = 'form-row';
|
|
461
683
|
constructor() {
|
|
@@ -470,6 +692,7 @@ class NgsFormsRowComponent extends NgsFormsBaseClassFormComponent {
|
|
|
470
692
|
}
|
|
471
693
|
return this.config.columnClasses[index];
|
|
472
694
|
}
|
|
695
|
+
/** Creates a `form-row` item. */
|
|
473
696
|
static create(config) {
|
|
474
697
|
return {
|
|
475
698
|
uuid: config.uuid || v4(),
|
|
@@ -485,8 +708,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
485
708
|
args: [{ selector: 'ngs-forms-row-component', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"{{config.containerClass}}\">\n @for(item of config.items; track item; let i = $index){\n <div class=\"{{getIndividualRowDivClass(i)}}\">\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n </div>\n }\n</div>\n" }]
|
|
486
709
|
}], ctorParameters: () => [] });
|
|
487
710
|
|
|
711
|
+
/**
|
|
712
|
+
* A named group of items backed by an `UntypedFormGroup`. Its value is nested under `name` in the form value.
|
|
713
|
+
*
|
|
714
|
+
* @remarks Registry key: `form-group`. Usually the `root` of a form, and also useful for nesting related fields
|
|
715
|
+
* (an address, for example) or for hiding a whole group with `visible$`.
|
|
716
|
+
* @example
|
|
717
|
+
* ```ts
|
|
718
|
+
* NgsFormsFormGroupComponent.create({
|
|
719
|
+
* name: 'address',
|
|
720
|
+
* items: [addressLine1, city, postcode],
|
|
721
|
+
* })
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
488
724
|
class NgsFormsFormGroupComponent extends NgsFormsFormItemWithVisibleAndValidatorsBase {
|
|
489
725
|
static key = 'form-group';
|
|
726
|
+
/** Creates a `form-group` item. */
|
|
490
727
|
static create(config) {
|
|
491
728
|
return {
|
|
492
729
|
uuid: config.uuid || v4(),
|
|
@@ -509,9 +746,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
509
746
|
args: [{ selector: 'ngs-forms-form-group', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (config.items) {\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\" [ngs-form-group]=\"formGroupControl\"></ng-container>\n }\n}\n" }]
|
|
510
747
|
}], ctorParameters: () => [] });
|
|
511
748
|
|
|
749
|
+
/**
|
|
750
|
+
* Holds the `UntypedFormArray` of one `NgsFormsFormArrayContainerComponent` and is shared with its list and buttons.
|
|
751
|
+
*
|
|
752
|
+
* @remarks Inject it in a custom component placed inside a form array to add or remove rows programmatically.
|
|
753
|
+
*/
|
|
512
754
|
class NgsFormsFormArrayInternalService {
|
|
513
755
|
config = inject(NGS_FORMS_ITEM_DATA).config;
|
|
514
756
|
formArray = new UntypedFormArray([]);
|
|
757
|
+
/** Emits after a row is added or removed. */
|
|
515
758
|
itemsChanged = new Subject();
|
|
516
759
|
constructor() {
|
|
517
760
|
if (this.config.initialItemCount) {
|
|
@@ -522,6 +765,7 @@ class NgsFormsFormArrayInternalService {
|
|
|
522
765
|
}
|
|
523
766
|
ngOnDestroy() {
|
|
524
767
|
}
|
|
768
|
+
/** Removes the row at `index`, unless that would go below `minItems`. */
|
|
525
769
|
removeAt(index) {
|
|
526
770
|
if (this.config?.minItems && this.formArray.controls.length <= this.config.minItems) {
|
|
527
771
|
return;
|
|
@@ -532,6 +776,7 @@ class NgsFormsFormArrayInternalService {
|
|
|
532
776
|
this.formArray.removeAt(index);
|
|
533
777
|
this.itemsChanged.next();
|
|
534
778
|
}
|
|
779
|
+
/** Appends an empty row, unless the array already has `maxItems` rows. */
|
|
535
780
|
addItem() {
|
|
536
781
|
if (this.config?.maxItems && this.formArray.controls.length >= this.config.maxItems) {
|
|
537
782
|
return;
|
|
@@ -546,9 +791,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
546
791
|
type: Injectable
|
|
547
792
|
}], ctorParameters: () => [] });
|
|
548
793
|
|
|
794
|
+
/**
|
|
795
|
+
* A button that appends a row to the enclosing form array, up to `maxItems`.
|
|
796
|
+
*
|
|
797
|
+
* @remarks Registry key: `form-array-add-item`. Must be placed inside an `NgsFormsFormArrayContainerComponent`.
|
|
798
|
+
*/
|
|
549
799
|
class NgsFormsFormArrayAddItemComponent extends NgsFormsBaseClassFormComponent {
|
|
550
800
|
static key = 'form-array-add-item';
|
|
551
801
|
internalArrayService = inject(NgsFormsFormArrayInternalService);
|
|
802
|
+
/** Creates a `form-array-add-item` item. */
|
|
552
803
|
static create(config) {
|
|
553
804
|
return {
|
|
554
805
|
uuid: config.uuid || v4(),
|
|
@@ -567,6 +818,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
567
818
|
args: [{ selector: 'ngs-forms-form-array-add-item', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"addItem()\"\n [class]=\"config.buttonClass || 'btn btn-outline-primary'\"\n type=\"button\">\n @if (config.buttonIcon) {\n <i [class]=\"config.buttonIcon\" aria-hidden=\"true\"></i>\n }\n {{ config.buttonText || 'Add Item' }}\n</button>\n" }]
|
|
568
819
|
}] });
|
|
569
820
|
|
|
821
|
+
/**
|
|
822
|
+
* A repeatable list of rows backed by an `UntypedFormArray`. Its value is an array under `name`.
|
|
823
|
+
*
|
|
824
|
+
* @remarks Registry key: `form-array`. Put an `NgsFormsFormArrayListComponent` inside `items` to render the rows,
|
|
825
|
+
* and `NgsFormsFormArrayAddItemComponent` / `NgsFormsFormArrayRemoveItemComponent` to let users change them.
|
|
826
|
+
*/
|
|
570
827
|
class NgsFormsFormArrayContainerComponent extends NgsFormsFormItemWithVisibleAndValidatorsBase {
|
|
571
828
|
formArrayService;
|
|
572
829
|
static key = 'form-array';
|
|
@@ -575,6 +832,7 @@ class NgsFormsFormArrayContainerComponent extends NgsFormsFormItemWithVisibleAnd
|
|
|
575
832
|
this.formArrayService = formArrayService;
|
|
576
833
|
this.control = this.formArrayService.formArray;
|
|
577
834
|
}
|
|
835
|
+
/** Creates a `form-array` item. */
|
|
578
836
|
static create(config) {
|
|
579
837
|
return {
|
|
580
838
|
uuid: config.uuid || v4(),
|
|
@@ -590,6 +848,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
590
848
|
args: [{ selector: 'ngs-forms-form-array', imports: [NgsFormsFormItemDirective], providers: [NgsFormsFormArrayInternalService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"config.containerClass || ''\">\n @for (item of config.items; track item.uuid) {\n <ng-container [ngs-form-item]=\"item\"></ng-container>\n }\n</div>\n" }]
|
|
591
849
|
}], ctorParameters: () => [{ type: NgsFormsFormArrayInternalService }] });
|
|
592
850
|
|
|
851
|
+
/**
|
|
852
|
+
* Renders one copy of `templateItem` for every row of the enclosing form array. Each row gets its own form group,
|
|
853
|
+
* so the controls in the template become the properties of each array entry.
|
|
854
|
+
*
|
|
855
|
+
* @remarks Registry key: `form-array-list`. Must be placed inside an `NgsFormsFormArrayContainerComponent`.
|
|
856
|
+
*/
|
|
593
857
|
class NgsFormsFormArrayListComponent extends NgsFormsBaseClassFormComponent {
|
|
594
858
|
static key = 'form-array-list';
|
|
595
859
|
internalArrayService = inject(NgsFormsFormArrayInternalService);
|
|
@@ -600,6 +864,7 @@ class NgsFormsFormArrayListComponent extends NgsFormsBaseClassFormComponent {
|
|
|
600
864
|
this.changeDetectorRef.markForCheck();
|
|
601
865
|
});
|
|
602
866
|
}
|
|
867
|
+
/** Creates a `form-array-list` item. */
|
|
603
868
|
static create(config) {
|
|
604
869
|
return {
|
|
605
870
|
uuid: config.uuid || v4(),
|
|
@@ -618,11 +883,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
618
883
|
args: [{ selector: 'ngs-forms-form-array-list', imports: [NgsFormsFormItemDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (formGroup of myFormArray.controls; track formGroup; let i = $index) {\n <div [attr.data-index]=\"$index\" [class]=\"config.containerClass || ''\">\n <ng-container [ngs-form-group]=\"castToFormGroup(formGroup)\" [ngs-form-index]=\"i\"\n [ngs-form-item]=\"config.templateItem\"></ng-container>\n </div>\n}\n" }]
|
|
619
884
|
}] });
|
|
620
885
|
|
|
886
|
+
/**
|
|
887
|
+
* A button that removes its own row from the enclosing form array, down to `minItems`.
|
|
888
|
+
*
|
|
889
|
+
* @remarks Registry key: `form-array-remove-item`. Place it inside the `templateItem` of an
|
|
890
|
+
* `NgsFormsFormArrayListComponent` so each row gets one.
|
|
891
|
+
*/
|
|
621
892
|
class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponent {
|
|
622
893
|
static key = 'form-array-remove-item';
|
|
623
894
|
internalArrayService = inject(NgsFormsFormArrayInternalService);
|
|
624
895
|
elementRef = inject(ElementRef);
|
|
625
896
|
indexFromDi = inject(NGS_FORMS_ITEM_INDEX, { optional: true });
|
|
897
|
+
/** Index of the row this button belongs to. */
|
|
626
898
|
get myIndex() {
|
|
627
899
|
if (this.indexFromDi !== null && this.indexFromDi !== undefined) {
|
|
628
900
|
return this.indexFromDi;
|
|
@@ -640,6 +912,7 @@ class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponen
|
|
|
640
912
|
constructor() {
|
|
641
913
|
super();
|
|
642
914
|
}
|
|
915
|
+
/** Creates a `form-array-remove-item` item. */
|
|
643
916
|
static create(config) {
|
|
644
917
|
return {
|
|
645
918
|
uuid: config.uuid || v4(),
|
|
@@ -651,15 +924,21 @@ class NgsFormsFormArrayRemoveItemComponent extends NgsFormsBaseClassFormComponen
|
|
|
651
924
|
this.internalArrayService.removeAt(this.myIndex);
|
|
652
925
|
}
|
|
653
926
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormArrayRemoveItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
654
|
-
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
|
|
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 });
|
|
655
928
|
}
|
|
656
929
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsFormArrayRemoveItemComponent, decorators: [{
|
|
657
930
|
type: Component,
|
|
658
|
-
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
|
|
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" }]
|
|
659
932
|
}], ctorParameters: () => [] });
|
|
660
933
|
|
|
934
|
+
/**
|
|
935
|
+
* Renders a line of static text, such as a heading or a hint, inside the form.
|
|
936
|
+
*
|
|
937
|
+
* @remarks Registry key: `text-div`. Config: `text` and optional `classes`.
|
|
938
|
+
*/
|
|
661
939
|
class NgsFormsTextDivComponent extends NgsFormsBaseClassFormComponent {
|
|
662
940
|
static key = 'text-div';
|
|
941
|
+
/** Creates a `text-div` item. */
|
|
663
942
|
static create(config) {
|
|
664
943
|
return {
|
|
665
944
|
uuid: config.uuid || v4(),
|
|
@@ -667,6 +946,9 @@ class NgsFormsTextDivComponent extends NgsFormsBaseClassFormComponent {
|
|
|
667
946
|
config: config,
|
|
668
947
|
};
|
|
669
948
|
}
|
|
949
|
+
/**
|
|
950
|
+
* Shortcut for a heading: creates a `text-div` with `text` and the given class (default `h2`).
|
|
951
|
+
*/
|
|
670
952
|
static title(title, divClass = 'h2') {
|
|
671
953
|
return NgsFormsTextDivComponent.create({
|
|
672
954
|
text: title,
|
|
@@ -674,13 +956,19 @@ class NgsFormsTextDivComponent extends NgsFormsBaseClassFormComponent {
|
|
|
674
956
|
});
|
|
675
957
|
}
|
|
676
958
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsTextDivComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
677
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsTextDivComponent, isStandalone: true, selector: "ngs-form-component-text-div", usesInheritance: true, ngImport: i0, template: "<div class=\"{{config.classes\">{{ config.text }}</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
959
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NgsFormsTextDivComponent, isStandalone: true, selector: "ngs-form-component-text-div", usesInheritance: true, ngImport: i0, template: "<div class=\"{{ config.classes }}\">{{ config.text }}</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
678
960
|
}
|
|
679
961
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsTextDivComponent, decorators: [{
|
|
680
962
|
type: Component,
|
|
681
|
-
args: [{ selector: 'ngs-form-component-text-div', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"{{config.classes\">{{ config.text }}</div>\n" }]
|
|
963
|
+
args: [{ selector: 'ngs-form-component-text-div', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"{{ config.classes }}\">{{ config.text }}</div>\n" }]
|
|
682
964
|
}] });
|
|
683
965
|
|
|
966
|
+
/**
|
|
967
|
+
* Renders an HTML string inside the form. The HTML is passed through Angular's sanitizer.
|
|
968
|
+
*
|
|
969
|
+
* @remarks Registry key: `content-html`. Registered by `registerCoreNgsFormComponents()`, which the theme packages'
|
|
970
|
+
* `registerAll…` helpers call for you.
|
|
971
|
+
*/
|
|
684
972
|
class NgsFormsHtmlContentComponent extends NgsFormsBaseClassFormComponent {
|
|
685
973
|
static key = 'content-html';
|
|
686
974
|
contentConfig = this.config;
|
|
@@ -692,6 +980,7 @@ class NgsFormsHtmlContentComponent extends NgsFormsBaseClassFormComponent {
|
|
|
692
980
|
sanitizer.sanitize(SecurityContext.HTML, this.contentConfig.html) ||
|
|
693
981
|
'';
|
|
694
982
|
}
|
|
983
|
+
/** Creates a `content-html` item. */
|
|
695
984
|
static create(config) {
|
|
696
985
|
return {
|
|
697
986
|
uuid: config.uuid || v4(),
|
|
@@ -712,8 +1001,16 @@ const sectionConfigDefaults = {
|
|
|
712
1001
|
subtitleClass: 'lead',
|
|
713
1002
|
};
|
|
714
1003
|
|
|
1004
|
+
/**
|
|
1005
|
+
* Groups items under a title and optional subtitle. Does not create a form control, so child values stay in the
|
|
1006
|
+
* parent group.
|
|
1007
|
+
*
|
|
1008
|
+
* @remarks Registry key: `section`. Config: `title`, `subtitle`, `titleClass` (default `h3`),
|
|
1009
|
+
* `subtitleClass` (default `lead`) and `items`.
|
|
1010
|
+
*/
|
|
715
1011
|
class NgsFormsFormSectionComponent extends NgsFormsBaseClassItemsContainerBase {
|
|
716
1012
|
static key = 'section';
|
|
1013
|
+
/** Creates a `section` item. */
|
|
717
1014
|
static create(config) {
|
|
718
1015
|
return {
|
|
719
1016
|
uuid: config.uuid || v4(),
|
|
@@ -732,7 +1029,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
732
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" }]
|
|
733
1030
|
}] });
|
|
734
1031
|
|
|
1032
|
+
// --
|
|
1033
|
+
|
|
1034
|
+
/** Registration helpers for the structural components in `@ng-simplicity/forms-core`. */
|
|
735
1035
|
class NgsFormsCoreModule {
|
|
1036
|
+
/**
|
|
1037
|
+
* Registers the structural components: `form-group`, `form-row`, `column`, `form-array`, `form-array-list`,
|
|
1038
|
+
* `form-array-add-item`, `form-array-remove-item`, `text-div`, `content-html` and `section`.
|
|
1039
|
+
*
|
|
1040
|
+
* @remarks The theme packages call this for you from their own `registerAll…` helpers.
|
|
1041
|
+
*/
|
|
736
1042
|
static registerCoreNgsFormComponents(registryService) {
|
|
737
1043
|
registryService.register(NgsFormsFormGroupComponent.key, NgsFormsFormGroupComponent);
|
|
738
1044
|
registryService.register(NgsFormsRowComponent.key, NgsFormsRowComponent);
|
|
@@ -742,7 +1048,7 @@ class NgsFormsCoreModule {
|
|
|
742
1048
|
registryService.register(NgsFormsFormArrayRemoveItemComponent.key, NgsFormsFormArrayRemoveItemComponent);
|
|
743
1049
|
registryService.register(NgsFormsFormArrayListComponent.key, NgsFormsFormArrayListComponent);
|
|
744
1050
|
registryService.register(NgsFormsTextDivComponent.key, NgsFormsTextDivComponent);
|
|
745
|
-
|
|
1051
|
+
registryService.register(NgsFormsHtmlContentComponent.key, NgsFormsHtmlContentComponent);
|
|
746
1052
|
registryService.register(NgsFormsFormSectionComponent.key, NgsFormsFormSectionComponent);
|
|
747
1053
|
}
|
|
748
1054
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NgsFormsCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|