Skip to content

Commit 56a3230

Browse files
committed
fix(cdk/portal): remove ComponentFactoryResolver usages
We now have all the necessary APIs to allow to remove our usages of the deprecated `ComponentFactoryResolver`. Fixes #24334.
1 parent 7c8a796 commit 56a3230

File tree

7 files changed

+53
-118
lines changed

7 files changed

+53
-118
lines changed

src/cdk/portal/dom-portal-outlet.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
import {
1010
ApplicationRef,
11-
ComponentFactoryResolver,
1211
ComponentRef,
1312
EmbeddedViewRef,
1413
Injector,
14+
createComponent,
1515
} from '@angular/core';
1616
import {BasePortalOutlet, ComponentPortal, DomPortal, TemplatePortal} from './portal';
1717

@@ -36,7 +36,11 @@ export class DomPortalOutlet extends BasePortalOutlet {
3636
constructor(
3737
/** Element into which the content is projected. */
3838
public outletElement: Element,
39-
private _componentFactoryResolver?: ComponentFactoryResolver,
39+
/**
40+
* @deprecated No longer in use. To be removed.
41+
* @breaking-change 18.0.0
42+
*/
43+
_componentFactoryResolver?: any,
4044
private _appRef?: ApplicationRef,
4145
private _defaultInjector?: Injector,
4246

@@ -51,41 +55,37 @@ export class DomPortalOutlet extends BasePortalOutlet {
5155
}
5256

5357
/**
54-
* Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver.
58+
* Attach the given ComponentPortal to DOM element.
5559
* @param portal Portal to be attached
5660
* @returns Reference to the created component.
5761
*/
5862
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
59-
const resolver = (portal.componentFactoryResolver || this._componentFactoryResolver)!;
60-
61-
if ((typeof ngDevMode === 'undefined' || ngDevMode) && !resolver) {
62-
throw Error('Cannot attach component portal to outlet without a ComponentFactoryResolver.');
63-
}
64-
65-
const componentFactory = resolver.resolveComponentFactory(portal.component);
6663
let componentRef: ComponentRef<T>;
6764

6865
// If the portal specifies a ViewContainerRef, we will use that as the attachment point
6966
// for the component (in terms of Angular's component tree, not rendering).
7067
// When the ViewContainerRef is missing, we use the factory to create the component directly
7168
// and then manually attach the view to the application.
7269
if (portal.viewContainerRef) {
73-
componentRef = portal.viewContainerRef.createComponent(
74-
componentFactory,
75-
portal.viewContainerRef.length,
76-
portal.injector || portal.viewContainerRef.injector,
77-
portal.projectableNodes || undefined,
78-
);
70+
componentRef = portal.viewContainerRef.createComponent(portal.component, {
71+
index: portal.viewContainerRef.length,
72+
injector: portal.injector || portal.viewContainerRef.injector,
73+
environmentInjector: this._appRef?.injector,
74+
projectableNodes: portal.projectableNodes || undefined,
75+
});
7976

8077
this.setDisposeFn(() => componentRef.destroy());
8178
} else {
8279
if ((typeof ngDevMode === 'undefined' || ngDevMode) && !this._appRef) {
8380
throw Error('Cannot attach component portal to outlet without an ApplicationRef.');
8481
}
8582

86-
componentRef = componentFactory.create(
87-
portal.injector || this._defaultInjector || Injector.NULL,
88-
);
83+
componentRef = createComponent(portal.component, {
84+
elementInjector: portal.injector || this._defaultInjector || Injector.NULL,
85+
environmentInjector: this._appRef!.injector,
86+
projectableNodes: portal.projectableNodes || undefined,
87+
});
88+
8989
this._appRef!.attachView(componentRef.hostView);
9090
this.setDisposeFn(() => {
9191
// Verify that the ApplicationRef has registered views before trying to detach a host view.

src/cdk/portal/portal-directives.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {
10-
ComponentFactoryResolver,
1110
ComponentRef,
1211
Directive,
1312
EmbeddedViewRef,
@@ -19,6 +18,8 @@ import {
1918
TemplateRef,
2019
ViewContainerRef,
2120
Inject,
21+
inject,
22+
ApplicationRef,
2223
} from '@angular/core';
2324
import {DOCUMENT} from '@angular/common';
2425
import {BasePortalOutlet, ComponentPortal, Portal, TemplatePortal, DomPortal} from './portal';
@@ -71,6 +72,7 @@ export type CdkPortalOutletAttachedRef = ComponentRef<any> | EmbeddedViewRef<any
7172
inputs: ['portal: cdkPortalOutlet'],
7273
})
7374
export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestroy {
75+
private _applicationRef = inject(ApplicationRef);
7476
private _document: Document;
7577

7678
/** Whether the portal component is initialized. */
@@ -80,7 +82,11 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
8082
private _attachedRef: CdkPortalOutletAttachedRef;
8183

8284
constructor(
83-
private _componentFactoryResolver: ComponentFactoryResolver,
85+
/**
86+
* @deprecated `_componentFactoryResolver` parameter no longer in use. To be removed.
87+
* @breaking-change 18.0.0
88+
*/
89+
@Inject(DOCUMENT) _componentFactoryResolver: any,
8490
private _viewContainerRef: ViewContainerRef,
8591

8692
/**
@@ -137,7 +143,7 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
137143
}
138144

139145
/**
140-
* Attach the given ComponentPortal to this PortalOutlet using the ComponentFactoryResolver.
146+
* Attach the given ComponentPortal to this PortalOutlet.
141147
*
142148
* @param portal Portal to be attached to the portal outlet.
143149
* @returns Reference to the created component.
@@ -150,14 +156,12 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
150156
const viewContainerRef =
151157
portal.viewContainerRef != null ? portal.viewContainerRef : this._viewContainerRef;
152158

153-
const resolver = portal.componentFactoryResolver || this._componentFactoryResolver;
154-
const componentFactory = resolver.resolveComponentFactory(portal.component);
155-
const ref = viewContainerRef.createComponent(
156-
componentFactory,
157-
viewContainerRef.length,
158-
portal.injector || viewContainerRef.injector,
159-
portal.projectableNodes || undefined,
160-
);
159+
const ref = viewContainerRef.createComponent(portal.component, {
160+
index: viewContainerRef.length,
161+
injector: portal.injector || viewContainerRef.injector,
162+
projectableNodes: portal.projectableNodes || undefined,
163+
environmentInjector: this._applicationRef.injector,
164+
});
161165

162166
// If we're using a view container that's different from the injected one (e.g. when the portal
163167
// specifies its own) we need to move the component into the outlet, otherwise it'll be rendered

src/cdk/portal/portal.spec.ts

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ import {CommonModule} from '@angular/common';
22
import {
33
ApplicationRef,
44
Component,
5-
ComponentFactoryResolver,
65
ComponentRef,
76
ElementRef,
87
Injector,
98
Optional,
109
QueryList,
1110
TemplateRef,
12-
Type,
1311
ViewChild,
1412
ViewChildren,
1513
ViewContainerRef,
@@ -37,15 +35,10 @@ describe('Portals', () => {
3735

3836
describe('CdkPortalOutlet', () => {
3937
let fixture: ComponentFixture<PortalTestApp>;
40-
let componentFactoryResolver: ComponentFactoryResolver;
4138

4239
beforeEach(() => {
4340
fixture = TestBed.createComponent(PortalTestApp);
4441
fixture.detectChanges();
45-
46-
inject([ComponentFactoryResolver], (cfr: ComponentFactoryResolver) => {
47-
componentFactoryResolver = cfr;
48-
})();
4942
});
5043

5144
it('should load a component into the portal', () => {
@@ -428,19 +421,6 @@ describe('Portals', () => {
428421
expect(instance.portalOutlet.hasAttached()).toBe(true);
429422
});
430423

431-
it('should use the `ComponentFactoryResolver` from the portal, if available', () => {
432-
const spy = jasmine.createSpy('resolveComponentFactorySpy');
433-
const portal = new ComponentPortal(PizzaMsg, undefined, undefined, {
434-
resolveComponentFactory: <T>(...args: [Type<T>]) => {
435-
spy();
436-
return componentFactoryResolver.resolveComponentFactory(...args);
437-
},
438-
});
439-
440-
fixture.componentInstance.portalOutlet.attachComponentPortal(portal);
441-
expect(spy).toHaveBeenCalled();
442-
});
443-
444424
it('should render inside outlet when component portal specifies view container ref', () => {
445425
const hostContainer = fixture.nativeElement.querySelector('.portal-container');
446426
const portal = new ComponentPortal(PizzaMsg, fixture.componentInstance.alternateContainer);
@@ -466,32 +446,23 @@ describe('Portals', () => {
466446
});
467447

468448
describe('DomPortalOutlet', () => {
469-
let componentFactoryResolver: ComponentFactoryResolver;
470449
let someViewContainerRef: ViewContainerRef;
471450
let someInjector: Injector;
472451
let someFixture: ComponentFixture<ArbitraryViewContainerRefComponent>;
473452
let someDomElement: HTMLElement;
474453
let host: DomPortalOutlet;
475454
let injector: Injector;
476455
let appRef: ApplicationRef;
477-
let deps = [ComponentFactoryResolver, Injector, ApplicationRef];
456+
let deps = [Injector, ApplicationRef];
478457

479-
beforeEach(inject(deps, (cfr: ComponentFactoryResolver, i: Injector, ar: ApplicationRef) => {
480-
componentFactoryResolver = cfr;
458+
beforeEach(inject(deps, (i: Injector, ar: ApplicationRef) => {
481459
injector = i;
482460
appRef = ar;
483461
}));
484462

485463
beforeEach(() => {
486464
someDomElement = document.createElement('div');
487-
host = new DomPortalOutlet(
488-
someDomElement,
489-
componentFactoryResolver,
490-
appRef,
491-
injector,
492-
document,
493-
);
494-
465+
host = new DomPortalOutlet(someDomElement, null, appRef, injector, document);
495466
someFixture = TestBed.createComponent(ArbitraryViewContainerRefComponent);
496467
someViewContainerRef = someFixture.componentInstance.viewContainerRef;
497468
someInjector = someFixture.componentInstance.injector;
@@ -647,19 +618,6 @@ describe('Portals', () => {
647618
expect(spy).toHaveBeenCalled();
648619
});
649620

650-
it('should use the `ComponentFactoryResolver` from the portal, if available', () => {
651-
const spy = jasmine.createSpy('resolveComponentFactorySpy');
652-
const portal = new ComponentPortal(PizzaMsg, undefined, undefined, {
653-
resolveComponentFactory: <T>(...args: [Type<T>]) => {
654-
spy();
655-
return componentFactoryResolver.resolveComponentFactory(...args);
656-
},
657-
});
658-
659-
host.attachComponentPortal(portal);
660-
expect(spy).toHaveBeenCalled();
661-
});
662-
663621
it('should attach and detach a DOM portal', () => {
664622
const fixture = TestBed.createComponent(PortalTestApp);
665623
fixture.detectChanges();

src/cdk/portal/portal.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
ComponentRef,
1414
EmbeddedViewRef,
1515
Injector,
16-
ComponentFactoryResolver,
1716
} from '@angular/core';
1817
import {
1918
throwNullPortalOutletError,
@@ -96,10 +95,10 @@ export class ComponentPortal<T> extends Portal<ComponentRef<T>> {
9695
injector?: Injector | null;
9796

9897
/**
99-
* Alternate `ComponentFactoryResolver` to use when resolving the associated component.
100-
* Defaults to using the resolver from the outlet that the portal is attached to.
98+
* @deprecated No longer in use. To be removed.
99+
* @breaking-change 18.0.0
101100
*/
102-
componentFactoryResolver?: ComponentFactoryResolver | null;
101+
componentFactoryResolver?: any;
103102

104103
/**
105104
* List of DOM nodes that should be projected through `<ng-content>` of the attached component.
@@ -110,14 +109,17 @@ export class ComponentPortal<T> extends Portal<ComponentRef<T>> {
110109
component: ComponentType<T>,
111110
viewContainerRef?: ViewContainerRef | null,
112111
injector?: Injector | null,
113-
componentFactoryResolver?: ComponentFactoryResolver | null,
112+
/**
113+
* @deprecated No longer in use. To be removed.
114+
* @breaking-change 18.0.0
115+
*/
116+
_componentFactoryResolver?: any,
114117
projectableNodes?: Node[][] | null,
115118
) {
116119
super();
117120
this.component = component;
118121
this.viewContainerRef = viewContainerRef;
119122
this.injector = injector;
120-
this.componentFactoryResolver = componentFactoryResolver;
121123
this.projectableNodes = projectableNodes;
122124
}
123125
}

src/material/dialog/dialog.spec.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {SpyLocation} from '@angular/common/testing';
1616
import {
1717
ChangeDetectionStrategy,
1818
Component,
19-
ComponentFactoryResolver,
2019
createNgModuleRef,
2120
Directive,
2221
Inject,
@@ -754,21 +753,6 @@ describe('MDC-based MatDialog', () => {
754753
expect(scrollStrategy.enable).toHaveBeenCalled();
755754
}));
756755

757-
it('should be able to pass in an alternate ComponentFactoryResolver', inject(
758-
[ComponentFactoryResolver],
759-
(resolver: ComponentFactoryResolver) => {
760-
spyOn(resolver, 'resolveComponentFactory').and.callThrough();
761-
762-
dialog.open(PizzaMsg, {
763-
viewContainerRef: testViewContainerRef,
764-
componentFactoryResolver: resolver,
765-
});
766-
viewContainerFixture.detectChanges();
767-
768-
expect(resolver.resolveComponentFactory).toHaveBeenCalled();
769-
},
770-
));
771-
772756
describe('passing in data', () => {
773757
it('should be able to pass in data', () => {
774758
let config = {data: {stringParam: 'hello', dateParam: new Date()}};

src/material/legacy-dialog/dialog.spec.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
TemplateRef,
1818
ViewChild,
1919
ViewContainerRef,
20-
ComponentFactoryResolver,
2120
NgZone,
2221
ViewEncapsulation,
2322
Injectable,
@@ -813,21 +812,6 @@ describe('MatDialog', () => {
813812
expect(scrollStrategy.enable).toHaveBeenCalled();
814813
}));
815814

816-
it('should be able to pass in an alternate ComponentFactoryResolver', inject(
817-
[ComponentFactoryResolver],
818-
(resolver: ComponentFactoryResolver) => {
819-
spyOn(resolver, 'resolveComponentFactory').and.callThrough();
820-
821-
dialog.open(PizzaMsg, {
822-
viewContainerRef: testViewContainerRef,
823-
componentFactoryResolver: resolver,
824-
});
825-
viewContainerFixture.detectChanges();
826-
827-
expect(resolver.resolveComponentFactory).toHaveBeenCalled();
828-
},
829-
));
830-
831815
describe('passing in data', () => {
832816
it('should be able to pass in data', () => {
833817
const config = {

tools/public_api_guard/cdk/portal.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
```ts
66

77
import { ApplicationRef } from '@angular/core';
8-
import { ComponentFactoryResolver } from '@angular/core';
98
import { ComponentRef } from '@angular/core';
109
import { ElementRef } from '@angular/core';
1110
import { EmbeddedViewRef } from '@angular/core';
@@ -53,7 +52,8 @@ export class CdkPortal extends TemplatePortal {
5352

5453
// @public
5554
export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestroy {
56-
constructor(_componentFactoryResolver: ComponentFactoryResolver, _viewContainerRef: ViewContainerRef,
55+
constructor(
56+
_componentFactoryResolver: any, _viewContainerRef: ViewContainerRef,
5757
_document?: any);
5858
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T>;
5959
// @deprecated
@@ -78,9 +78,11 @@ export type CdkPortalOutletAttachedRef = ComponentRef<any> | EmbeddedViewRef<any
7878

7979
// @public
8080
export class ComponentPortal<T> extends Portal<ComponentRef<T>> {
81-
constructor(component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null, componentFactoryResolver?: ComponentFactoryResolver | null, projectableNodes?: Node[][] | null);
81+
constructor(component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null,
82+
_componentFactoryResolver?: any, projectableNodes?: Node[][] | null);
8283
component: ComponentType<T>;
83-
componentFactoryResolver?: ComponentFactoryResolver | null;
84+
// @deprecated (undocumented)
85+
componentFactoryResolver?: any;
8486
injector?: Injector | null;
8587
projectableNodes?: Node[][] | null;
8688
viewContainerRef?: ViewContainerRef | null;
@@ -105,7 +107,8 @@ export class DomPortalHost extends DomPortalOutlet {
105107
// @public
106108
export class DomPortalOutlet extends BasePortalOutlet {
107109
constructor(
108-
outletElement: Element, _componentFactoryResolver?: ComponentFactoryResolver | undefined, _appRef?: ApplicationRef | undefined, _defaultInjector?: Injector | undefined,
110+
outletElement: Element,
111+
_componentFactoryResolver?: any, _appRef?: ApplicationRef | undefined, _defaultInjector?: Injector | undefined,
109112
_document?: any);
110113
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T>;
111114
// @deprecated

0 commit comments

Comments
 (0)