@olafvv/ngx-dynamic-form 19.0.1 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -204
- package/fesm2022/olafvv-ngx-dynamic-form.mjs +289 -357
- package/fesm2022/olafvv-ngx-dynamic-form.mjs.map +1 -1
- package/index.d.ts +561 -3
- package/package.json +5 -5
- package/lib/components/dynamic-form/dynamic-form.component.d.ts +0 -31
- package/lib/components/dynamic-form-field/dynamic-form-field.component.d.ts +0 -49
- package/lib/controls/button/dynamic-button.component.d.ts +0 -11
- package/lib/controls/button/dynamic-button.model.d.ts +0 -26
- package/lib/controls/button-toggles/dynamic-button-toggles.component.d.ts +0 -13
- package/lib/controls/button-toggles/dynamic-button-toggles.model.d.ts +0 -14
- package/lib/controls/checkbox/dynamic-checkbox.component.d.ts +0 -13
- package/lib/controls/checkbox/dynamic-checkbox.model.d.ts +0 -15
- package/lib/controls/controls.d.ts +0 -9
- package/lib/controls/datepicker/dynamic-datepicker.component.d.ts +0 -13
- package/lib/controls/datepicker/dynamic-datepicker.model.d.ts +0 -21
- package/lib/controls/input/dynamic-input.component.d.ts +0 -19
- package/lib/controls/input/dynamic-input.model.d.ts +0 -35
- package/lib/controls/radio-group/dynamic-radio-group.component.d.ts +0 -13
- package/lib/controls/radio-group/dynamic-radio-group.model.d.ts +0 -14
- package/lib/controls/readonly/dynamic-readonly.component.d.ts +0 -10
- package/lib/controls/readonly/dynamic-readonly.model.d.ts +0 -9
- package/lib/controls/select/dynamic-select.component.d.ts +0 -13
- package/lib/controls/select/dynamic-select.model.d.ts +0 -19
- package/lib/controls/textarea/dynamic-textarea.component.d.ts +0 -18
- package/lib/controls/textarea/dynamic-textarea.model.d.ts +0 -46
- package/lib/models/classes/dynamic-form-field-base.d.ts +0 -23
- package/lib/models/classes/dynamic-form-field-model.d.ts +0 -26
- package/lib/models/classes/dynamic-form-field-option-model.d.ts +0 -41
- package/lib/models/classes/dynamic-form-field-value-model.d.ts +0 -33
- package/lib/models/classes/dynamic-form-validators.d.ts +0 -48
- package/lib/models/constants/dynamic-relations.const.d.ts +0 -15
- package/lib/models/index.d.ts +0 -11
- package/lib/models/interfaces/dynamic-form-field-config.interface.d.ts +0 -68
- package/lib/models/interfaces/dynamic-form-field-event.interface.d.ts +0 -10
- package/lib/models/interfaces/dynamic-form-field-relation.interface.d.ts +0 -28
- package/lib/models/interfaces/dynamic-form-validator.interface.d.ts +0 -6
- package/lib/models/tokens/dynamic-form-field-map-fn.token.d.ts +0 -2
- package/lib/models/types/dynamic-form-config.type.d.ts +0 -2
- package/lib/models/types/related-form-controls.type.d.ts +0 -4
- package/lib/services/dynamic-form-relations.service.d.ts +0 -26
- package/lib/services/dynamic-form.service.d.ts +0 -46
- package/lib/services/dynamic-validations.service.d.ts +0 -20
- package/public-api.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
# NgxDynamicForm
|
|
1
|
+
# NgxDynamicForm 🚀
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://angular.io/)
|
|
4
|
+
[](https://www.npmjs.com/package/@olafvv/ngx-dynamic-form)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**NgxDynamicForm** is a modern, strongly-typed, and highly performant library for dynamically generating Reactive Forms based on a JSON-like configuration model.
|
|
7
|
+
|
|
8
|
+
Leveraging cutting-edge Angular features like **Strict Typed Forms** and **Signal Inputs**, this library gives you top-tier developer experience without the boilerplate. Use our polished Angular Material built-in controls out of the box, or easily plug in your own custom fields!
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
> _Tip: Replace the placeholder above with a GIF of the dynamic form in action!_
|
|
13
|
+
|
|
14
|
+
[](#) _(Link this to a live playground playground once deployed!)_
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📖 Table of contents
|
|
6
19
|
|
|
7
20
|
- [Getting Started](#getting-started)
|
|
8
|
-
- [Usage](#usage)
|
|
21
|
+
- [Basic Usage (3 Steps)](#usage)
|
|
9
22
|
- [Validators](#validators)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- [Operator](#operator)
|
|
16
|
-
- [Custom Form Controls](#custom-form-controls)
|
|
23
|
+
- [Relations (Conditional Logic)](#relations)
|
|
24
|
+
- [Built-in Form Controls](#built-in-form-controls)
|
|
25
|
+
- [Creating Custom Form Controls](#custom-form-controls)
|
|
26
|
+
|
|
27
|
+
---
|
|
17
28
|
|
|
18
29
|
## Getting started
|
|
19
30
|
|
|
20
|
-
##### 1.
|
|
31
|
+
##### 1. Configure Angular Material
|
|
21
32
|
|
|
22
|
-
|
|
33
|
+
Make sure to install and configure [Angular Material](https://material.angular.io/guide/getting-started) if you want to use the built-in form controls.
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
##### 2. Install the library
|
|
36
|
+
|
|
37
|
+
```bash
|
|
25
38
|
npm i --save @olafvv/ngx-dynamic-form
|
|
26
39
|
```
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
##### 1. Import the (standalone) component
|
|
41
|
+
---
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
import { DynamicFormComponent } from 'olafvv/ngx-dynamic-form';
|
|
34
|
-
|
|
35
|
-
@NgModule({
|
|
36
|
-
//...
|
|
37
|
-
imports: [DynamicFormComponent]
|
|
38
|
-
//...
|
|
39
|
-
})
|
|
40
|
-
export class AppModule {}
|
|
41
|
-
```
|
|
43
|
+
## Usage
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
##### 1. Import the standalone component
|
|
44
46
|
|
|
45
47
|
```ts
|
|
46
48
|
import { DynamicFormComponent } from '@olafvv/ngx-dynamic-form';
|
|
47
49
|
|
|
48
50
|
@Component({
|
|
49
51
|
standalone: true,
|
|
50
|
-
imports: [DynamicFormComponent]
|
|
52
|
+
imports: [DynamicFormComponent, ReactiveFormsModule]
|
|
51
53
|
//...
|
|
52
54
|
})
|
|
53
|
-
export class AppComponent {
|
|
54
|
-
//...
|
|
55
|
-
}
|
|
55
|
+
export class AppComponent {}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
##### 2. Define your form configuration
|
|
59
59
|
|
|
60
|
-
Create the
|
|
60
|
+
Create the structured configuration for the form. This is a two-dimensional array where each array is a row in the form layout.
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
|
-
import { DynamicInput, DynamicTextarea,
|
|
63
|
+
import { DynamicInput, DynamicTextarea, DynamicFormConfig } from '@olafvv/ngx-dynamic-form';
|
|
64
64
|
|
|
65
65
|
export const SAMPLE_FORM: DynamicFormConfig = [
|
|
66
66
|
[
|
|
@@ -70,13 +70,6 @@ export const SAMPLE_FORM: DynamicFormConfig = [
|
|
|
70
70
|
label: 'Name'
|
|
71
71
|
})
|
|
72
72
|
],
|
|
73
|
-
[
|
|
74
|
-
new DynamicInput({
|
|
75
|
-
name: 'email',
|
|
76
|
-
inputType: 'email',
|
|
77
|
-
label: 'Email address'
|
|
78
|
-
})
|
|
79
|
-
],
|
|
80
73
|
[
|
|
81
74
|
new DynamicTextarea({
|
|
82
75
|
name: 'message',
|
|
@@ -88,49 +81,53 @@ export const SAMPLE_FORM: DynamicFormConfig = [
|
|
|
88
81
|
];
|
|
89
82
|
```
|
|
90
83
|
|
|
91
|
-
##### 3. Create
|
|
84
|
+
##### 3. Create the Form & Render it!
|
|
92
85
|
|
|
93
|
-
|
|
86
|
+
Use the library's `DynamicFormService` to generate a strict, reactive `FormGroup` and pass tis `FormGroup` and the configuration to the `<dynamic-form>` component inside the template.
|
|
94
87
|
|
|
95
88
|
```ts
|
|
96
|
-
import {
|
|
97
|
-
import { DynamicFormService } from '@olafvv/ngx-dynamic-form;
|
|
89
|
+
import { Component, inject } from '@angular/core';
|
|
90
|
+
import { DynamicFormService, DynamicFormConfig } from '@olafvv/ngx-dynamic-form';
|
|
91
|
+
import { SAMPLE_FORM } from './sample-form';
|
|
98
92
|
|
|
99
93
|
@Component({
|
|
100
|
-
|
|
94
|
+
standalone: true,
|
|
95
|
+
imports: [DynamicFormComponent],
|
|
96
|
+
template: `
|
|
97
|
+
<form [formGroup]="formGroup">
|
|
98
|
+
<!-- 🚀 Render the dynamic form dynamically! -->
|
|
99
|
+
<dynamic-form
|
|
100
|
+
[group]="formGroup"
|
|
101
|
+
[formConfig]="formConfig" />
|
|
102
|
+
</form>
|
|
103
|
+
`
|
|
101
104
|
})
|
|
102
|
-
export class
|
|
103
|
-
|
|
104
|
-
sampleFormGroup: FormGroup<SampleFormModel> = this.dynamicFormService.createFormGroup(this.sampleFormConfig);
|
|
105
|
+
export class MyFormComponent {
|
|
106
|
+
private dynamicFormService = inject(DynamicFormService);
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
formConfig: DynamicFormConfig = SAMPLE_FORM;
|
|
109
|
+
formGroup = this.dynamicFormService.createFormGroup(this.formConfig);
|
|
107
110
|
}
|
|
108
111
|
```
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
To add the form to your template, you have to use the selector `<dynamic-form></dynamic-form>` and pass the FormGroup and configuration as inputs:
|
|
113
|
-
|
|
114
|
-
```html
|
|
115
|
-
<form [formGroup]="sampleFormGroup">
|
|
116
|
-
<dynamic-form [group]="sampleFormGroup" [formConfig]="sampleFormConfig">
|
|
117
|
-
</form>
|
|
118
|
-
```
|
|
113
|
+
---
|
|
119
114
|
|
|
120
115
|
## Validators
|
|
121
116
|
|
|
122
|
-
This library comes with a set of built-in
|
|
123
|
-
|
|
124
|
-
To use the validators, pass them inside the validators array of the field configuration:
|
|
117
|
+
This library comes with a set of built-in formatters mapped seamlessly to standard [Angular Validators](https://angular.dev/api/forms/Validators). They are provided via static methods inside `DynamicFormValidators` (e.g. `DynamicFormValidators.required()`).
|
|
125
118
|
|
|
126
119
|
```ts
|
|
127
120
|
import { DynamicFormValidators } from '@olafvv/ngx-dynamic-form';
|
|
128
121
|
|
|
129
122
|
export const SAMPLE_FORM = [
|
|
130
123
|
[
|
|
131
|
-
new
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
new DynamicInput({
|
|
125
|
+
name: 'email',
|
|
126
|
+
inputType: 'email',
|
|
127
|
+
validators: [
|
|
128
|
+
DynamicFormValidators.required('Email address is required!'),
|
|
129
|
+
DynamicFormValidators.email('Please provide a valid email')
|
|
130
|
+
]
|
|
134
131
|
})
|
|
135
132
|
]
|
|
136
133
|
];
|
|
@@ -163,115 +160,41 @@ class DynamicFormValidators {
|
|
|
163
160
|
|
|
164
161
|
#### Custom Validators
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Read more about creating custom Angular validators on the following page:
|
|
169
|
-
|
|
170
|
-
> https://blog.angular-university.io/angular-custom-validators/
|
|
171
|
-
|
|
172
|
-
E.g. a validator to require a minimum time:
|
|
163
|
+
You can easily provide custom validation logic by passing an object of type `DynamicFormValidator` into the `validators` property.
|
|
173
164
|
|
|
174
165
|
```ts
|
|
175
|
-
function minTimeValidatorFn(minTime: string): ValidatorFn {
|
|
176
|
-
return (control: AbstractControl): ValidationErrors | null => {
|
|
177
|
-
const inputTime = control.value as string;
|
|
178
|
-
|
|
179
|
-
if (!inputTime) return null;
|
|
180
|
-
|
|
181
|
-
const minTimeObj = new Date(`2000-01-01T${minTime}`);
|
|
182
|
-
const inputTimeObj = new Date(`2000-01-01T${inputTime}`);
|
|
183
|
-
|
|
184
|
-
return inputTimeObj >= minTimeObj ? null : { minTime: true };
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
166
|
export const minTimeValidator: (minTime: string, msg?: string) => DynamicFormValidator = (minTime: string, msg?: string) => ({
|
|
189
167
|
name: 'minTime',
|
|
190
|
-
validator: minTimeValidatorFn(minTime),
|
|
191
|
-
message: msg ?? `
|
|
168
|
+
validator: minTimeValidatorFn(minTime), // Your custom function returning an Angular ValidationErrors object
|
|
169
|
+
message: msg ?? `Minimum time allowed is ${minTime}`
|
|
192
170
|
});
|
|
193
171
|
```
|
|
194
172
|
|
|
195
|
-
|
|
196
|
-
import { minTimeValidator } from './min-time-validator.ts';
|
|
197
|
-
|
|
198
|
-
//..
|
|
199
|
-
|
|
200
|
-
const formConfig: DynamicFormConfig = [
|
|
201
|
-
[
|
|
202
|
-
new DynamicInput({
|
|
203
|
-
name: 'time',
|
|
204
|
-
inputType: 'time',
|
|
205
|
-
validators: [minTimeValidator('11:00')]
|
|
206
|
-
})
|
|
207
|
-
]
|
|
208
|
-
];
|
|
209
|
-
```
|
|
173
|
+
---
|
|
210
174
|
|
|
211
175
|
## Relations
|
|
212
176
|
|
|
213
|
-
Sometimes you want to create
|
|
177
|
+
Sometimes you want to create interconnected logic between fields (e.g., hiding a passport number input unless the document type is set to "Passport"). NgxDynamicForm handles conditionally reactive states natively using **Relations**.
|
|
178
|
+
|
|
179
|
+
Each relation defines an Action Type, a source condition, and an operator.
|
|
214
180
|
|
|
215
|
-
|
|
181
|
+
##### DynamicFormFieldRelation type
|
|
216
182
|
|
|
217
183
|
```ts
|
|
218
|
-
|
|
184
|
+
type DynamicFormFieldRelation {
|
|
219
185
|
actionType: RelationActionType;
|
|
220
186
|
conditions: RelationCondition[];
|
|
221
187
|
operator?: RelationOperator;
|
|
222
188
|
}
|
|
223
189
|
```
|
|
224
190
|
|
|
225
|
-
##### Action
|
|
226
|
-
|
|
227
|
-
The action type is the type of state the field should be when the conditions are met. Right now the library contains three different action types:
|
|
191
|
+
##### Action Types
|
|
228
192
|
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
-
|
|
193
|
+
- `DISABLED` / `ENABLED`
|
|
194
|
+
- `HIDDEN` / `VISIBLE`
|
|
195
|
+
- `REQUIRED` / `OPTIONAL`
|
|
232
196
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
enum RelationActionType {
|
|
237
|
-
DISABLED = 'DISABLED',
|
|
238
|
-
ENABLED = 'ENABLED',
|
|
239
|
-
HIDDEN = 'HIDDEN',
|
|
240
|
-
VISIBLE = 'VISIBLE',
|
|
241
|
-
REQUIRED = 'REQUIRED',
|
|
242
|
-
OPTIONAL = 'OPTIONAL'
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
##### Conditions
|
|
247
|
-
|
|
248
|
-
The conditions is an array of `RelationCondition` objects. Each condition contains the name of the field the related field is depended on and a value when the condition is met:
|
|
249
|
-
|
|
250
|
-
```ts
|
|
251
|
-
interface RelationCondition {
|
|
252
|
-
// The name of the field this field is related to
|
|
253
|
-
fieldName: string;
|
|
254
|
-
// A method which has to return a boolean. Returning true means the condition is met
|
|
255
|
-
value: (val: any) => boolean;
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
##### Operator
|
|
260
|
-
|
|
261
|
-
The operator is an optional property and determines if all or any one of the conditions have to return true to trigger the required state of the field. By default the value of this property is `RelationOperator.AND` and is only used when there are more than 1 conditions.
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
enum RelationOperator {
|
|
265
|
-
// All conditions have to equal true
|
|
266
|
-
AND = 'AND',
|
|
267
|
-
// On of the conditions have to equal true
|
|
268
|
-
OR = 'OR'
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
##### Example
|
|
273
|
-
|
|
274
|
-
Here is an example of a field called 'passportNumber' which has to be visible when the value of the field named 'documentType' equals to 'passport':
|
|
197
|
+
##### Example: Conditional Visibility
|
|
275
198
|
|
|
276
199
|
```ts
|
|
277
200
|
const formConfig = [
|
|
@@ -280,21 +203,22 @@ const formConfig = [
|
|
|
280
203
|
name: 'documentType',
|
|
281
204
|
label: 'Document type',
|
|
282
205
|
options: [
|
|
283
|
-
{ label: 'Passport', value: 'passport' }
|
|
284
|
-
|
|
206
|
+
{ label: 'Passport', value: 'passport' },
|
|
207
|
+
{ label: 'ID Card', value: 'id' }
|
|
285
208
|
]
|
|
286
209
|
})
|
|
287
|
-
]
|
|
288
|
-
|
|
210
|
+
],
|
|
211
|
+
[
|
|
212
|
+
new DynamicInput({
|
|
289
213
|
name: 'passportNumber',
|
|
290
214
|
label: 'Passport number',
|
|
291
215
|
relations: [
|
|
292
216
|
{
|
|
293
|
-
actionType: RelationActionType.VISIBLE,
|
|
217
|
+
actionType: RelationActionType.VISIBLE, // Make this field visible...
|
|
294
218
|
conditions: [
|
|
295
219
|
{
|
|
296
|
-
fieldName: 'documentType',
|
|
297
|
-
value: (val: string) => val === 'passport'
|
|
220
|
+
fieldName: 'documentType', // ...when 'documentType' field...
|
|
221
|
+
value: (val: string) => val === 'passport' // ...equals 'passport'
|
|
298
222
|
}
|
|
299
223
|
]
|
|
300
224
|
}
|
|
@@ -304,75 +228,114 @@ const formConfig = [
|
|
|
304
228
|
];
|
|
305
229
|
```
|
|
306
230
|
|
|
307
|
-
|
|
231
|
+
---
|
|
308
232
|
|
|
309
|
-
|
|
233
|
+
## Built-in form controls
|
|
310
234
|
|
|
311
|
-
The library comes with
|
|
235
|
+
The library comes with a battle-tested set of built-in form controls utilizing **Angular Material**.
|
|
312
236
|
|
|
313
|
-
|
|
|
314
|
-
| :---------------- |
|
|
315
|
-
| Button
|
|
316
|
-
| Button toggle
|
|
317
|
-
| Checkbox
|
|
318
|
-
| Input
|
|
319
|
-
| Radio group
|
|
320
|
-
| Readonly
|
|
321
|
-
| Select
|
|
322
|
-
| Textarea
|
|
237
|
+
| Control Name | Description |
|
|
238
|
+
| :---------------- | :------------------------------------------------------------------------------------ |
|
|
239
|
+
| **Button** | Highly-customizable actionable button with a click callback. |
|
|
240
|
+
| **Button toggle** | Horizontal toggle groupings ideal for single or multi-select radio behavior. |
|
|
241
|
+
| **Checkbox** | Standard binary state checkbox. |
|
|
242
|
+
| **Input** | Standard HTML5 inputs with embedded floating labels, validation hints, and matchings. |
|
|
243
|
+
| **Radio group** | Vertically or horizontally stacked radio selectors. |
|
|
244
|
+
| **Readonly** | Presentational un-editable field representation. |
|
|
245
|
+
| **Select** | Dropdown menu powered by `mat-select` or native `<select>`. |
|
|
246
|
+
| **Textarea** | Auto-resizing text area input. |
|
|
323
247
|
|
|
324
|
-
|
|
248
|
+
---
|
|
325
249
|
|
|
326
250
|
## Custom Form Controls
|
|
327
251
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
> https://blog.angular-university.io/angular-custom-form-controls/
|
|
331
|
-
|
|
332
|
-
After that follow the following steps.
|
|
333
|
-
|
|
334
|
-
#### 1. Create a model
|
|
335
|
-
|
|
336
|
-
First step is to create a model and interface for the custom control containing the control specific properties for the configuration definitions, extending the base interface/model from the library.
|
|
337
|
-
Also, you need to create an (unique) name for the type of the model/.
|
|
338
|
-
|
|
339
|
-
For example if you're creating a control with a slider to select a value between 0 and 10:
|
|
252
|
+
NgxDynamicForm was built with modern extensibility in mind. Creating a brand new dynamic control is easy using the generic `DynamicFormFieldBase<M>` abstraction.
|
|
340
253
|
|
|
341
|
-
|
|
254
|
+
### 1. Create a Model & Options Type
|
|
342
255
|
|
|
343
|
-
|
|
256
|
+
First, define a type for your specific options and a model class that Angular will parse.
|
|
344
257
|
|
|
345
258
|
```typescript
|
|
346
|
-
|
|
347
|
-
min: number;
|
|
348
|
-
max: number;
|
|
349
|
-
step: number;
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
#### Model
|
|
354
|
-
|
|
355
|
-
The model is what is called when creating the form config (e.g. `new SliderInput(sliderConfig)`).
|
|
356
|
-
The model contains the same properties defined in the configuration interface, and provides them with a value from the config or a default value.
|
|
259
|
+
import { DynamicFormFieldValueConfig, DynamicFormFieldValueModel } from '@olafvv/ngx-dynamic-form';
|
|
357
260
|
|
|
358
|
-
|
|
261
|
+
export type SliderInputConfig = DynamicFormFieldValueConfig<number | null> & {
|
|
262
|
+
min?: number;
|
|
263
|
+
max?: number;
|
|
264
|
+
step?: number;
|
|
265
|
+
};
|
|
359
266
|
|
|
360
|
-
```typescript
|
|
361
267
|
export const DYNAMIC_FORM_FIELD_SLIDER = 'slider';
|
|
362
268
|
|
|
363
269
|
export class SliderInput extends DynamicFormFieldValueModel<number | null> {
|
|
364
270
|
public min: number;
|
|
365
271
|
public max: number;
|
|
366
272
|
public step: number;
|
|
367
|
-
|
|
368
|
-
readonly type = DYNAMIC_FORM_FIELD_SLIDER;
|
|
273
|
+
public readonly type = DYNAMIC_FORM_FIELD_SLIDER;
|
|
369
274
|
|
|
370
275
|
constructor(config: SliderInputConfig) {
|
|
371
276
|
super(config);
|
|
372
|
-
|
|
373
277
|
this.min = config.min ?? 0;
|
|
374
278
|
this.max = config.max ?? 10;
|
|
375
279
|
this.step = config.step ?? 1;
|
|
376
280
|
}
|
|
377
281
|
}
|
|
378
282
|
```
|
|
283
|
+
|
|
284
|
+
### 2. Create the Strongly-Typed Component
|
|
285
|
+
|
|
286
|
+
Because this library uses **Angular 17+ Signal Inputs**, your custom component should extend `DynamicFormFieldBase` natively.
|
|
287
|
+
|
|
288
|
+
**`slider.component.ts`**:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { Component, input } from '@angular/core';
|
|
292
|
+
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
293
|
+
import { DynamicFormFieldBase } from '@olafvv/ngx-dynamic-form';
|
|
294
|
+
import { SliderInput } from './slider-input.model';
|
|
295
|
+
|
|
296
|
+
@Component({
|
|
297
|
+
standalone: true,
|
|
298
|
+
selector: 'app-custom-slider',
|
|
299
|
+
imports: [ReactiveFormsModule],
|
|
300
|
+
template: `
|
|
301
|
+
<div
|
|
302
|
+
class="slider-wrapper"
|
|
303
|
+
[formGroup]="group()">
|
|
304
|
+
<label>{{ model().label }}</label>
|
|
305
|
+
|
|
306
|
+
<!-- Your custom markup here -->
|
|
307
|
+
<input
|
|
308
|
+
type="range"
|
|
309
|
+
[min]="model().min"
|
|
310
|
+
[max]="model().max"
|
|
311
|
+
[step]="model().step"
|
|
312
|
+
[formControlName]="model().name" />
|
|
313
|
+
</div>
|
|
314
|
+
`
|
|
315
|
+
})
|
|
316
|
+
// Notice how passing `SliderInput` strictly types the generic base class!
|
|
317
|
+
export class SliderComponent extends DynamicFormFieldBase<SliderInput> {
|
|
318
|
+
public model = input.required<SliderInput>();
|
|
319
|
+
public group = input.required<FormGroup>();
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 3. Registering the Control
|
|
324
|
+
|
|
325
|
+
Finally, tell the `DynamicFormService` how to connect the `SliderInput` model (`slider`) to the `SliderComponent` rendering engine via dependency injection using `DYNAMIC_FORM_CONTROL_MAP`.
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
@NgModule({
|
|
329
|
+
...
|
|
330
|
+
providers: [
|
|
331
|
+
{
|
|
332
|
+
provide: DYNAMIC_FORM_FIELD_MAP,
|
|
333
|
+
useValue: {
|
|
334
|
+
[DYNAMIC_FORM_FIELD_SLIDER]: SliderComponent
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
})
|
|
339
|
+
export class AppModule {}
|
|
340
|
+
|
|
341
|
+
```
|