2016-04-12 09:40:37 -07:00
|
|
|
import {
|
|
|
|
ListWrapper,
|
|
|
|
MapWrapper,
|
|
|
|
Map,
|
|
|
|
StringMapWrapper,
|
|
|
|
} from 'angular2/src/facade/collection';
|
|
|
|
import {
|
|
|
|
ChangeDetector,
|
|
|
|
ChangeDispatcher,
|
|
|
|
DirectiveIndex,
|
|
|
|
BindingTarget,
|
|
|
|
Locals,
|
|
|
|
ProtoChangeDetector,
|
|
|
|
ChangeDetectorRef
|
|
|
|
} from 'angular2/src/core/change_detection/change_detection';
|
2015-12-02 10:35:51 -08:00
|
|
|
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
2015-08-20 14:28:25 -07:00
|
|
|
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
2015-05-20 09:48:15 -07:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
import {AppProtoElement, AppElement, DirectiveProvider} from './element';
|
2016-04-12 09:40:37 -07:00
|
|
|
import {
|
|
|
|
isPresent,
|
|
|
|
isBlank,
|
|
|
|
Type,
|
|
|
|
isArray,
|
|
|
|
isNumber,
|
|
|
|
CONST,
|
|
|
|
CONST_EXPR
|
|
|
|
} from 'angular2/src/facade/lang';
|
2015-11-06 17:34:07 -08:00
|
|
|
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
2016-01-13 21:35:21 -08:00
|
|
|
import {Renderer, RootRenderer, RenderDebugInfo} from 'angular2/src/core/render/api';
|
2015-12-02 10:35:51 -08:00
|
|
|
import {ViewRef_, HostViewFactoryRef} from './view_ref';
|
2015-08-07 11:41:38 -07:00
|
|
|
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
2015-11-17 15:24:36 -08:00
|
|
|
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
|
2015-04-24 17:53:06 -07:00
|
|
|
|
2015-08-20 14:28:25 -07:00
|
|
|
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
2015-12-02 10:35:51 -08:00
|
|
|
import {Pipes} from 'angular2/src/core/pipes/pipes';
|
|
|
|
import {AppViewManager_, AppViewManager} from './view_manager';
|
|
|
|
import {ResolvedMetadataCache} from './resolved_metadata_cache';
|
|
|
|
import {ViewType} from './view_type';
|
2015-07-28 12:43:41 -07:00
|
|
|
|
2015-08-20 15:11:12 -07:00
|
|
|
const REFLECT_PREFIX: string = 'ng-reflect-';
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
const EMPTY_CONTEXT = CONST_EXPR(new Object());
|
2014-09-28 16:29:11 -07:00
|
|
|
|
2014-11-21 15:13:01 -08:00
|
|
|
/**
|
2015-07-07 08:15:58 +02:00
|
|
|
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
2015-03-31 22:47:11 +00:00
|
|
|
*
|
2014-10-10 20:44:55 -07:00
|
|
|
*/
|
2015-12-02 10:35:51 -08:00
|
|
|
export class AppView implements ChangeDispatcher {
|
|
|
|
ref: ViewRef_;
|
|
|
|
rootNodesOrAppElements: any[];
|
|
|
|
allNodes: any[];
|
|
|
|
disposables: Function[];
|
|
|
|
appElements: AppElement[];
|
2015-06-24 13:46:39 -07:00
|
|
|
|
2015-04-06 13:19:30 -07:00
|
|
|
/**
|
|
|
|
* The context against which data-binding expressions in this view are evaluated against.
|
|
|
|
* This is always a component instance.
|
|
|
|
*/
|
2015-06-12 22:38:44 +02:00
|
|
|
context: any = null;
|
2015-04-06 13:19:30 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Variables, local to this view, that can be used in binding expressions (in addition to the
|
|
|
|
* context). This is used for thing like `<video #player>` or
|
|
|
|
* `<li template="for #item of items">`, where "player" and "item" are locals, respectively.
|
|
|
|
*/
|
2015-05-20 09:48:15 -07:00
|
|
|
locals: Locals;
|
2015-01-02 14:23:59 -08:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
pipes: Pipes;
|
|
|
|
|
|
|
|
parentInjector: Injector;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether root injectors of this view
|
|
|
|
* have a hostBoundary.
|
|
|
|
*/
|
|
|
|
hostInjectorBoundary: boolean;
|
|
|
|
|
|
|
|
destroyed: boolean = false;
|
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
constructor(public proto: AppProtoView, public renderer: Renderer,
|
|
|
|
public viewManager: AppViewManager_, public projectableNodes: Array<any | any[]>,
|
|
|
|
public containerAppElement: AppElement,
|
|
|
|
imperativelyCreatedProviders: ResolvedProvider[], rootInjector: Injector,
|
|
|
|
public changeDetector: ChangeDetector) {
|
2015-10-06 06:53:39 -07:00
|
|
|
this.ref = new ViewRef_(this);
|
2015-12-02 10:35:51 -08:00
|
|
|
var injectorWithHostBoundary = AppElement.getViewParentInjector(
|
|
|
|
this.proto.type, containerAppElement, imperativelyCreatedProviders, rootInjector);
|
|
|
|
this.parentInjector = injectorWithHostBoundary.injector;
|
|
|
|
this.hostInjectorBoundary = injectorWithHostBoundary.hostInjectorBoundary;
|
|
|
|
var pipes;
|
|
|
|
var context;
|
|
|
|
switch (proto.type) {
|
|
|
|
case ViewType.COMPONENT:
|
|
|
|
pipes = new Pipes(proto.protoPipes, containerAppElement.getInjector());
|
|
|
|
context = containerAppElement.getComponent();
|
|
|
|
break;
|
|
|
|
case ViewType.EMBEDDED:
|
|
|
|
pipes = containerAppElement.parentView.pipes;
|
|
|
|
context = containerAppElement.parentView.context;
|
|
|
|
break;
|
|
|
|
case ViewType.HOST:
|
|
|
|
pipes = null;
|
|
|
|
context = EMPTY_CONTEXT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
this.pipes = pipes;
|
|
|
|
this.context = context;
|
|
|
|
}
|
2015-06-24 13:46:39 -07:00
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
|
|
|
|
appElements: AppElement[]) {
|
2015-12-02 10:35:51 -08:00
|
|
|
this.rootNodesOrAppElements = rootNodesOrAppElements;
|
|
|
|
this.allNodes = allNodes;
|
|
|
|
this.disposables = disposables;
|
|
|
|
this.appElements = appElements;
|
|
|
|
var localsMap = new Map<string, any>();
|
2016-02-19 11:49:31 -08:00
|
|
|
StringMapWrapper.forEach(
|
|
|
|
this.proto.templateVariableBindings,
|
|
|
|
(templateName: string, _: string) => { localsMap.set(templateName, null); });
|
2015-12-02 10:35:51 -08:00
|
|
|
for (var i = 0; i < appElements.length; i++) {
|
|
|
|
var appEl = appElements[i];
|
2016-01-13 21:35:21 -08:00
|
|
|
var providerTokens = [];
|
|
|
|
if (isPresent(appEl.proto.protoInjector)) {
|
|
|
|
for (var j = 0; j < appEl.proto.protoInjector.numberOfProviders; j++) {
|
|
|
|
providerTokens.push(appEl.proto.protoInjector.getProviderAtIndex(j).key.token);
|
|
|
|
}
|
|
|
|
}
|
2016-04-12 09:40:37 -07:00
|
|
|
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings,
|
|
|
|
(directiveIndex: number, name: string) => {
|
|
|
|
if (isBlank(directiveIndex)) {
|
|
|
|
localsMap.set(name, appEl.nativeElement);
|
|
|
|
} else {
|
|
|
|
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
|
|
|
|
}
|
|
|
|
});
|
2016-01-13 21:35:21 -08:00
|
|
|
this.renderer.setElementDebugInfo(
|
2016-04-12 09:40:37 -07:00
|
|
|
appEl.nativeElement, new RenderDebugInfo(appEl.getInjector(), appEl.getComponent(),
|
|
|
|
providerTokens, localsMap));
|
2015-12-02 10:35:51 -08:00
|
|
|
}
|
|
|
|
var parentLocals = null;
|
|
|
|
if (this.proto.type !== ViewType.COMPONENT) {
|
|
|
|
parentLocals =
|
|
|
|
isPresent(this.containerAppElement) ? this.containerAppElement.parentView.locals : null;
|
|
|
|
}
|
|
|
|
if (this.proto.type === ViewType.COMPONENT) {
|
|
|
|
// Note: the render nodes have been attached to their host element
|
|
|
|
// in the ViewFactory already.
|
|
|
|
this.containerAppElement.attachComponentView(this);
|
|
|
|
this.containerAppElement.parentView.changeDetector.addViewChild(this.changeDetector);
|
|
|
|
}
|
|
|
|
this.locals = new Locals(parentLocals, localsMap);
|
|
|
|
this.changeDetector.hydrate(this.context, this.locals, this, this.pipes);
|
|
|
|
this.viewManager.onViewCreated(this);
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
destroy() {
|
|
|
|
if (this.destroyed) {
|
|
|
|
throw new BaseException('This view has already been destroyed!');
|
|
|
|
}
|
|
|
|
this.changeDetector.destroyRecursive();
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
notifyOnDestroy() {
|
|
|
|
this.destroyed = true;
|
|
|
|
var hostElement =
|
|
|
|
this.proto.type === ViewType.COMPONENT ? this.containerAppElement.nativeElement : null;
|
|
|
|
this.renderer.destroyView(hostElement, this.allNodes);
|
|
|
|
for (var i = 0; i < this.disposables.length; i++) {
|
|
|
|
this.disposables[i]();
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
2015-12-02 10:35:51 -08:00
|
|
|
this.viewManager.onViewDestroyed(this);
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
get changeDetectorRef(): ChangeDetectorRef { return this.changeDetector.ref; }
|
2014-12-01 18:41:55 -08:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
|
|
|
|
|
|
|
|
hasLocal(contextName: string): boolean {
|
|
|
|
return StringMapWrapper.contains(this.proto.templateVariableBindings, contextName);
|
|
|
|
}
|
|
|
|
|
|
|
|
setLocal(contextName: string, value: any): void {
|
|
|
|
if (!this.hasLocal(contextName)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var templateName = this.proto.templateVariableBindings[contextName];
|
|
|
|
this.locals.set(templateName, value);
|
2015-03-10 10:03:26 +01:00
|
|
|
}
|
|
|
|
|
2015-03-31 09:07:01 -07:00
|
|
|
// dispatch to element injector or text nodes based on context
|
2015-08-19 11:26:45 -07:00
|
|
|
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
2015-06-24 13:46:39 -07:00
|
|
|
if (b.isTextNode()) {
|
2015-12-02 10:35:51 -08:00
|
|
|
this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
|
2015-06-18 15:44:44 -07:00
|
|
|
} else {
|
2015-12-02 10:35:51 -08:00
|
|
|
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
2015-06-24 13:46:39 -07:00
|
|
|
if (b.isElementProperty()) {
|
2015-12-02 10:35:51 -08:00
|
|
|
this.renderer.setElementProperty(nativeElement, b.name, currentValue);
|
2015-06-24 13:46:39 -07:00
|
|
|
} else if (b.isElementAttribute()) {
|
2016-04-12 09:40:37 -07:00
|
|
|
this.renderer.setElementAttribute(nativeElement, b.name,
|
|
|
|
isPresent(currentValue) ? `${currentValue}` : null);
|
2015-06-24 13:46:39 -07:00
|
|
|
} else if (b.isElementClass()) {
|
2015-12-02 10:35:51 -08:00
|
|
|
this.renderer.setElementClass(nativeElement, b.name, currentValue);
|
2015-06-24 13:46:39 -07:00
|
|
|
} else if (b.isElementStyle()) {
|
2015-08-19 11:26:45 -07:00
|
|
|
var unit = isPresent(b.unit) ? b.unit : '';
|
2016-04-12 09:40:37 -07:00
|
|
|
this.renderer.setElementStyle(nativeElement, b.name,
|
|
|
|
isPresent(currentValue) ? `${currentValue}${unit}` : null);
|
2015-06-24 13:46:39 -07:00
|
|
|
} else {
|
|
|
|
throw new BaseException('Unsupported directive record');
|
|
|
|
}
|
2015-04-07 20:54:20 -07:00
|
|
|
}
|
|
|
|
}
|
2015-04-09 21:20:11 +02:00
|
|
|
|
2015-08-20 15:11:12 -07:00
|
|
|
logBindingUpdate(b: BindingTarget, value: any): void {
|
|
|
|
if (b.isDirective() || b.isElementProperty()) {
|
2015-12-02 10:35:51 -08:00
|
|
|
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
2015-11-19 11:14:44 -08:00
|
|
|
this.renderer.setBindingDebugInfo(
|
2015-12-02 10:35:51 -08:00
|
|
|
nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
2015-08-20 15:11:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-27 21:19:56 -07:00
|
|
|
notifyAfterContentChecked(): void {
|
2015-12-02 10:35:51 -08:00
|
|
|
var count = this.appElements.length;
|
|
|
|
for (var i = count - 1; i >= 0; i--) {
|
|
|
|
this.appElements[i].ngAfterContentChecked();
|
2015-06-12 09:45:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-28 18:11:04 -07:00
|
|
|
notifyAfterViewChecked(): void {
|
2015-12-02 10:35:51 -08:00
|
|
|
var count = this.appElements.length;
|
|
|
|
for (var i = count - 1; i >= 0; i--) {
|
|
|
|
this.appElements[i].ngAfterViewChecked();
|
2015-08-18 21:51:28 -07:00
|
|
|
}
|
2015-08-28 18:11:04 -07:00
|
|
|
}
|
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
getDebugContext(appElement: AppElement, elementIndex: number,
|
|
|
|
directiveIndex: number): DebugContext {
|
2015-07-23 18:01:34 -07:00
|
|
|
try {
|
2015-12-02 10:35:51 -08:00
|
|
|
if (isBlank(appElement) && elementIndex < this.appElements.length) {
|
|
|
|
appElement = this.appElements[elementIndex];
|
|
|
|
}
|
|
|
|
var container = this.containerAppElement;
|
2015-07-23 18:01:34 -07:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
var element = isPresent(appElement) ? appElement.nativeElement : null;
|
2015-10-01 10:07:49 -07:00
|
|
|
var componentElement = isPresent(container) ? container.nativeElement : null;
|
2015-12-02 10:35:51 -08:00
|
|
|
var directive =
|
|
|
|
isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
|
|
|
|
var injector = isPresent(appElement) ? appElement.getInjector() : null;
|
2015-07-23 18:01:34 -07:00
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
return new DebugContext(element, componentElement, directive, this.context,
|
|
|
|
_localsToStringMap(this.locals), injector);
|
2015-07-27 15:47:42 -07:00
|
|
|
|
2015-07-23 18:01:34 -07:00
|
|
|
} catch (e) {
|
|
|
|
// TODO: vsavkin log the exception once we have a good way to log errors and warnings
|
2015-09-30 15:28:26 -07:00
|
|
|
// if an error happens during getting the debug context, we return null.
|
2015-07-27 15:47:42 -07:00
|
|
|
return null;
|
2015-07-23 18:01:34 -07:00
|
|
|
}
|
2015-07-22 12:00:35 -07:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
getDirectiveFor(directive: DirectiveIndex): any {
|
|
|
|
return this.appElements[directive.elementIndex].getDirectiveAtIndex(directive.directiveIndex);
|
2015-05-11 12:31:16 -07:00
|
|
|
}
|
|
|
|
|
2016-01-21 18:13:58 -08:00
|
|
|
getDetectorFor(directive: DirectiveIndex): ChangeDetector {
|
2015-12-02 10:35:51 -08:00
|
|
|
var componentView = this.appElements[directive.elementIndex].componentView;
|
|
|
|
return isPresent(componentView) ? componentView.changeDetector : null;
|
2015-06-24 13:46:39 -07:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
/**
|
|
|
|
* Triggers the event handlers for the element and the directives.
|
|
|
|
*
|
|
|
|
* This method is intended to be called from directive EventEmitters.
|
|
|
|
*
|
|
|
|
* @param {string} eventName
|
|
|
|
* @param {*} eventObj
|
|
|
|
* @param {number} boundElementIndex
|
|
|
|
* @return false if preventDefault must be applied to the DOM event
|
|
|
|
*/
|
|
|
|
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): boolean {
|
|
|
|
return this.changeDetector.handleEvent(eventName, boundElementIndex, eventObj);
|
2014-12-02 17:09:46 -08:00
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
}
|
|
|
|
|
2015-10-02 16:47:54 -07:00
|
|
|
function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
2015-07-23 18:01:34 -07:00
|
|
|
var res = {};
|
|
|
|
var c = locals;
|
|
|
|
while (isPresent(c)) {
|
|
|
|
res = StringMapWrapper.merge(res, MapWrapper.toStringMap(c.current));
|
|
|
|
c = c.parent;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-07-27 15:47:42 -07:00
|
|
|
/**
|
2015-12-02 10:35:51 -08:00
|
|
|
*
|
2015-07-27 15:47:42 -07:00
|
|
|
*/
|
2015-12-02 10:35:51 -08:00
|
|
|
export class AppProtoView {
|
2016-04-12 09:40:37 -07:00
|
|
|
static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
|
|
|
|
templateVariableBindings: {[key: string]: string}): AppProtoView {
|
2015-12-02 10:35:51 -08:00
|
|
|
var protoPipes = null;
|
|
|
|
if (isPresent(pipes) && pipes.length > 0) {
|
|
|
|
var boundPipes = ListWrapper.createFixedSize(pipes.length);
|
|
|
|
for (var i = 0; i < pipes.length; i++) {
|
|
|
|
boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
|
|
|
|
}
|
|
|
|
protoPipes = ProtoPipes.fromProviders(boundPipes);
|
|
|
|
}
|
|
|
|
return new AppProtoView(type, protoPipes, templateVariableBindings);
|
2015-07-27 15:47:42 -07:00
|
|
|
}
|
2015-12-02 10:35:51 -08:00
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
constructor(public type: ViewType, public protoPipes: ProtoPipes,
|
|
|
|
public templateVariableBindings: {[key: string]: string}) {}
|
2015-07-27 15:47:42 -07:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
|
|
|
|
@CONST()
|
|
|
|
export class HostViewFactory {
|
|
|
|
constructor(public selector: string, public viewFactory: Function) {}
|
2015-10-01 10:07:49 -07:00
|
|
|
}
|
2015-07-27 15:47:42 -07:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
|
|
|
|
return _flattenNestedViewRenderNodes(nodes, []);
|
|
|
|
}
|
2015-10-01 10:07:49 -07:00
|
|
|
|
2015-12-02 10:35:51 -08:00
|
|
|
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
|
|
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
|
|
var node = nodes[i];
|
|
|
|
if (node instanceof AppElement) {
|
|
|
|
var appEl = <AppElement>node;
|
|
|
|
renderNodes.push(appEl.nativeElement);
|
|
|
|
if (isPresent(appEl.nestedViews)) {
|
|
|
|
for (var k = 0; k < appEl.nestedViews.length; k++) {
|
|
|
|
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
renderNodes.push(node);
|
2015-10-01 10:07:49 -07:00
|
|
|
}
|
2014-11-18 16:38:36 -08:00
|
|
|
}
|
2015-12-02 10:35:51 -08:00
|
|
|
return renderNodes;
|
|
|
|
}
|
2014-11-18 16:38:36 -08:00
|
|
|
|
2016-02-03 16:35:38 -08:00
|
|
|
export function findLastRenderNode(node: any): any {
|
|
|
|
var lastNode;
|
|
|
|
if (node instanceof AppElement) {
|
|
|
|
var appEl = <AppElement>node;
|
|
|
|
lastNode = appEl.nativeElement;
|
|
|
|
if (isPresent(appEl.nestedViews)) {
|
|
|
|
// Note: Views might have no root nodes at all!
|
|
|
|
for (var i = appEl.nestedViews.length - 1; i >= 0; i--) {
|
|
|
|
var nestedView = appEl.nestedViews[i];
|
|
|
|
if (nestedView.rootNodesOrAppElements.length > 0) {
|
|
|
|
lastNode = findLastRenderNode(
|
|
|
|
nestedView.rootNodesOrAppElements[nestedView.rootNodesOrAppElements.length - 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lastNode = node;
|
|
|
|
}
|
|
|
|
return lastNode;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:40:37 -07:00
|
|
|
export function checkSlotCount(componentName: string, expectedSlotCount: number,
|
|
|
|
projectableNodes: any[][]): void {
|
2015-12-02 10:35:51 -08:00
|
|
|
var givenSlotCount = isPresent(projectableNodes) ? projectableNodes.length : 0;
|
|
|
|
if (givenSlotCount < expectedSlotCount) {
|
|
|
|
throw new BaseException(
|
|
|
|
`The component ${componentName} has ${expectedSlotCount} <ng-content> elements,` +
|
|
|
|
` but only ${givenSlotCount} slots were provided.`);
|
|
|
|
}
|
2015-10-02 16:47:54 -07:00
|
|
|
}
|