Skip to content

Commit b095048

Browse files
authored
fix(cdk/portal): allow bindings to be passed to component portal (#32814)
Adds some pass-through logic that allows users to pass inputs to the `ComponentPortal`. Fixes #32757.
1 parent 0d36e86 commit b095048

File tree

8 files changed

+51
-1
lines changed

8 files changed

+51
-1
lines changed

goldens/cdk/dialog/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { AfterContentInit } from '@angular/core';
88
import { AfterViewInit } from '@angular/core';
99
import * as _angular_cdk_portal from '@angular/cdk/portal';
10+
import { Binding } from '@angular/core';
1011
import { ChangeDetectorRef } from '@angular/core';
1112
import { ComponentRef } from '@angular/core';
1213
import { DoCheck } from '@angular/core';

goldens/cdk/menu/index.api.md

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

77
import { AfterContentInit } from '@angular/core';
8+
import { Binding } from '@angular/core';
89
import { ComponentRef } from '@angular/core';
910
import { DoCheck } from '@angular/core';
1011
import { ElementRef } from '@angular/core';

goldens/cdk/overlay/index.api.md

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

77
import { AfterContentInit } from '@angular/core';
8+
import { Binding } from '@angular/core';
89
import { ComponentRef } from '@angular/core';
910
import { DoCheck } from '@angular/core';
1011
import { ElementRef } from '@angular/core';

goldens/cdk/portal/index.api.md

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

77
import { ApplicationRef } from '@angular/core';
8+
import { Binding } from '@angular/core';
89
import { ComponentRef } from '@angular/core';
910
import { ElementRef } from '@angular/core';
1011
import { EmbeddedViewRef } from '@angular/core';
@@ -72,7 +73,8 @@ export type CdkPortalOutletAttachedRef = ComponentRef<any> | EmbeddedViewRef<any
7273

7374
// @public
7475
export class ComponentPortal<T> extends Portal<ComponentRef<T>> {
75-
constructor(component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null, projectableNodes?: Node[][] | null);
76+
constructor(component: ComponentType<T>, viewContainerRef?: ViewContainerRef | null, injector?: Injector | null, projectableNodes?: Node[][] | null, bindings?: Binding[]);
77+
readonly bindings: Binding[] | null;
7678
component: ComponentType<T>;
7779
injector?: Injector | null;
7880
projectableNodes?: Node[][] | null;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export class DomPortalOutlet extends BasePortalOutlet {
5959
injector,
6060
ngModuleRef,
6161
projectableNodes: portal.projectableNodes || undefined,
62+
bindings: portal.bindings || undefined,
6263
});
6364

6465
this.setDisposeFn(() => componentRef.destroy());
@@ -74,6 +75,7 @@ export class DomPortalOutlet extends BasePortalOutlet {
7475
elementInjector,
7576
environmentInjector,
7677
projectableNodes: portal.projectableNodes || undefined,
78+
bindings: portal.bindings || undefined,
7779
});
7880

7981
appRef.attachView(componentRef.hostView);

src/cdk/portal/portal-directives.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
140140
injector: portal.injector || viewContainerRef.injector,
141141
projectableNodes: portal.projectableNodes || undefined,
142142
ngModuleRef: this._moduleRef || undefined,
143+
bindings: portal.bindings || undefined,
143144
});
144145

145146
// If we're using a view container that's different from the injected one (e.g. when the portal

src/cdk/portal/portal.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ElementRef,
88
EnvironmentInjector,
99
Injector,
10+
Input,
1011
QueryList,
1112
TemplateRef,
1213
ViewChild,
@@ -15,6 +16,7 @@ import {
1516
createComponent,
1617
createEnvironmentInjector,
1718
inject,
19+
inputBinding,
1820
} from '@angular/core';
1921
import {ComponentFixture, TestBed} from '@angular/core/testing';
2022
import {DomPortalOutlet} from './dom-portal-outlet';
@@ -459,6 +461,25 @@ describe('Portals', () => {
459461

460462
expect(fixture.nativeElement.textContent).toContain('Projectable node');
461463
});
464+
465+
it('should be able to pass bindings to the component', () => {
466+
let flavor = 'pepperoni';
467+
const componentPortal = new ComponentPortal(PizzaMsg, null, null, null, [
468+
inputBinding('flavor', () => flavor),
469+
]);
470+
471+
fixture.componentInstance.selectedPortal = componentPortal;
472+
fixture.changeDetectorRef.markForCheck();
473+
fixture.detectChanges();
474+
475+
const ref = fixture.componentInstance.portalOutlet.attachedRef as ComponentRef<PizzaMsg>;
476+
expect(ref.instance.flavor).toBe('pepperoni');
477+
478+
flavor = 'cheese';
479+
fixture.changeDetectorRef.markForCheck();
480+
fixture.detectChanges();
481+
expect(ref.instance.flavor).toBe('cheese');
482+
});
462483
});
463484

464485
describe('DomPortalOutlet', () => {
@@ -717,6 +738,17 @@ describe('Portals', () => {
717738
host.attachDomPortal(new DomPortal(fixture.componentInstance.domPortalContent));
718739
expect(host.hasAttached()).toBe(true);
719740
});
741+
742+
it('should be able to pass bindings to the component', () => {
743+
const portal = new ComponentPortal(PizzaMsg, null, null, null, [
744+
inputBinding('flavor', () => 'pepperoni'),
745+
]);
746+
747+
const componentInstance: PizzaMsg = portal.attach(host).instance;
748+
someFixture.changeDetectorRef.markForCheck();
749+
someFixture.detectChanges();
750+
expect(componentInstance.flavor).toBe('pepperoni');
751+
});
720752
});
721753
});
722754

@@ -744,6 +776,8 @@ class ChocolateInjector {
744776
})
745777
class PizzaMsg {
746778
snack = inject(Chocolate, {optional: true});
779+
780+
@Input() flavor = 'unknown';
747781
}
748782

749783
/**

src/cdk/portal/portal.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ComponentRef,
1414
EmbeddedViewRef,
1515
Injector,
16+
Binding,
1617
} from '@angular/core';
1718
import {
1819
throwNullPortalOutletError,
@@ -99,17 +100,24 @@ export class ComponentPortal<T> extends Portal<ComponentRef<T>> {
99100
*/
100101
projectableNodes?: Node[][] | null;
101102

103+
/**
104+
* Bindings to apply to the created component.
105+
*/
106+
readonly bindings: Binding[] | null;
107+
102108
constructor(
103109
component: ComponentType<T>,
104110
viewContainerRef?: ViewContainerRef | null,
105111
injector?: Injector | null,
106112
projectableNodes?: Node[][] | null,
113+
bindings?: Binding[],
107114
) {
108115
super();
109116
this.component = component;
110117
this.viewContainerRef = viewContainerRef;
111118
this.injector = injector;
112119
this.projectableNodes = projectableNodes;
120+
this.bindings = bindings || null;
113121
}
114122
}
115123

0 commit comments

Comments
 (0)