feat(router): Add EnvironmentInjector to RouterOutlet.activateWith (#45597)

The current API for `activateWith` uses the deprecated
`ComponentFactoryResolver`. The replacement for this is to use
`EnvironmentInjector`. That is, instead of

```
const factory = componentFactoryResolver.resolveComponentFactory(component);
location.createComponent(factory, location.length, injector);
```

the replacement would be

```
location.createComponent(
    component, {index: location.length, injector, environmentInjector});
```

PR Close #45597
This commit is contained in:
Andrew Scott
2022-04-18 14:56:26 -07:00
committed by Dylan Hunn
parent 4e0957a4e1
commit f4fd1a8262
4 changed files with 35 additions and 13 deletions
+6 -3
View File
@@ -357,6 +357,8 @@ export class OutletContext {
// (undocumented)
children: ChildrenOutletContexts;
// (undocumented)
injector: EnvironmentInjector | null;
// (undocumented)
outlet: RouterOutletContract | null;
// (undocumented)
resolver: ComponentFactoryResolver | null;
@@ -645,7 +647,7 @@ export class RouterModule {
// @public
export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
constructor(parentContexts: ChildrenOutletContexts, location: ViewContainerRef, resolver: ComponentFactoryResolver, name: string, changeDetector: ChangeDetectorRef);
constructor(parentContexts: ChildrenOutletContexts, location: ViewContainerRef, name: string, changeDetector: ChangeDetectorRef, environmentInjector: EnvironmentInjector);
// (undocumented)
get activatedRoute(): ActivatedRoute;
// (undocumented)
@@ -653,7 +655,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
// (undocumented)
activateEvents: EventEmitter<any>;
// (undocumented)
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void;
activateWith(activatedRoute: ActivatedRoute, resolverOrInjector?: ComponentFactoryResolver | EnvironmentInjector | null): void;
attach(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): void;
attachEvents: EventEmitter<unknown>;
// (undocumented)
@@ -673,7 +675,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<RouterOutlet, "router-outlet", ["outlet"], {}, { "activateEvents": "activate"; "deactivateEvents": "deactivate"; "attachEvents": "attach"; "detachEvents": "detach"; }, never, never, false>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<RouterOutlet, [null, null, null, { attribute: "name"; }, null]>;
static ɵfac: i0.ɵɵFactoryDeclaration<RouterOutlet, [null, null, { attribute: "name"; }, null, null]>;
}
// @public
@@ -681,6 +683,7 @@ export interface RouterOutletContract {
activatedRoute: ActivatedRoute | null;
activatedRouteData: Data;
activateEvents?: EventEmitter<unknown>;
activateWith(activatedRoute: ActivatedRoute, environmnetInjector: EnvironmentInjector | null): void;
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void;
attach(ref: ComponentRef<unknown>, activatedRoute: ActivatedRoute): void;
attachEvents?: EventEmitter<unknown>;
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, OnInit, Output, ViewContainerRef} from '@angular/core';
import {Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, EnvironmentInjector, EventEmitter, Injector, OnDestroy, OnInit, Output, ViewContainerRef,} from '@angular/core';
import {Data} from '../models';
import {ChildrenOutletContexts} from '../router_outlet_context';
@@ -47,6 +47,10 @@ export interface RouterOutletContract {
*/
activatedRoute: ActivatedRoute|null;
/**
* Called by the `Router` when the outlet should activate (create a component).
*/
activateWith(activatedRoute: ActivatedRoute, environmnetInjector: EnvironmentInjector|null): void;
/**
* Called by the `Router` when the outlet should activate (create a component).
*/
@@ -168,8 +172,8 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
constructor(
private parentContexts: ChildrenOutletContexts, private location: ViewContainerRef,
private resolver: ComponentFactoryResolver, @Attribute('name') name: string,
private changeDetector: ChangeDetectorRef) {
@Attribute('name') name: string, private changeDetector: ChangeDetectorRef,
private environmentInjector: EnvironmentInjector) {
this.name = name || PRIMARY_OUTLET;
parentContexts.onChildOutletCreated(this.name, this);
}
@@ -191,7 +195,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
this.attach(context.attachRef, context.route);
} else {
// otherwise the component defined in the configuration is created
this.activateWith(context.route, context.resolver || null);
this.activateWith(context.route, context.injector);
}
}
}
@@ -255,18 +259,27 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {
}
}
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) {
activateWith(
activatedRoute: ActivatedRoute,
resolverOrInjector?: ComponentFactoryResolver|EnvironmentInjector|null) {
if (this.isActivated) {
throw new Error('Cannot activate an already activated outlet');
}
this._activatedRoute = activatedRoute;
const location = this.location;
const snapshot = activatedRoute._futureSnapshot;
const component = <any>snapshot.routeConfig!.component;
resolver = resolver || this.resolver;
const factory = resolver.resolveComponentFactory(component);
const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
this.activated = this.location.createComponent(factory, this.location.length, injector);
const injector = new OutletInjector(activatedRoute, childContexts, location.injector);
if (resolverOrInjector && isComponentFactoryResolver(resolverOrInjector)) {
const factory = resolverOrInjector.resolveComponentFactory(component);
this.activated = location.createComponent(factory, location.length, injector);
} else {
const environmentInjector = resolverOrInjector ?? this.environmentInjector;
this.activated = location.createComponent(
component, {index: location.length, injector, environmentInjector});
}
// Calling `markForCheck` to make sure we will run the change detection when the
// `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
this.changeDetector.markForCheck();
@@ -291,3 +304,7 @@ class OutletInjector implements Injector {
return this.parent.get(token, notFoundValue);
}
}
function isComponentFactoryResolver(item: any): item is ComponentFactoryResolver {
return !!item.resolveComponentFactory;
}
@@ -198,10 +198,11 @@ export class ActivateRoutes {
context.attachRef = null;
context.route = future;
context.resolver = cmpFactoryResolver;
context.injector = injector;
if (context.outlet) {
// Activate the outlet when it has already been instantiated
// Otherwise it will get activated from its `ngOnInit` when instantiated
context.outlet.activateWith(future, cmpFactoryResolver);
context.outlet.activateWith(future, context.injector);
}
this.activateChildRoutes(futureNode, null, context.children);
@@ -21,6 +21,7 @@ export class OutletContext {
outlet: RouterOutletContract|null = null;
route: ActivatedRoute|null = null;
resolver: ComponentFactoryResolver|null = null;
injector: EnvironmentInjector|null = null;
children = new ChildrenOutletContexts();
attachRef: ComponentRef<any>|null = null;
}