ng-messages2
Version:
Angular version of ng-message
178 lines (141 loc) • 4.87 kB
text/typescript
import {
TemplateRef,
ViewContainerRef,
ElementRef
} from '@angular/core';
import {
NullPortalHostError,
PortalAlreadyAttachedError,
NoPortalAttachedError,
NullPortalError,
PortalHostAlreadyDisposedError,
UnknownPortalTypeError
} from './portal-errors';
/**
* A `Portal` is something that you want to render somewhere else.
* It can be attach to / detached from a `PortalHost`.
*/
export abstract class Portal<T> {
private _attachedHost: PortalHost;
/** Attach this portal to a host. */
attach(host: PortalHost): T {
if (host == null) {
throw new NullPortalHostError();
}
if (host.hasAttached()) {
throw new PortalAlreadyAttachedError();
}
this._attachedHost = host;
return <T>host.attach(this);
}
/** Detach this portal from its host */
detach(): void {
const host = this._attachedHost;
if (host == null) {
throw new NoPortalAttachedError();
}
this._attachedHost = null;
return host.detach();
}
/** Whether this portal is attached to a host. */
get isAttached(): boolean {
return this._attachedHost != null;
}
/**
* Sets the PortalHost reference without performing `attach()`. This is used directly by
* the PortalHost when it is performing an `attach()` or `detach()`.
*/
setAttachedHost(host: PortalHost) {
this._attachedHost = host;
}
}
/**
* A `TemplatePortal` is a portal that represents some embedded template (TemplateRef).
*/
export class TemplatePortal extends Portal<Map<string, any>> {
/** The embedded template that will be used to instantiate an embedded View in the host. */
templateRef: TemplateRef<any>;
/** Reference to the ViewContainer into which the template will be stamped out. */
viewContainerRef: ViewContainerRef;
/**
* Additional locals for the instantiated embedded view.
* These locals can be seen as "exports" for the template, such as how ngFor has
* index / event / odd.
* See https://angular.io/docs/ts/latest/api/core/EmbeddedViewRef-class.html
*/
locals: Map<string, any> = new Map<string, any>();
constructor(template: TemplateRef<any>, viewContainerRef: ViewContainerRef) {
super();
this.templateRef = template;
this.viewContainerRef = viewContainerRef;
}
get origin(): ElementRef {
return this.templateRef.elementRef;
}
attach(host: PortalHost, locals?: Map<string, any>): Map<string, any> {
this.locals = locals == null ? new Map<string, any>() : locals;
return super.attach(host);
}
detach(): void {
this.locals = new Map<string, any>();
return super.detach();
}
}
/**
* A `PortalHost` is an space that can contain a single `Portal`.
*/
export interface PortalHost {
attach(portal: Portal<any>): any;
detach(): any;
dispose(): void;
hasAttached(): boolean;
}
/**
* Partial implementation of PortalHost that only deals with attaching either a TemplatePortal.
*/
export abstract class BasePortalHost implements PortalHost {
/** The portal currently attached to the host. */
private _attachedPortal: Portal<any>;
/** A function that will permanently dispose this host. */
private _disposeFn: () => void;
/** Whether this host has already been permanently disposed. */
private _isDisposed: boolean = false;
/** Whether this host has an attached portal. */
hasAttached() {
return this._attachedPortal != null;
}
attach(portal: Portal<any>): any {
if (portal == null) {
throw new NullPortalError();
}
if (this.hasAttached()) {
throw new PortalAlreadyAttachedError();
}
if (this._isDisposed) {
throw new PortalHostAlreadyDisposedError();
}
if (portal instanceof TemplatePortal) {
this._attachedPortal = portal;
return this.attachTemplatePortal(portal);
}
throw new UnknownPortalTypeError();
}
abstract attachTemplatePortal(portal: TemplatePortal): Map<string, any>;
detach(): void {
if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); }
this._attachedPortal = null;
if (this._disposeFn != null) {
this._disposeFn();
this._disposeFn = null;
}
}
dispose() {
if (this.hasAttached()) {
this.detach();
}
this._isDisposed = true;
}
setDisposeFn(fn: () => void) {
this._disposeFn = fn;
}
}