mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(portal): expose attached result in CdkPortalOutlet (#9326)
Exposes the attach `ComponentRef` or `EmbeddedViewRef` in the `CdkPortalOutlet` directive. Fixes #9304.
This commit is contained in:
committed by
Jeremy Elbourn
parent
a72085b5a9
commit
b626b133d8
@@ -17,6 +17,8 @@ import {
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import {Portal, TemplatePortal, ComponentPortal, BasePortalOutlet} from './portal';
|
||||
|
||||
@@ -35,6 +37,11 @@ export class CdkPortal extends TemplatePortal<any> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible attached references to the CdkPortalOutlet.
|
||||
*/
|
||||
export type CdkPortalOutletAttachedRef = ComponentRef<any> | EmbeddedViewRef<any> | null;
|
||||
|
||||
|
||||
/**
|
||||
* Directive version of a PortalOutlet. Because the directive *is* a PortalOutlet, portals can be
|
||||
@@ -52,6 +59,9 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
|
||||
/** Whether the portal component is initialized. */
|
||||
private _isInitialized = false;
|
||||
|
||||
/** Reference to the currently-attached component/view ref. */
|
||||
private _attachedRef: CdkPortalOutletAttachedRef;
|
||||
|
||||
constructor(
|
||||
private _componentFactoryResolver: ComponentFactoryResolver,
|
||||
private _viewContainerRef: ViewContainerRef) {
|
||||
@@ -93,6 +103,14 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
|
||||
this._attachedPortal = portal;
|
||||
}
|
||||
|
||||
@Output('attached') attached: EventEmitter<CdkPortalOutletAttachedRef> =
|
||||
new EventEmitter<CdkPortalOutletAttachedRef>();
|
||||
|
||||
/** Component or view reference that is attached to the portal. */
|
||||
get attachedRef(): CdkPortalOutletAttachedRef {
|
||||
return this._attachedRef;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._isInitialized = true;
|
||||
}
|
||||
@@ -100,6 +118,7 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
|
||||
ngOnDestroy() {
|
||||
super.dispose();
|
||||
this._attachedPortal = null;
|
||||
this._attachedRef = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +144,8 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
|
||||
|
||||
super.setDisposeFn(() => ref.destroy());
|
||||
this._attachedPortal = portal;
|
||||
this._attachedRef = ref;
|
||||
this.attached.emit(ref);
|
||||
|
||||
return ref;
|
||||
}
|
||||
@@ -140,6 +161,8 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr
|
||||
super.setDisposeFn(() => this._viewContainerRef.clear());
|
||||
|
||||
this._attachedPortal = portal;
|
||||
this._attachedRef = viewRef;
|
||||
this.attached.emit(viewRef);
|
||||
|
||||
return viewRef;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
Optional,
|
||||
Injector,
|
||||
ApplicationRef,
|
||||
TemplateRef
|
||||
TemplateRef,
|
||||
ComponentRef,
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {CdkPortal, CdkPortalOutlet, PortalModule} from './portal-directives';
|
||||
@@ -45,6 +46,9 @@ describe('Portals', () => {
|
||||
// Expect that the content of the attached portal is present.
|
||||
expect(hostContainer.textContent).toContain('Pizza');
|
||||
expect(testAppComponent.portalOutlet.portal).toBe(componentPortal);
|
||||
expect(testAppComponent.portalOutlet.attachedRef instanceof ComponentRef).toBe(true);
|
||||
expect(testAppComponent.attachedSpy)
|
||||
.toHaveBeenCalledWith(testAppComponent.portalOutlet.attachedRef);
|
||||
});
|
||||
|
||||
it('should load a template into the portal', () => {
|
||||
@@ -58,6 +62,13 @@ describe('Portals', () => {
|
||||
// Expect that the content of the attached portal is present and no context is projected
|
||||
expect(hostContainer.textContent).toContain('Banana');
|
||||
expect(testAppComponent.portalOutlet.portal).toBe(templatePortal);
|
||||
|
||||
// We can't test whether it's an instance of an `EmbeddedViewRef` so
|
||||
// we verify that it's defined and that it's not a ComponentRef.
|
||||
expect(testAppComponent.portalOutlet.attachedRef instanceof ComponentRef).toBe(false);
|
||||
expect(testAppComponent.portalOutlet.attachedRef).toBeTruthy();
|
||||
expect(testAppComponent.attachedSpy)
|
||||
.toHaveBeenCalledWith(testAppComponent.portalOutlet.attachedRef);
|
||||
});
|
||||
|
||||
it('should project template context bindings in the portal', () => {
|
||||
@@ -499,7 +510,7 @@ class ArbitraryViewContainerRefComponent {
|
||||
selector: 'portal-test',
|
||||
template: `
|
||||
<div class="portal-container">
|
||||
<ng-template [cdkPortalOutlet]="selectedPortal"></ng-template>
|
||||
<ng-template [cdkPortalOutlet]="selectedPortal" (attached)="attachedSpy($event)"></ng-template>
|
||||
</div>
|
||||
|
||||
<ng-template cdk-portal>Cake</ng-template>
|
||||
@@ -524,6 +535,7 @@ class PortalTestApp {
|
||||
selectedPortal: Portal<any>|undefined;
|
||||
fruit: string = 'Banana';
|
||||
fruits = ['Apple', 'Pineapple', 'Durian'];
|
||||
attachedSpy = jasmine.createSpy('attached spy');
|
||||
|
||||
constructor(public injector: Injector) { }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user