0
# Input/Output Management
1
2
Type-safe system for binding inputs and outputs to dynamic components at runtime, supporting both function handlers and complex output expressions with additional arguments and custom contexts.
3
4
## Capabilities
5
6
### Dynamic I/O Directive
7
8
Main directive for managing dynamic inputs and outputs on ndc-dynamic components.
9
10
```typescript { .api }
11
/**
12
* Manages dynamic inputs and outputs for ndc-dynamic components
13
* Automatically updates component properties and event handlers when inputs change
14
*/
15
@Directive({
16
selector: '[ndcDynamicInputs],[ndcDynamicOutputs]',
17
standalone: true,
18
exportAs: 'ndcDynamicIo'
19
})
20
export class DynamicIoDirective implements DoCheck {
21
/** Dynamic input bindings for the component */
22
@Input() ndcDynamicInputs?: InputsType | null;
23
24
/** Dynamic output bindings for the component */
25
@Input() ndcDynamicOutputs?: OutputsType | null;
26
}
27
```
28
29
### I/O Service
30
31
Core service for managing dynamic inputs and outputs with lifecycle management.
32
33
```typescript { .api }
34
/**
35
* Core service for managing dynamic inputs and outputs
36
* Handles property updates and event subscription management
37
*/
38
@Injectable()
39
export class IoService implements OnDestroy {
40
/**
41
* Updates component inputs and outputs
42
* @param inputs - Object mapping property names to values
43
* @param outputs - Object mapping event names to handlers
44
*/
45
update(inputs?: InputsType | null, outputs?: OutputsType | null): void;
46
}
47
48
/**
49
* Configuration options for IoService
50
*/
51
@Injectable({ providedIn: 'root' })
52
export class IoServiceOptions {
53
/** Whether to track output changes for optimization */
54
trackOutputChanges: boolean = false;
55
}
56
```
57
58
### I/O Factory Service
59
60
Factory service for creating IoService instances with custom configuration.
61
62
```typescript { .api }
63
/**
64
* Factory for creating IoService instances
65
* Enables custom configuration per component instance
66
*/
67
@Injectable({ providedIn: 'root' })
68
export class IoFactoryService {
69
/**
70
* Creates a new IoService instance
71
* @param componentInjector - Component injector providing ComponentRef access
72
* @param ioOptions - Optional configuration for the service
73
* @returns Configured IoService instance
74
*/
75
create(
76
componentInjector: DynamicComponentInjector,
77
ioOptions?: IoServiceOptions & IoFactoryServiceOptions
78
): IoService;
79
}
80
81
/**
82
* Additional options for IoFactoryService.create method
83
*/
84
interface IoFactoryServiceOptions {
85
/** Optional injector override */
86
injector?: Injector;
87
}
88
```
89
90
**Usage Examples:**
91
92
```typescript
93
import { Component } from '@angular/core';
94
import { DynamicComponent, DynamicIoDirective, InputsType, OutputsType } from 'ng-dynamic-component';
95
96
// Basic input/output binding
97
@Component({
98
standalone: true,
99
imports: [DynamicComponent, DynamicIoDirective],
100
template: `
101
<ndc-dynamic
102
[ndcDynamicComponent]="component"
103
[ndcDynamicInputs]="inputs"
104
[ndcDynamicOutputs]="outputs">
105
</ndc-dynamic>
106
`
107
})
108
export class BasicIoExampleComponent {
109
component = MyFormComponent;
110
111
inputs: InputsType = {
112
title: 'User Registration',
113
required: true,
114
placeholder: 'Enter your name',
115
maxLength: 50
116
};
117
118
outputs: OutputsType = {
119
onSubmit: (formData: any) => this.handleSubmit(formData),
120
onCancel: () => this.handleCancel(),
121
onChange: (value: string) => console.log('Value changed:', value)
122
};
123
124
handleSubmit(formData: any) {
125
console.log('Form submitted:', formData);
126
// Process form submission
127
}
128
129
handleCancel() {
130
console.log('Form cancelled');
131
// Handle cancellation
132
}
133
}
134
135
// Dynamic input/output changes
136
@Component({
137
template: `
138
<button (click)="updateInputs()">Update Inputs</button>
139
<button (click)="toggleOutputs()">Toggle Outputs</button>
140
141
<ndc-dynamic
142
[ndcDynamicComponent]="component"
143
[ndcDynamicInputs]="currentInputs"
144
[ndcDynamicOutputs]="currentOutputs">
145
</ndc-dynamic>
146
`
147
})
148
export class DynamicIoExampleComponent {
149
component = DataDisplayComponent;
150
151
private inputVariations: InputsType[] = [
152
{ title: 'Variation 1', theme: 'light', showHeader: true },
153
{ title: 'Variation 2', theme: 'dark', showHeader: false },
154
{ title: 'Variation 3', theme: 'auto', showHeader: true }
155
];
156
157
private currentVariation = 0;
158
private outputsEnabled = true;
159
160
get currentInputs(): InputsType {
161
return this.inputVariations[this.currentVariation];
162
}
163
164
get currentOutputs(): OutputsType | null {
165
return this.outputsEnabled ? {
166
onItemClick: (item: any) => console.log('Item clicked:', item),
167
onSelectionChange: (selection: any[]) => console.log('Selection:', selection)
168
} : null;
169
}
170
171
updateInputs() {
172
this.currentVariation = (this.currentVariation + 1) % this.inputVariations.length;
173
}
174
175
toggleOutputs() {
176
this.outputsEnabled = !this.outputsEnabled;
177
}
178
}
179
```
180
181
## Types
182
183
### Core Types
184
185
```typescript { .api }
186
/**
187
* Type for dynamic input bindings
188
* Maps property names to their values
189
*/
190
interface InputsType {
191
[k: string]: unknown;
192
}
193
194
/**
195
* Type for dynamic output bindings
196
* Maps event names to handler expressions
197
*/
198
interface OutputsType {
199
[k: string]: OutputExpression | undefined;
200
}
201
202
/**
203
* Union type for output expressions
204
* Can be a simple handler function or complex expression with arguments
205
*/
206
type OutputExpression = EventHandler | OutputWithArgs;
207
208
/**
209
* Type for event handler functions
210
* @template T - The event data type
211
*/
212
type EventHandler<T = unknown> = (event: T) => unknown;
213
214
/**
215
* Generic function type for handlers with arguments
216
*/
217
type AnyFunction = (...args: unknown[]) => unknown;
218
```
219
220
### Advanced Output Types
221
222
```typescript { .api }
223
/**
224
* Output expression with additional arguments
225
* Enables passing extra data to event handlers
226
*/
227
interface OutputWithArgs {
228
/** The event handler function */
229
handler: AnyFunction;
230
/** Optional arguments to pass to the handler */
231
args?: unknown[];
232
}
233
```
234
235
**Usage Examples:**
236
237
```typescript
238
// Using OutputWithArgs for complex event handling
239
const complexOutputs: OutputsType = {
240
// Simple handler
241
onSimpleEvent: (data: any) => console.log(data),
242
243
// Handler with additional arguments
244
onComplexEvent: {
245
handler: (event: any, userId: number, context: string) => {
246
console.log('Event:', event, 'User:', userId, 'Context:', context);
247
},
248
args: [123, 'dashboard']
249
}
250
};
251
```
252
253
## Event Context and Configuration
254
255
### Event Argument Configuration
256
257
```typescript { .api }
258
/**
259
* Default factory for event argument token
260
* @returns The default event argument name
261
*/
262
function defaultEventArgumentFactory(): string;
263
264
/**
265
* Token for configuring event argument name in output expressions
266
* Default value is '$event'
267
*/
268
const IoEventArgumentToken: InjectionToken<string>;
269
270
/**
271
* @deprecated Use IoEventArgumentToken instead
272
* Deprecated since v10.4.0
273
*/
274
const EventArgumentToken: InjectionToken<string>;
275
```
276
277
### Event Context Providers
278
279
```typescript { .api }
280
/**
281
* Token for providing custom context for output handlers
282
* Allows injecting additional data available to all event handlers
283
*/
284
const IoEventContextToken: InjectionToken<unknown>;
285
286
/**
287
* Token for providing StaticProvider that configures IoEventContextToken
288
* Enables dynamic context configuration per component
289
*/
290
const IoEventContextProviderToken: InjectionToken<StaticProvider>;
291
```
292
293
**Usage Examples:**
294
295
```typescript
296
// Custom event argument configuration
297
@Component({
298
providers: [
299
{ provide: IoEventArgumentToken, useValue: '$data' }
300
],
301
template: `
302
<ndc-dynamic
303
[ndcDynamicComponent]="component"
304
[ndcDynamicOutputs]="outputs">
305
</ndc-dynamic>
306
`
307
})
308
export class CustomEventArgComponent {
309
outputs = {
310
// Now '$data' is used instead of '$event'
311
onClick: '$data.preventDefault(); handleClick($data)'
312
};
313
}
314
315
// Custom event context
316
@Component({
317
providers: [
318
{
319
provide: IoEventContextToken,
320
useValue: { userId: 123, permissions: ['read', 'write'] }
321
}
322
],
323
template: `<!-- Dynamic component with custom context -->`
324
})
325
export class ContextExampleComponent { }
326
```
327
328
## Component I/O Abstract Service
329
330
### ComponentIO Service
331
332
```typescript { .api }
333
/**
334
* Abstract service for setting inputs and getting outputs on dynamic components
335
* Extensible interface for custom I/O management implementations
336
*/
337
@Injectable({
338
providedIn: 'root',
339
useClass: ClassicComponentIO
340
})
341
export abstract class ComponentIO {
342
/**
343
* Sets an input property on a component
344
* @param componentRef - Reference to the component
345
* @param name - Name of the input property
346
* @param value - Value to set
347
*/
348
abstract setInput<T, K extends ComponentInputKey<T>>(
349
componentRef: ComponentRef<T>,
350
name: K,
351
value: T[K]
352
): void;
353
354
/**
355
* Gets an output observable from a component
356
* @param componentRef - Reference to the component
357
* @param name - Name of the output property
358
* @returns Observable of the output events
359
*/
360
abstract getOutput<T, K extends ComponentInputKey<T>>(
361
componentRef: ComponentRef<T>,
362
name: K
363
): Observable<unknown>;
364
}
365
366
/**
367
* Type constraint for component input property names
368
* Ensures type safety when setting component inputs
369
*/
370
type ComponentInputKey<T> = keyof T & string;
371
```