forked from angular/angular
/
ng_component_outlet.ts
136 lines (122 loc) · 4.47 KB
/
ng_component_outlet.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ComponentRef, createNgModuleRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
/**
* Instantiates a {@link Component} type and inserts its Host View into the current View.
* `NgComponentOutlet` provides a declarative approach for dynamic component creation.
*
* `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
* any existing component will be destroyed.
*
* @usageNotes
*
* ### Fine tune control
*
* You can control the component creation process by using the following optional attributes:
*
* * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for
* the Component. Defaults to the injector of the current view container.
*
* * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content
* section of the component, if it exists.
*
* * `ngComponentOutletNgModule`: Optional NgModule class reference to allow loading another
* module dynamically, then loading a component from that module.
*
* * `ngComponentOutletNgModuleFactory`: Deprecated config option that allows providing optional
* NgModule factory to allow loading another module dynamically, then loading a component from that
* module. Use `ngComponentOutletNgModule` instead.
*
* ### Syntax
*
* Simple
* ```
* <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container>
* ```
*
* Customized injector/content
* ```
* <ng-container *ngComponentOutlet="componentTypeExpression;
* injector: injectorExpression;
* content: contentNodesExpression;">
* </ng-container>
* ```
*
* Customized NgModule reference
* ```
* <ng-container *ngComponentOutlet="componentTypeExpression;
* ngModule: ngModuleClass;">
* </ng-container>
* ```
*
* ### A simple example
*
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
*
* A more complete example with additional options:
*
* {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'}
*
* @publicApi
* @ngModule CommonModule
*/
@Directive({
selector: '[ngComponentOutlet]',
standalone: true,
})
export class NgComponentOutlet implements OnChanges, OnDestroy {
@Input() ngComponentOutlet: Type<any>|null = null;
@Input() ngComponentOutletInjector?: Injector;
@Input() ngComponentOutletContent?: any[][];
@Input() ngComponentOutletNgModule?: Type<any>;
/**
* @deprecated This input is deprecated, use `ngComponentOutletNgModule` instead.
*/
@Input() ngComponentOutletNgModuleFactory?: NgModuleFactory<any>;
private _componentRef: ComponentRef<any>|undefined;
private _moduleRef: NgModuleRef<any>|undefined;
constructor(private _viewContainerRef: ViewContainerRef) {}
/** @nodoc */
ngOnChanges(changes: SimpleChanges) {
const {
_viewContainerRef: viewContainerRef,
ngComponentOutletNgModule: ngModule,
ngComponentOutletNgModuleFactory: ngModuleFactory,
} = this;
viewContainerRef.clear();
this._componentRef = undefined;
if (this.ngComponentOutlet) {
const injector = this.ngComponentOutletInjector || viewContainerRef.parentInjector;
if (changes['ngComponentOutletNgModule'] || changes['ngComponentOutletNgModuleFactory']) {
if (this._moduleRef) this._moduleRef.destroy();
if (ngModule) {
this._moduleRef = createNgModuleRef(ngModule, getParentInjector(injector));
} else if (ngModuleFactory) {
this._moduleRef = ngModuleFactory.create(getParentInjector(injector));
} else {
this._moduleRef = undefined;
}
}
this._componentRef = viewContainerRef.createComponent(this.ngComponentOutlet, {
index: viewContainerRef.length,
injector,
ngModuleRef: this._moduleRef,
projectableNodes: this.ngComponentOutletContent,
});
}
}
/** @nodoc */
ngOnDestroy() {
if (this._moduleRef) this._moduleRef.destroy();
}
}
// Helper function that returns an Injector instance of a parent NgModule.
function getParentInjector(injector: Injector): Injector {
const parentNgModule = injector.get(NgModuleRef);
return parentNgModule.injector;
}