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:
Kristiyan Kostadinov
2018-01-23 16:56:04 +01:00
committed by Jeremy Elbourn
parent a72085b5a9
commit b626b133d8
2 changed files with 37 additions and 2 deletions
+23
View File
@@ -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;
}
+14 -2
View File
@@ -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) { }