diff --git a/packages/compiler/src/metadata_resolver.ts b/packages/compiler/src/metadata_resolver.ts index 713ffd1e422bc5..c0d70800ff991b 100644 --- a/packages/compiler/src/metadata_resolver.ts +++ b/packages/compiler/src/metadata_resolver.ts @@ -136,30 +136,23 @@ export class CompileMetadataResolver { } } - private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory { + private getComponentFactory( + selector: string, dirType: any, inputs: {[key: string]: string}, + outputs: {[key: string]: string}): StaticSymbol|ComponentFactory { if (dirType instanceof StaticSymbol) { return this._staticSymbolCache.get( ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType)); } else { const hostView = this.getHostComponentViewClass(dirType); - // Note: inputs / outputs / ngContentSelectors will be filled later once the template is + // Note: ngContentSelectors will be filled later once the template is // loaded. - return createComponentFactory(selector, dirType, hostView, {}, {}, []); + return createComponentFactory(selector, dirType, hostView, inputs, outputs, []); } } private initComponentFactory( - factory: StaticSymbol|ComponentFactory, inputs: {[key: string]: string}, - outputs: {[key: string]: string}, ngContentSelectors: string[]) { + factory: StaticSymbol|ComponentFactory, ngContentSelectors: string[]) { if (!(factory instanceof StaticSymbol)) { - for (let propName in inputs) { - const templateName = inputs[propName]; - factory.inputs.push({propName, templateName}); - } - for (let propName in outputs) { - const templateName = outputs[propName]; - factory.outputs.push({propName, templateName}); - } factory.ngContentSelectors.push(...ngContentSelectors); } } @@ -205,9 +198,7 @@ export class CompileMetadataResolver { template: templateMetadata }); if (templateMetadata) { - this.initComponentFactory( - metadata.componentFactory, metadata.inputs, metadata.outputs, - templateMetadata.ngContentSelectors); + this.initComponentFactory(metadata.componentFactory, templateMetadata.ngContentSelectors); } this._directiveCache.set(directiveType, normalizedDirMeta); this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); @@ -343,10 +334,12 @@ export class CompileMetadataResolver { componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) : undefined, rendererType: nonNormalizedTemplateMetadata ? this.getRendererType(directiveType) : undefined, - componentFactory: nonNormalizedTemplateMetadata ? - this.getComponentFactory(selector, directiveType) : - undefined + componentFactory: undefined }); + if (nonNormalizedTemplateMetadata) { + metadata.componentFactory = + this.getComponentFactory(selector, directiveType, metadata.inputs, metadata.outputs); + } cacheEntry = {metadata, annotation: dirMeta}; this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry); return cacheEntry; diff --git a/packages/core/src/view/element.ts b/packages/core/src/view/element.ts index 95737968c3d148..633e740bff61a1 100644 --- a/packages/core/src/view/element.ts +++ b/packages/core/src/view/element.ts @@ -6,11 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import {ViewEncapsulation} from '../metadata/view'; import {Renderer2, RendererType2} from '../render/api'; import {SecurityContext} from '../security'; import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, NodeData, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData, asProviderData} from './types'; -import {NOOP, checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveViewDefinition, splitMatchedQueriesDsl, splitNamespace} from './util'; +import {NOOP, checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveRendererType2, resolveViewDefinition, splitMatchedQueriesDsl, splitNamespace} from './util'; export function anchorDef( flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], @@ -112,11 +113,7 @@ export function elementDef( const [ns, name] = splitNamespace(namespaceAndName); return [ns, name, value]; }); - // This is needed as the jit compiler always uses an empty hash as default RendererType2, - // which is not filled for host views. - if (componentRendererType && componentRendererType.encapsulation == null) { - componentRendererType = null; - } + componentRendererType = resolveRendererType2(componentRendererType); if (componentView) { flags |= NodeFlags.ComponentView; } diff --git a/packages/core/src/view/refs.ts b/packages/core/src/view/refs.ts index cd720198dce8f5..70413c3d0824e1 100644 --- a/packages/core/src/view/refs.ts +++ b/packages/core/src/view/refs.ts @@ -25,22 +25,14 @@ import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachVi const EMPTY_CONTEXT = new Object(); +// Attention: this function is called as top level function. +// Putting any logic in here will destroy closure tree shaking! export function createComponentFactory( selector: string, componentType: Type, viewDefFactory: ViewDefinitionFactory, inputs: {[propName: string]: string}, outputs: {[propName: string]: string}, ngContentSelectors: string[]): ComponentFactory { - const inputsArr: {propName: string, templateName: string}[] = []; - for (let propName in inputs) { - const templateName = inputs[propName]; - inputsArr.push({propName, templateName}); - } - const outputsArr: {propName: string, templateName: string}[] = []; - for (let propName in outputs) { - const templateName = outputs[propName]; - outputsArr.push({propName, templateName}); - } return new ComponentFactory_( - selector, componentType, viewDefFactory, inputsArr, outputsArr, ngContentSelectors); + selector, componentType, viewDefFactory, inputs, outputs, ngContentSelectors); } export function getComponentViewDefinitionFactory(componentFactory: ComponentFactory): @@ -56,14 +48,32 @@ class ComponentFactory_ extends ComponentFactory { constructor( public selector: string, public componentType: Type, - viewDefFactory: ViewDefinitionFactory, - public inputs: {propName: string, templateName: string}[], - public outputs: {propName: string, templateName: string}[], - public ngContentSelectors: string[]) { + viewDefFactory: ViewDefinitionFactory, private _inputs: {[propName: string]: string}, + private _outputs: {[propName: string]: string}, public ngContentSelectors: string[]) { + // Attention: this ctor is called as top level function. + // Putting any logic in here will destroy closure tree shaking! super(); this.viewDefFactory = viewDefFactory; } + get inputs() { + const inputsArr: {propName: string, templateName: string}[] = []; + for (let propName in this._inputs) { + const templateName = this._inputs[propName]; + inputsArr.push({propName, templateName}); + } + return inputsArr; + } + + get outputs() { + const outputsArr: {propName: string, templateName: string}[] = []; + for (let propName in this._outputs) { + const templateName = this._outputs[propName]; + outputsArr.push({propName, templateName}); + } + return outputsArr; + } + /** * Creates a new component. */ diff --git a/packages/core/src/view/util.ts b/packages/core/src/view/util.ts index 8df0fcdb8d4bed..2e41682500ecfb 100644 --- a/packages/core/src/view/util.ts +++ b/packages/core/src/view/util.ts @@ -42,21 +42,42 @@ export function unwrapValue(value: any): any { return value; } -let _renderCompCount = 0; +const UNDEFINED_RENDERER_TYPE_ID = '$$undefined'; +const EMPTY_RENDERER_TYPE_ID = '$$empty'; +// Attention: this function is called as top level function. +// Putting any logic in here will destroy closure tree shaking! export function createRendererType2(values: { styles: (string | any[])[], encapsulation: ViewEncapsulation, data: {[kind: string]: any[]} }): RendererType2 { - const isFilled = values && (values.encapsulation !== ViewEncapsulation.None || - values.styles.length || Object.keys(values.data).length); - if (isFilled) { - const id = `c${_renderCompCount++}`; - return {id: id, styles: values.styles, encapsulation: values.encapsulation, data: values.data}; - } else { - return null; + return { + id: UNDEFINED_RENDERER_TYPE_ID, + styles: values.styles, + encapsulation: values.encapsulation, + data: values.data + }; +} + +let _renderCompCount = 0; + +export function resolveRendererType2(type: RendererType2): RendererType2 { + if (type && type.id === UNDEFINED_RENDERER_TYPE_ID) { + // first time we see this RendererType2. Initialize it... + const isFilled = + ((type.encapsulation != null && type.encapsulation !== ViewEncapsulation.None) || + type.styles.length || Object.keys(type.data).length); + if (isFilled) { + type.id = `c${_renderCompCount++}`; + } else { + type.id = EMPTY_RENDERER_TYPE_ID; + } + } + if (type && type.id === EMPTY_RENDERER_TYPE_ID) { + type = null; } + return type; } export function checkBinding(