|
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | 8 |
|
9 | | -import {Attribute, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, Output, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core'; |
10 | | -import {RouterOutletMap} from '../router_outlet_map'; |
| 9 | +import {Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, OnInit, Output, ViewContainerRef} from '@angular/core'; |
| 10 | + |
| 11 | +import {ChildrenOutletContexts} from '../router_outlet_context'; |
11 | 12 | import {ActivatedRoute} from '../router_state'; |
12 | 13 | import {PRIMARY_OUTLET} from '../shared'; |
13 | 14 |
|
@@ -36,23 +37,40 @@ import {PRIMARY_OUTLET} from '../shared'; |
36 | 37 | * @stable |
37 | 38 | */ |
38 | 39 | @Directive({selector: 'router-outlet'}) |
39 | | -export class RouterOutlet implements OnDestroy { |
| 40 | +export class RouterOutlet implements OnDestroy, OnInit { |
40 | 41 | private activated: ComponentRef<any>|null = null; |
41 | 42 | private _activatedRoute: ActivatedRoute|null = null; |
42 | | - private _outletName: string; |
43 | | - public outletMap: RouterOutletMap; |
| 43 | + private name: string; |
44 | 44 |
|
45 | 45 | @Output('activate') activateEvents = new EventEmitter<any>(); |
46 | 46 | @Output('deactivate') deactivateEvents = new EventEmitter<any>(); |
47 | 47 |
|
48 | 48 | constructor( |
49 | | - private parentOutletMap: RouterOutletMap, private location: ViewContainerRef, |
50 | | - private resolver: ComponentFactoryResolver, @Attribute('name') name: string) { |
51 | | - this._outletName = name || PRIMARY_OUTLET; |
52 | | - parentOutletMap.registerOutlet(this._outletName, this); |
| 49 | + private parentContexts: ChildrenOutletContexts, private location: ViewContainerRef, |
| 50 | + private resolver: ComponentFactoryResolver, @Attribute('name') name: string, |
| 51 | + private changeDetector: ChangeDetectorRef) { |
| 52 | + this.name = name || PRIMARY_OUTLET; |
| 53 | + parentContexts.onChildOutletCreated(this.name, this); |
53 | 54 | } |
54 | 55 |
|
55 | | - ngOnDestroy(): void { this.parentOutletMap.removeOutlet(this._outletName); } |
| 56 | + ngOnDestroy(): void { this.parentContexts.onChildOutletDestroyed(this.name); } |
| 57 | + |
| 58 | + ngOnInit(): void { |
| 59 | + if (!this.activated) { |
| 60 | + // If the outlet was not instantiated at the time the route got activated we need to populate |
| 61 | + // the outlet when it is initialized (ie inside a NgIf) |
| 62 | + const context = this.parentContexts.getContext(this.name); |
| 63 | + if (context && context.route) { |
| 64 | + if (context.attachRef) { |
| 65 | + // `attachRef` is populated when there is an existing component to mount |
| 66 | + this.attach(context.attachRef, context.route); |
| 67 | + } else { |
| 68 | + // otherwise the component defined in the configuration is created |
| 69 | + this.activateWith(context.route, context.resolver || null); |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + } |
56 | 74 |
|
57 | 75 | /** @deprecated since v4 **/ |
58 | 76 | get locationInjector(): Injector { return this.location.injector; } |
@@ -102,65 +120,37 @@ export class RouterOutlet implements OnDestroy { |
102 | 120 | } |
103 | 121 | } |
104 | 122 |
|
105 | | - /** @deprecated since v4, use {@link #activateWith} */ |
106 | | - activate( |
107 | | - activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver, injector: Injector, |
108 | | - providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap): void { |
109 | | - if (this.isActivated) { |
110 | | - throw new Error('Cannot activate an already activated outlet'); |
111 | | - } |
112 | | - |
113 | | - this.outletMap = outletMap; |
114 | | - this._activatedRoute = activatedRoute; |
115 | | - |
116 | | - const snapshot = activatedRoute._futureSnapshot; |
117 | | - const component: any = <any>snapshot._routeConfig !.component; |
118 | | - const factory = resolver.resolveComponentFactory(component) !; |
119 | | - |
120 | | - const inj = ReflectiveInjector.fromResolvedProviders(providers, injector); |
121 | | - |
122 | | - this.activated = this.location.createComponent(factory, this.location.length, inj, []); |
123 | | - this.activated.changeDetectorRef.detectChanges(); |
124 | | - |
125 | | - this.activateEvents.emit(this.activated.instance); |
126 | | - } |
127 | | - |
128 | | - activateWith( |
129 | | - activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null, |
130 | | - outletMap: RouterOutletMap) { |
| 123 | + activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) { |
131 | 124 | if (this.isActivated) { |
132 | 125 | throw new Error('Cannot activate an already activated outlet'); |
133 | 126 | } |
134 | | - |
135 | | - this.outletMap = outletMap; |
136 | 127 | this._activatedRoute = activatedRoute; |
137 | | - |
138 | 128 | const snapshot = activatedRoute._futureSnapshot; |
139 | 129 | const component = <any>snapshot._routeConfig !.component; |
140 | | - |
141 | 130 | resolver = resolver || this.resolver; |
142 | 131 | const factory = resolver.resolveComponentFactory(component); |
143 | | - |
144 | | - const injector = new OutletInjector(activatedRoute, outletMap, this.location.injector); |
145 | | - |
| 132 | + const childContexts = this.parentContexts.getOrCreateContext(this.name).children; |
| 133 | + const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector); |
146 | 134 | this.activated = this.location.createComponent(factory, this.location.length, injector); |
147 | | - this.activated.changeDetectorRef.detectChanges(); |
148 | | - |
| 135 | + // Calling `markForCheck` to make sure we will run the change detection when the |
| 136 | + // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. |
| 137 | + this.changeDetector.markForCheck(); |
149 | 138 | this.activateEvents.emit(this.activated.instance); |
150 | 139 | } |
151 | 140 | } |
152 | 141 |
|
153 | 142 | class OutletInjector implements Injector { |
154 | 143 | constructor( |
155 | | - private route: ActivatedRoute, private map: RouterOutletMap, private parent: Injector) {} |
| 144 | + private route: ActivatedRoute, private childContexts: ChildrenOutletContexts, |
| 145 | + private parent: Injector) {} |
156 | 146 |
|
157 | 147 | get(token: any, notFoundValue?: any): any { |
158 | 148 | if (token === ActivatedRoute) { |
159 | 149 | return this.route; |
160 | 150 | } |
161 | 151 |
|
162 | | - if (token === RouterOutletMap) { |
163 | | - return this.map; |
| 152 | + if (token === ChildrenOutletContexts) { |
| 153 | + return this.childContexts; |
164 | 154 | } |
165 | 155 |
|
166 | 156 | return this.parent.get(token, notFoundValue); |
|
0 commit comments