@olafvv/ngx-dynamic-form 20.0.0 → 20.2.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 CHANGED
@@ -1,138 +1,188 @@
1
- # NgxDynamicForm
1
+ # NgxDynamicForm 🚀
2
2
 
3
- This library is to create a form based on a JSON model. The forms is based on the `ReactiveFormsModule` from Angular and contains a set of pre-built form controls using the Angular Material library. Using the built-in form controls is optional, you can use your own components to use as form controls inside the dynamic form. See [Custom Form Controls](#custom-form-controls) form more information.
3
+ [![Angular Version](https://img.shields.io/badge/Angular-17.1%2B-red)](https://angular.io/)
4
+ [![Npm Version](https://img.shields.io/npm/v/@olafvv/ngx-dynamic-form)](https://www.npmjs.com/package/@olafvv/ngx-dynamic-form)
4
5
 
5
- ## Table of contents
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
+ ![NgxDynamicForm Showcase](https://via.placeholder.com/800x400.png?text=Interactive+Form+Animation+Here)
11
+
12
+ > _Tip: Replace the placeholder above with a GIF of the dynamic form in action!_
13
+
14
+ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](#) _(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)
22
+ - [Layout](#layout)
23
+ - [Field Width Control](#field-width-control)
9
24
  - [Validators](#validators)
10
- - [Built-in validators](#built-in-validators)
11
- - [Custom validators](#custom-validators)
12
- - [Relations](#relations)
13
- - [Action type](#action-type)
14
- - [Conditions](#conditions)
15
- - [Operator](#operator)
16
- - [Custom Form Controls](#custom-form-controls)
25
+ - [Relations (Conditional Logic)](#relations)
26
+ - [Built-in Form Controls](#built-in-form-controls)
27
+ - [Creating Custom Form Controls](#custom-form-controls)
28
+
29
+ ---
17
30
 
18
31
  ## Getting started
19
32
 
20
- ##### 1. 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
33
+ ##### 1. Configure Angular Material
21
34
 
22
- ##### 2. Install the library using npm:
35
+ 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
36
 
24
- ```
37
+ ##### 2. Install the library
38
+
39
+ ```bash
25
40
  npm i --save @olafvv/ngx-dynamic-form
26
41
  ```
27
42
 
28
- ## Usage
29
-
30
- ##### 1. Import the (standalone) component
43
+ ---
31
44
 
32
- ```ts
33
- import { DynamicFormComponent } from 'olafvv/ngx-dynamic-form';
34
-
35
- @NgModule({
36
- //...
37
- imports: [DynamicFormComponent]
38
- //...
39
- })
40
- export class AppModule {}
41
- ```
45
+ ## Usage
42
46
 
43
- **Or when using a standalone application/components:**
47
+ ##### 1. Import the standalone component
44
48
 
45
49
  ```ts
46
50
  import { DynamicFormComponent } from '@olafvv/ngx-dynamic-form';
47
51
 
48
52
  @Component({
49
53
  standalone: true,
50
- imports: [DynamicFormComponent]
54
+ imports: [DynamicFormComponent, ReactiveFormsModule]
51
55
  //...
52
56
  })
53
- export class AppComponent {
54
- //...
55
- }
57
+ export class AppComponent {}
56
58
  ```
57
59
 
58
60
  ##### 2. Define your form configuration
59
61
 
60
- Create the JSON configuration for the form. This is a two-dimensional array where each array is a row in the form. So if you want multiple fields next to each other, define them in a single array. See [Form layout](#form-layout) for more information.
62
+ Create an array of field models. Each model describes a single field its type, label, validators, and any conditional logic.
61
63
 
62
64
  ```ts
63
- import { DynamicInput, DynamicTextarea, DynamicFormValidators } from '@olafvv/ngx-dynamic-form';
65
+ import { DynamicInput, DynamicTextarea, DynamicStaticText, DynamicFormConfig } from '@olafvv/ngx-dynamic-form';
64
66
 
65
67
  export const SAMPLE_FORM: DynamicFormConfig = [
66
- [
67
- new DynamicInput({
68
- name: 'name',
69
- inputType: 'text',
70
- label: 'Name'
71
- })
72
- ],
73
- [
74
- new DynamicInput({
75
- name: 'email',
76
- inputType: 'email',
77
- label: 'Email address'
78
- })
79
- ],
80
- [
81
- new DynamicTextarea({
82
- name: 'message',
83
- label: 'Your message',
84
- maxLength: 200,
85
- rows: 5
86
- })
87
- ]
68
+ new DynamicStaticText({
69
+ name: 'intro',
70
+ value: {
71
+ title: 'Contact Information',
72
+ text: 'Please fill in your details below.'
73
+ }
74
+ }),
75
+ new DynamicInput({
76
+ name: 'name',
77
+ inputType: 'text',
78
+ label: 'Name'
79
+ }),
80
+ new DynamicTextarea({
81
+ name: 'message',
82
+ label: 'Your message',
83
+ maxLength: 200,
84
+ rows: 5
85
+ })
88
86
  ];
89
87
  ```
90
88
 
91
- ##### 3. Create a FormGroup
89
+ ##### 3. Create the Form & Render it!
92
90
 
93
- This library provides the service `DynamicFormService` which you can use to create a FormGroup based on the created form configuration.
91
+ 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
92
 
95
93
  ```ts
96
- import { SAMPLE_FORM } from './sample-form.ts';
97
- import { DynamicFormService } from '@olafvv/ngx-dynamic-form;
94
+ import { Component, inject } from '@angular/core';
95
+ import { DynamicFormService, DynamicFormConfig } from '@olafvv/ngx-dynamic-form';
96
+ import { SAMPLE_FORM } from './sample-form';
98
97
 
99
98
  @Component({
100
- //...
99
+ standalone: true,
100
+ imports: [DynamicFormComponent],
101
+ template: `
102
+ <form [formGroup]="formGroup">
103
+ <!-- 🚀 Render the dynamic form dynamically! -->
104
+ <dynamic-form
105
+ [group]="formGroup"
106
+ [formConfig]="formConfig" />
107
+ </form>
108
+ `
101
109
  })
102
- export class AppComponent {
103
- sampleFormConfig: DynamicFormConfig = SAMPLE_FORM;
104
- sampleFormGroup: FormGroup<SampleFormModel> = this.dynamicFormService.createFormGroup(this.sampleFormConfig);
110
+ export class MyFormComponent {
111
+ private dynamicFormService = inject(DynamicFormService);
105
112
 
106
- constructor(private dynamicFormService: DynamicFormService) {}
113
+ formConfig: DynamicFormConfig = SAMPLE_FORM;
114
+ formGroup = this.dynamicFormService.createFormGroup(this.formConfig);
107
115
  }
108
116
  ```
109
117
 
110
- ##### 4. Add the `dynamic-form` inside the template
118
+ ---
119
+
120
+ ## Layout
121
+
122
+ By default, fields are stacked vertically — one per row. To arrange fields side-by-side, pass a `layout` input to `<dynamic-form>`. Each string in the array represents one **row**, containing space-separated field `name` values.
111
123
 
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:
124
+ ```ts
125
+ // component.ts
126
+ layout: string[] = [
127
+ 'firstName lastName', // two fields side-by-side
128
+ 'email', // full-width
129
+ 'street city zip', // three fields in a row
130
+ ];
131
+ ```
113
132
 
114
133
  ```html
115
- <form [formGroup]="sampleFormGroup">
116
- <dynamic-form [group]="sampleFormGroup" [formConfig]="sampleFormConfig">
117
- </form>
134
+ <!-- component.html -->
135
+ <dynamic-form
136
+ [group]="formGroup"
137
+ [formConfig]="formConfig"
138
+ [layout]="layout" />
118
139
  ```
119
140
 
120
- ## Validators
141
+ Fields not referenced in the layout are not rendered. Fields within a row share available space equally by default.
142
+
143
+ ---
144
+
145
+ ## Field Width Control
146
+
147
+ Field widths are controlled via **CSS custom properties**, keeping presentation cleanly separated from data configuration. Each field exposes a CSS variable named `--field-{name}-width`.
148
+
149
+ Since `<dynamic-form>` is rendered in your own component's template, you can set these variables from your component's scoped stylesheet — no `::ng-deep` or global styles required.
150
+
151
+ ```scss
152
+ // my-form.component.scss
153
+ dynamic-form {
154
+ --field-postcode-width: 25%; // postcode takes 25%
155
+ // street fills the remaining 75% automatically
156
+ }
157
+ ```
158
+
159
+ This approach also works natively with `@media` queries for fully responsive layouts:
160
+
161
+ ```scss
162
+ dynamic-form {
163
+ --field-postcode-width: 25%;
164
+
165
+ @media (max-width: 600px) {
166
+ --field-postcode-width: 100%; // stack on mobile
167
+ }
168
+ }
169
+ ```
170
+
171
+ ---
121
172
 
122
- This library comes with a set of built-in validators, based on the [Angular Validators](https://angular.dev/api/forms/Validators). All provided validators are static methods inside the class `DynamicFormValidators`, each returning a `DynamicFormValidator` (e.g. `DynamicFormValidators.required()`).
173
+ ## Validators
123
174
 
124
- To use the validators, pass them inside the validators array of the field configuration:
175
+ 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
176
 
126
177
  ```ts
127
178
  import { DynamicFormValidators } from '@olafvv/ngx-dynamic-form';
128
179
 
129
- export const SAMPLE_FORM = [
130
- [
131
- new DynamicFormInput({
132
- //..
133
- validators: [DynamicFormValidators.required('Custom error message')]
134
- })
135
- ]
180
+ export const SAMPLE_FORM: DynamicFormConfig = [
181
+ new DynamicInput({
182
+ name: 'email',
183
+ inputType: 'email',
184
+ validators: [DynamicFormValidators.required('Email address is required!'), DynamicFormValidators.email('Please provide a valid email')]
185
+ })
136
186
  ];
137
187
  ```
138
188
 
@@ -163,216 +213,180 @@ class DynamicFormValidators {
163
213
 
164
214
  #### Custom Validators
165
215
 
166
- Adding a custom validator is quite straightforward. All you have to do is to provide an object of the type `DynamicFormValidator` to the validators array of a field with a (unique) name, validatorFn and errorMessage.
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:
216
+ You can easily provide custom validation logic by passing an object of type `DynamicFormValidator` into the `validators` property.
173
217
 
174
218
  ```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
219
  export const minTimeValidator: (minTime: string, msg?: string) => DynamicFormValidator = (minTime: string, msg?: string) => ({
189
220
  name: 'minTime',
190
- validator: minTimeValidatorFn(minTime),
191
- message: msg ?? `Minimale tijd is ${minTime}`
221
+ validator: minTimeValidatorFn(minTime), // Your custom function returning an Angular ValidationErrors object
222
+ message: msg ?? `Minimum time allowed is ${minTime}`
192
223
  });
193
224
  ```
194
225
 
195
- ```ts
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
- ```
226
+ ---
210
227
 
211
228
  ## Relations
212
229
 
213
- Sometimes you want to create certain connections between fields in a form to control their state, based on the value of the other field. This library provides a way to configure these connections,called relations.
230
+ 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**.
231
+
232
+ Each relation defines an Action Type, a source condition, and an operator.
214
233
 
215
- Each relation is defined on the field that requires a specific state based on one or more conditions. Each relation, a `DynamicFormFieldRelation`, contains an actionType, one or more condition(s) and optionally an operator.
234
+ ##### DynamicFormFieldRelation type
216
235
 
217
236
  ```ts
218
- interface DynamicFormFieldRelation {
237
+ type DynamicFormFieldRelation {
219
238
  actionType: RelationActionType;
220
239
  conditions: RelationCondition[];
221
240
  operator?: RelationOperator;
222
241
  }
223
242
  ```
224
243
 
225
- ##### Action type
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:
228
-
229
- - Disable/Enable
230
- - Visible/Hidden
231
- - Required/Optional
232
-
233
- To set the type you have to use one of the provided enums:
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
- ```
244
+ ##### Action Types
271
245
 
272
- ##### Example
246
+ - `DISABLED` / `ENABLED`
247
+ - `HIDDEN` / `VISIBLE`
248
+ - `REQUIRED` / `OPTIONAL`
273
249
 
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':
250
+ ##### Example: Conditional Visibility
275
251
 
276
252
  ```ts
277
- const formConfig = [
278
- [
279
- new DynamicSelect({
280
- name: 'documentType',
281
- label: 'Document type',
282
- options: [
283
- { label: 'Passport', value: 'passport' }
284
- //..
285
- ]
286
- })
287
- ][
288
- new DymamicInput({
289
- name: 'passportNumber',
290
- label: 'Passport number',
291
- relations: [
292
- {
293
- actionType: RelationActionType.VISIBLE,
294
- conditions: [
295
- {
296
- fieldName: 'documentType',
297
- value: (val: string) => val === 'passport'
298
- }
299
- ]
300
- }
301
- ]
302
- })
303
- ]
253
+ const formConfig: DynamicFormConfig = [
254
+ new DynamicSelect({
255
+ name: 'documentType',
256
+ label: 'Document type',
257
+ options: [
258
+ { label: 'Passport', value: 'passport' },
259
+ { label: 'ID Card', value: 'id' }
260
+ ]
261
+ }),
262
+ new DynamicInput({
263
+ name: 'passportNumber',
264
+ label: 'Passport number',
265
+ relations: [
266
+ {
267
+ actionType: RelationActionType.VISIBLE, // Make this field visible...
268
+ conditions: [
269
+ {
270
+ fieldName: 'documentType', // ...when 'documentType' field...
271
+ value: (val: string) => val === 'passport' // ...equals 'passport'
272
+ }
273
+ ]
274
+ }
275
+ ]
276
+ })
304
277
  ];
305
278
  ```
306
279
 
307
- ## Built-in form controls
280
+ ---
308
281
 
309
- The library comes with a set of built-in form controls, using the Angular Material library. To make use of these you have to make sure to install and configure [Angular Material](https://material.angular.io).
282
+ ## Built-in form controls
310
283
 
311
- The library comes with the following form controls:
284
+ The library comes with a battle-tested set of built-in form controls utilizing **Angular Material**.
312
285
 
313
- | Form control name | Description |
314
- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
315
- | Button | Shows a button inside the form. Can be provided with a label and/or icon. You also provide a callback method for what happens when the button is clicked |
316
- | Button toggle | Based on the Angular Material component which shows a row of buttons, each holding a value (can be compared to radio buttons). It is possible to select one or multiple options |
317
- | Checkbox | Shows a checkbox, indicating a true or false value |
318
- | Input | Text based input using the HTML5 input element with Angular Material styling and animations. |
319
- | Radio group | Uses the `mat-radio-group` of Angular Material to show a group of radio buttons to select a single option |
320
- | Readonly | Shows the value of the control without the possibility to interact with it. |
321
- | Select | Uses the `mat-select` of Angular Material to show a list of options inside a dropdown. |
322
- | Textarea | The HTML5 `<textarea>` element with enchanced Angular Material styling and animations. |
286
+ | Control Name | Description |
287
+ | :---------------- | :------------------------------------------------------------------------------------ |
288
+ | **Button** | Highly-customizable actionable button with a click callback. |
289
+ | **Button toggle** | Horizontal toggle groupings ideal for single or multi-select radio behavior. |
290
+ | **Checkbox** | Standard binary state checkbox. |
291
+ | **Slide toggle** | Standard binary state slide toggle. |
292
+ | **Input** | Standard HTML5 inputs with embedded floating labels, validation hints, and matchings. |
293
+ | **Radio group** | Vertically or horizontally stacked radio selectors. |
294
+ | **Readonly** | Presentational un-editable field representation. |
295
+ | **Static Text** | Presentational text for titles and/or descriptions. |
296
+ | **Select** | Dropdown menu powered by `mat-select` or native `<select>`. |
297
+ | **Textarea** | Auto-resizing text area input. |
323
298
 
324
- Each control has a basic set of properties (see `DynamicFormFieldConfig`) that can be passed to the control's model. They also have a specific set of properties that only apply to the selected control.
299
+ ---
325
300
 
326
301
  ## Custom Form Controls
327
302
 
328
- Other than the build-in form controls, it is possible to plug in you own custom controls. Start out with creating a custom form control component:
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
303
+ NgxDynamicForm was built with modern extensibility in mind. Creating a brand new dynamic control is easy using the generic `DynamicFormFieldBase<M>` abstraction.
335
304
 
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/.
305
+ ### 1. Create a Model & Options Type
338
306
 
339
- For example if you're creating a control with a slider to select a value between 0 and 10:
340
-
341
- ##### Interface:
342
-
343
- The interface extends the base interface `DynamicFormFieldConfig` and expects a generic type describing the possible value(s) of the field. In this case that would be a number or null value.
307
+ First, define a type for your specific options and a model class that Angular will parse.
344
308
 
345
309
  ```typescript
346
- export interface SliderInputConfig extends DynamicFormFieldConfig<number | null> {
347
- min: number;
348
- max: number;
349
- step: number;
350
- }
351
- ```
310
+ import { DynamicFormFieldValueConfig, DynamicFormFieldValueModel } from '@olafvv/ngx-dynamic-form';
352
311
 
353
- #### Model
312
+ export type SliderInputConfig = DynamicFormFieldValueConfig<number | null> & {
313
+ min?: number;
314
+ max?: number;
315
+ step?: number;
316
+ };
354
317
 
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.
357
-
358
- Also, you need to create a field type token which we can later use to map the used model to the control component. In this case we created the token with the name `DYNAMIC_FORM_FIELD_SLIDER` with the value `slider`. The value HAS to be unique.
359
-
360
- ```typescript
361
318
  export const DYNAMIC_FORM_FIELD_SLIDER = 'slider';
362
319
 
363
320
  export class SliderInput extends DynamicFormFieldValueModel<number | null> {
364
321
  public min: number;
365
322
  public max: number;
366
323
  public step: number;
367
-
368
- readonly type = DYNAMIC_FORM_FIELD_SLIDER;
324
+ public readonly type = DYNAMIC_FORM_FIELD_SLIDER;
369
325
 
370
326
  constructor(config: SliderInputConfig) {
371
327
  super(config);
372
-
373
328
  this.min = config.min ?? 0;
374
329
  this.max = config.max ?? 10;
375
330
  this.step = config.step ?? 1;
376
331
  }
377
332
  }
378
333
  ```
334
+
335
+ ### 2. Create the Strongly-Typed Component
336
+
337
+ Because this library uses **Angular 17+ Signal Inputs**, your custom component should extend `DynamicFormFieldBase` natively.
338
+
339
+ **`slider.component.ts`**:
340
+
341
+ ```typescript
342
+ import { Component, input } from '@angular/core';
343
+ import { FormGroup, ReactiveFormsModule } from '@angular/forms';
344
+ import { DynamicFormFieldBase } from '@olafvv/ngx-dynamic-form';
345
+ import { SliderInput } from './slider-input.model';
346
+
347
+ @Component({
348
+ standalone: true,
349
+ selector: 'app-custom-slider',
350
+ imports: [ReactiveFormsModule],
351
+ template: `
352
+ <div
353
+ class="slider-wrapper"
354
+ [formGroup]="group()">
355
+ <label>{{ model().label }}</label>
356
+
357
+ <!-- Your custom markup here -->
358
+ <input
359
+ type="range"
360
+ [min]="model().min"
361
+ [max]="model().max"
362
+ [step]="model().step"
363
+ [formControlName]="model().name" />
364
+ </div>
365
+ `
366
+ })
367
+ // Notice how passing `SliderInput` strictly types the generic base class!
368
+ export class SliderComponent extends DynamicFormFieldBase<SliderInput> {
369
+ public model = input.required<SliderInput>();
370
+ public group = input.required<FormGroup>();
371
+ }
372
+ ```
373
+
374
+ ### 3. Registering the Control
375
+
376
+ Finally, tell the `DynamicFormService` how to connect the `SliderInput` model (`slider`) to the `SliderComponent` rendering engine via dependency injection using `DYNAMIC_FORM_CONTROL_MAP`.
377
+
378
+ ```ts
379
+ @NgModule({
380
+ ...
381
+ providers: [
382
+ {
383
+ provide: DYNAMIC_FORM_FIELD_MAP,
384
+ useValue: {
385
+ [DYNAMIC_FORM_FIELD_SLIDER]: SliderComponent
386
+ }
387
+ }
388
+ ]
389
+ })
390
+ export class AppModule {}
391
+
392
+ ```