0
# Utilities
1
2
Utility functions and helpers for form manipulation, data binding, reactive programming, and field configuration management.
3
4
## Capabilities
5
6
### Property Management
7
8
Utility functions for managing field properties and hidden properties.
9
10
```typescript { .api }
11
/**
12
* Define a hidden (non-enumerable) property on an object
13
* @param field - The object to define the property on
14
* @param prop - The property name
15
* @param defaultValue - The default value for the property
16
*/
17
function defineHiddenProp(field: any, prop: string, defaultValue: any): void;
18
19
/**
20
* Check if a field has a valid key for model binding
21
* @param field - The field configuration to check
22
* @returns True if field has a key
23
*/
24
function hasKey(field: FormlyFieldConfig): boolean;
25
```
26
27
**Usage Example:**
28
29
```typescript
30
import { defineHiddenProp, hasKey } from '@ngx-formly/core';
31
32
// Define a hidden property for internal use
33
const field: FormlyFieldConfig = { key: 'username', type: 'input' };
34
defineHiddenProp(field, '_internalId', 'field_123');
35
36
// Check if field can be bound to model
37
if (hasKey(field)) {
38
console.log('Field will be bound to model property:', field.key);
39
}
40
```
41
42
### Data Manipulation
43
44
Functions for cloning, merging, and retrieving field values.
45
46
```typescript { .api }
47
/**
48
* Deep clone a value, handling circular references and complex objects
49
* @param value - The value to clone
50
* @returns Deep cloned copy of the value
51
*/
52
function clone(value: any): any;
53
54
/**
55
* Reverse deep merge multiple objects, with later arguments taking precedence
56
* @param dest - The destination object
57
* @param args - Source objects to merge (later objects override earlier ones)
58
* @returns The merged object
59
*/
60
function reverseDeepMerge(dest: any, ...args: any[]): any;
61
62
/**
63
* Get the current value of a field from its form control or model
64
* @param field - The field configuration
65
* @returns The current field value
66
*/
67
function getFieldValue(field: FormlyFieldConfig): any;
68
```
69
70
**Usage Example:**
71
72
```typescript
73
import { clone, reverseDeepMerge, getFieldValue } from '@ngx-formly/core';
74
75
// Clone a complex object
76
const originalData = { user: { name: 'John', settings: { theme: 'dark' } } };
77
const clonedData = clone(originalData);
78
79
// Merge configuration objects
80
const baseConfig = { type: 'input', props: { label: 'Name' } };
81
const customConfig = { props: { placeholder: 'Enter name', required: true } };
82
const merged = reverseDeepMerge({}, baseConfig, customConfig);
83
// Result: { type: 'input', props: { label: 'Name', placeholder: 'Enter name', required: true } }
84
85
// Get current field value
86
const field: FormlyFieldConfig = {
87
key: 'email',
88
formControl: new FormControl('user@example.com')
89
};
90
const currentValue = getFieldValue(field); // 'user@example.com'
91
```
92
93
### Reactive Programming
94
95
Observable utilities for handling field changes and reactive updates.
96
97
```typescript { .api }
98
/**
99
* Create an observable from a field expression or value
100
* @param field - The field configuration
101
* @param expression - The expression to observe (string, function, or observable)
102
* @param defaultValue - Default value if expression is undefined
103
* @returns Observable that emits when the expression changes
104
*/
105
function observe<T>(
106
field: FormlyFieldConfig,
107
expression: string | ((field: FormlyFieldConfig) => T) | Observable<T>,
108
defaultValue?: T
109
): Observable<T>;
110
```
111
112
**Usage Example:**
113
114
```typescript
115
import { observe } from '@ngx-formly/core';
116
import { map, distinctUntilChanged } from 'rxjs/operators';
117
118
// Observe field property changes
119
const field: FormlyFieldConfig = {
120
key: 'email',
121
type: 'input',
122
expressions: {
123
'props.disabled': 'model.isReadonly'
124
}
125
};
126
127
// Observe the disabled state
128
observe(field, 'props.disabled', false)
129
.pipe(
130
distinctUntilChanged(),
131
map(disabled => disabled ? 'Field is disabled' : 'Field is enabled')
132
)
133
.subscribe(status => console.log(status));
134
135
// Observe using a function
136
observe(field, (f) => f.formControl?.valid, false)
137
.subscribe(isValid => {
138
console.log('Field validity changed:', isValid);
139
});
140
```
141
142
### FormlyGroup Template Component
143
144
Utility component for grouping fields without additional wrapper markup.
145
146
```typescript { .api }
147
/**
148
* Internal component for rendering field groups without wrapper markup
149
* Used by the form rendering system for fieldGroup configurations
150
*/
151
@Component({
152
selector: 'formly-group',
153
template: `<formly-field *ngFor="let f of field.fieldGroup" [field]="f"></formly-field>`
154
})
155
export class FormlyGroup {
156
/** The field configuration containing fieldGroup */
157
@Input() field: FormlyFieldConfig;
158
}
159
```
160
161
**Usage Example:**
162
163
```typescript
164
// Field configuration using fieldGroup
165
const fieldConfig: FormlyFieldConfig = {
166
fieldGroup: [
167
{
168
key: 'firstName',
169
type: 'input',
170
props: { label: 'First Name', required: true }
171
},
172
{
173
key: 'lastName',
174
type: 'input',
175
props: { label: 'Last Name', required: true }
176
}
177
],
178
fieldGroupClassName: 'row' // CSS class for the group
179
};
180
```
181
182
## Types
183
184
### Utility Function Types
185
186
```typescript { .api }
187
/**
188
* Type for field expression that can be a string, function, or observable
189
*/
190
type FieldExpression<T = any> = string | ((field: FormlyFieldConfig) => T) | Observable<T>;
191
192
/**
193
* Map of field expressions for different properties
194
*/
195
interface FieldExpressions {
196
[property: string]: FieldExpression;
197
198
/** Expression for CSS class names */
199
className?: FieldExpression<string>;
200
201
/** Expression for hiding the field */
202
hide?: FieldExpression<boolean>;
203
204
/** Expression for disabling the field */
205
'props.disabled'?: FieldExpression<boolean>;
206
207
/** Expression for making the field required */
208
'props.required'?: FieldExpression<boolean>;
209
}
210
211
/**
212
* Interface for objects that can be cloned
213
*/
214
interface Cloneable {
215
clone?(): any;
216
}
217
218
/**
219
* Options for merge operations
220
*/
221
interface MergeOptions {
222
/** Whether to merge arrays by concatenating */
223
arrayMerge?: boolean;
224
225
/** Whether to clone values during merge */
226
clone?: boolean;
227
}
228
```
229
230
### Expression Evaluation Types
231
232
```typescript { .api }
233
/**
234
* Context object passed to expression evaluation
235
*/
236
interface ExpressionContext {
237
/** The current field configuration */
238
field: FormlyFieldConfig;
239
240
/** The form model data */
241
model: any;
242
243
/** The current form state */
244
formState: any;
245
246
/** The form options */
247
options: FormlyFormOptions;
248
}
249
250
/**
251
* Result of expression evaluation
252
*/
253
interface ExpressionResult<T = any> {
254
/** The evaluated value */
255
value: T;
256
257
/** Whether the value has changed since last evaluation */
258
changed: boolean;
259
}
260
```
261
262
## Internal APIs
263
264
### Internal Utility Functions
265
266
These functions are marked with the `ɵ` prefix and are intended for internal use by the library.
267
268
```typescript { .api }
269
/**
270
* Internal function for defining hidden properties
271
* @internal
272
*/
273
function ɵdefineHiddenProp(field: any, prop: string, defaultValue: any): void;
274
275
/**
276
* Internal function for reverse deep merge operations
277
* @internal
278
*/
279
function ɵreverseDeepMerge(dest: any, ...args: any[]): any;
280
281
/**
282
* Internal function for getting field values
283
* @internal
284
*/
285
function ɵgetFieldValue(field: FormlyFieldConfig): any;
286
287
/**
288
* Internal function for cloning values
289
* @internal
290
*/
291
function ɵclone(value: any): any;
292
293
/**
294
* Internal function for creating observables
295
* @internal
296
*/
297
function ɵobserve<T>(
298
field: FormlyFieldConfig,
299
expression: FieldExpression<T>,
300
defaultValue?: T
301
): Observable<T>;
302
303
/**
304
* Internal function for checking if field has a key
305
* @internal
306
*/
307
function ɵhasKey(field: FormlyFieldConfig): boolean;
308
```
309
310
## Advanced Usage Examples
311
312
### Custom Expression Evaluator
313
314
```typescript
315
import { observe, FormlyFieldConfig } from '@ngx-formly/core';
316
import { combineLatest } from 'rxjs';
317
import { map } from 'rxjs/operators';
318
319
class CustomExpressionEvaluator {
320
evaluateConditionalVisibility(field: FormlyFieldConfig): Observable<boolean> {
321
const modelObservable = observe(field, 'model', {});
322
const formStateObservable = observe(field, 'formState', {});
323
324
return combineLatest([modelObservable, formStateObservable]).pipe(
325
map(([model, formState]) => {
326
// Complex visibility logic
327
return model.showAdvanced && formState.userLevel === 'expert';
328
})
329
);
330
}
331
}
332
```
333
334
### Custom Field Configuration Merger
335
336
```typescript
337
import { reverseDeepMerge, clone } from '@ngx-formly/core';
338
339
class FieldConfigurationManager {
340
mergeConfigurations(
341
baseConfig: FormlyFieldConfig,
342
...overrides: Partial<FormlyFieldConfig>[]
343
): FormlyFieldConfig {
344
// Clone base configuration to avoid mutations
345
const clonedBase = clone(baseConfig);
346
347
// Merge all overrides
348
return reverseDeepMerge(clonedBase, ...overrides);
349
}
350
351
createVariant(
352
baseConfig: FormlyFieldConfig,
353
variant: 'readonly' | 'required' | 'hidden'
354
): FormlyFieldConfig {
355
const variants = {
356
readonly: { props: { readonly: true } },
357
required: { props: { required: true } },
358
hidden: { hide: true }
359
};
360
361
return this.mergeConfigurations(baseConfig, variants[variant]);
362
}
363
}
364
```