0
# NgComponentOutlet Enhancement
1
2
Directives that extend Angular's built-in NgComponentOutlet with dynamic input/output binding, component reference access, and additional lifecycle management capabilities.
3
4
## Capabilities
5
6
### Component Outlet Injector Directive
7
8
Provides access to the ComponentRef created by NgComponentOutlet for further manipulation and lifecycle management.
9
10
```typescript { .api }
11
/**
12
* Provides ComponentRef access for NgComponentOutlet
13
* Enables accessing the component instance created by the outlet
14
*/
15
@Directive({
16
selector: '[ngComponentOutlet]',
17
standalone: true,
18
exportAs: 'ndcComponentOutletInjector'
19
})
20
export class ComponentOutletInjectorDirective {
21
/**
22
* Reference to the component created by NgComponentOutlet
23
* Provides access to the component instance and lifecycle methods
24
*/
25
readonly componentRef: ComponentRef<unknown>;
26
}
27
```
28
29
### Component Outlet I/O Directive
30
31
Enables dynamic input/output binding for components created by NgComponentOutlet.
32
33
```typescript { .api }
34
/**
35
* Enables dynamic inputs/outputs for NgComponentOutlet
36
* Works in conjunction with Angular's built-in outlet directive
37
*/
38
@Directive({
39
selector: '[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]',
40
standalone: true,
41
exportAs: 'ndcDynamicIo'
42
})
43
export class ComponentOutletIoDirective implements DoCheck {
44
/** Dynamic inputs for the outlet component */
45
@Input() ngComponentOutletNdcDynamicInputs?: InputsType | null;
46
47
/** Dynamic outputs for the outlet component */
48
@Input() ngComponentOutletNdcDynamicOutputs?: OutputsType | null;
49
}
50
```
51
52
### Component Outlet Injector Module
53
54
NgModule that provides both outlet enhancement directives for traditional module-based applications.
55
56
```typescript { .api }
57
/**
58
* Module that exports both outlet enhancement directives
59
* Use this in NgModule-based applications
60
*/
61
@NgModule({
62
imports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
63
exports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective]
64
})
65
export class ComponentOutletInjectorModule {}
66
```
67
68
**Usage Examples:**
69
70
```typescript
71
import { Component, Type, ComponentRef } from '@angular/core';
72
import { ComponentOutletInjectorDirective, ComponentOutletIoDirective } from 'ng-dynamic-component';
73
74
// Basic outlet with component reference access
75
@Component({
76
standalone: true,
77
imports: [ComponentOutletInjectorDirective],
78
template: `
79
<ng-container
80
*ngComponentOutlet="selectedComponent"
81
#outlet="ndcComponentOutletInjector">
82
</ng-container>
83
84
<button (click)="accessComponent(outlet.componentRef)">
85
Access Component
86
</button>
87
`
88
})
89
export class OutletBasicExampleComponent {
90
selectedComponent: Type<any> = MyComponent;
91
92
accessComponent(componentRef: ComponentRef<any>) {
93
console.log('Component instance:', componentRef.instance);
94
95
// Directly modify component properties
96
componentRef.instance.title = 'Modified Title';
97
98
// Trigger change detection
99
componentRef.changeDetectorRef.detectChanges();
100
101
// Access component methods
102
if (typeof componentRef.instance.refresh === 'function') {
103
componentRef.instance.refresh();
104
}
105
}
106
}
107
108
// Outlet with dynamic I/O binding
109
@Component({
110
standalone: true,
111
imports: [ComponentOutletIoDirective],
112
template: `
113
<ng-container
114
*ngComponentOutlet="componentType"
115
[ngComponentOutletNdcDynamicInputs]="inputs"
116
[ngComponentOutletNdcDynamicOutputs]="outputs">
117
</ng-container>
118
`
119
})
120
export class OutletIoExampleComponent {
121
componentType = UserProfileComponent;
122
123
inputs = {
124
userId: 123,
125
editMode: false,
126
theme: 'light'
127
};
128
129
outputs = {
130
onSave: (userData: any) => this.saveUser(userData),
131
onCancel: () => this.cancelEdit(),
132
onProfilePictureChange: (imageUrl: string) => this.updateProfilePicture(imageUrl)
133
};
134
135
saveUser(userData: any) {
136
console.log('Saving user:', userData);
137
// Implement save logic
138
}
139
140
cancelEdit() {
141
console.log('Edit cancelled');
142
this.inputs = { ...this.inputs, editMode: false };
143
}
144
145
updateProfilePicture(imageUrl: string) {
146
console.log('Profile picture updated:', imageUrl);
147
// Handle image update
148
}
149
}
150
151
// Combined usage with both directives
152
@Component({
153
standalone: true,
154
imports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
155
template: `
156
<ng-container
157
*ngComponentOutlet="currentComponent"
158
[ngComponentOutletNdcDynamicInputs]="dynamicInputs"
159
[ngComponentOutletNdcDynamicOutputs]="dynamicOutputs"
160
#outlet="ndcComponentOutletInjector">
161
</ng-container>
162
163
<div class="controls">
164
<button (click)="refreshComponent(outlet.componentRef)">Refresh</button>
165
<button (click)="switchComponent()">Switch Component</button>
166
</div>
167
`
168
})
169
export class OutletCombinedExampleComponent {
170
private components = [DashboardComponent, SettingsComponent, ProfileComponent];
171
private currentIndex = 0;
172
173
get currentComponent() {
174
return this.components[this.currentIndex];
175
}
176
177
get dynamicInputs() {
178
// Return different inputs based on current component
179
switch (this.currentComponent) {
180
case DashboardComponent:
181
return { userId: 123, refreshInterval: 5000 };
182
case SettingsComponent:
183
return { theme: 'dark', notifications: true };
184
case ProfileComponent:
185
return { userId: 123, editMode: false };
186
default:
187
return {};
188
}
189
}
190
191
get dynamicOutputs() {
192
return {
193
onAction: (action: string, data?: any) => this.handleAction(action, data),
194
onNavigate: (route: string) => this.navigate(route)
195
};
196
}
197
198
refreshComponent(componentRef: ComponentRef<any>) {
199
if (typeof componentRef.instance.refresh === 'function') {
200
componentRef.instance.refresh();
201
}
202
}
203
204
switchComponent() {
205
this.currentIndex = (this.currentIndex + 1) % this.components.length;
206
console.log('Switched to:', this.currentComponent.name);
207
}
208
209
handleAction(action: string, data?: any) {
210
console.log('Action received:', action, data);
211
}
212
213
navigate(route: string) {
214
console.log('Navigate to:', route);
215
// Implement navigation logic
216
}
217
}
218
```
219
220
### Working with Complex Component Hierarchies
221
222
Handle nested components and complex injection scenarios:
223
224
```typescript
225
@Component({
226
template: `
227
<div class="parent-container">
228
<ng-container
229
*ngComponentOutlet="
230
parentComponent;
231
injector: customInjector;
232
content: projectedNodes
233
"
234
[ngComponentOutletNdcDynamicInputs]="parentInputs"
235
[ngComponentOutletNdcDynamicOutputs]="parentOutputs"
236
#parentOutlet="ndcComponentOutletInjector">
237
</ng-container>
238
239
<!-- Child component that depends on parent -->
240
<ng-container
241
*ngComponentOutlet="childComponent"
242
[ngComponentOutletNdcDynamicInputs]="getChildInputs(parentOutlet.componentRef)"
243
[ngComponentOutletNdcDynamicOutputs]="childOutputs">
244
</ng-container>
245
</div>
246
`
247
})
248
export class HierarchicalOutletComponent {
249
parentComponent = ParentComponent;
250
childComponent = ChildComponent;
251
252
parentInputs = {
253
title: 'Parent Component',
254
configuration: { mode: 'advanced' }
255
};
256
257
parentOutputs = {
258
onConfigChange: (config: any) => this.updateConfiguration(config)
259
};
260
261
childOutputs = {
262
onChildEvent: (event: any) => this.handleChildEvent(event)
263
};
264
265
constructor(private injector: Injector) {}
266
267
get customInjector() {
268
return Injector.create({
269
providers: [
270
{ provide: 'PARENT_CONFIG', useValue: { theme: 'blue' } }
271
],
272
parent: this.injector
273
});
274
}
275
276
get projectedNodes() {
277
// Create content to project into parent component
278
return [
279
// Projected content nodes
280
];
281
}
282
283
getChildInputs(parentComponentRef: ComponentRef<any>) {
284
// Child inputs depend on parent component state
285
return {
286
parentData: parentComponentRef?.instance?.data || null,
287
inherited: true
288
};
289
}
290
291
updateConfiguration(config: any) {
292
console.log('Parent configuration updated:', config);
293
}
294
295
handleChildEvent(event: any) {
296
console.log('Child event received:', event);
297
}
298
}
299
```
300
301
### Integration with Angular Router
302
303
Use outlet components with router data and parameters:
304
305
```typescript
306
import { ActivatedRoute } from '@angular/router';
307
308
@Component({
309
template: `
310
<ng-container
311
*ngComponentOutlet="getComponentForRoute()"
312
[ngComponentOutletNdcDynamicInputs]="routeInputs"
313
[ngComponentOutletNdcDynamicOutputs]="routeOutputs">
314
</ng-container>
315
`
316
})
317
export class RoutedOutletComponent implements OnInit {
318
private routeData: any = {};
319
private routeParams: any = {};
320
321
constructor(private route: ActivatedRoute) {}
322
323
ngOnInit() {
324
// Subscribe to route data and parameters
325
this.route.data.subscribe(data => {
326
this.routeData = data;
327
});
328
329
this.route.params.subscribe(params => {
330
this.routeParams = params;
331
});
332
}
333
334
getComponentForRoute(): Type<any> {
335
// Select component based on route data
336
return this.routeData.component || DefaultComponent;
337
}
338
339
get routeInputs() {
340
return {
341
...this.routeParams,
342
routeData: this.routeData,
343
timestamp: Date.now()
344
};
345
}
346
347
get routeOutputs() {
348
return {
349
onRouteAction: (action: string) => this.handleRouteAction(action)
350
};
351
}
352
353
handleRouteAction(action: string) {
354
console.log('Route action:', action);
355
// Handle route-specific actions
356
}
357
}
358
```
359
360
## Module Usage
361
362
For NgModule-based applications:
363
364
```typescript
365
import { NgModule } from '@angular/core';
366
import { ComponentOutletInjectorModule } from 'ng-dynamic-component';
367
368
@NgModule({
369
imports: [
370
ComponentOutletInjectorModule
371
],
372
// Component can now use outlet directives
373
})
374
export class MyFeatureModule {}
375
```