diff --git a/modules/benchmarks/src/largetable/render3/table.ts b/modules/benchmarks/src/largetable/render3/table.ts index 5f4a94caa1..15cac6a3be 100644 --- a/modules/benchmarks/src/largetable/render3/table.ts +++ b/modules/benchmarks/src/largetable/render3/table.ts @@ -17,7 +17,7 @@ export class LargeTableComponent { /** @nocollapse */ static ngComponentDef: ComponentDef = defineComponent({ type: LargeTableComponent, - tag: 'largetable', + selector: [[['largetable'], null]], template: function(ctx: LargeTableComponent, cm: boolean) { if (cm) { E(0, 'table'); diff --git a/modules/benchmarks/src/tree/render3/tree.ts b/modules/benchmarks/src/tree/render3/tree.ts index 08e96499df..74c3e5aeaa 100644 --- a/modules/benchmarks/src/tree/render3/tree.ts +++ b/modules/benchmarks/src/tree/render3/tree.ts @@ -37,7 +37,7 @@ export class TreeComponent { /** @nocollapse */ static ngComponentDef: ComponentDef = defineComponent({ type: TreeComponent, - tag: 'tree', + selector: [[['tree'], null]], template: function(ctx: TreeComponent, cm: boolean) { if (cm) { E(0, 'span'); @@ -54,7 +54,7 @@ export class TreeComponent { let cm0 = V(0); { if (cm0) { - E(0, TreeComponent); + E(0, 'tree'); e(); } p(0, 'data', b(ctx.data.left)); @@ -69,7 +69,7 @@ export class TreeComponent { let cm0 = V(0); { if (cm0) { - E(0, TreeComponent); + E(0, 'tree'); e(); } p(0, 'data', b(ctx.data.right)); @@ -80,7 +80,8 @@ export class TreeComponent { cr(); }, factory: () => new TreeComponent, - inputs: {data: 'data'} + inputs: {data: 'data'}, + directiveDefs: () => [TreeComponent.ngComponentDef] }); } @@ -90,7 +91,7 @@ export class TreeFunction { /** @nocollapse */ static ngComponentDef: ComponentDef = defineComponent({ type: TreeFunction, - tag: 'tree', + selector: [[['tree'], null]], template: function(ctx: TreeFunction, cm: boolean) { // bit of a hack TreeTpl(ctx.data, cm); diff --git a/packages/compiler/src/render3/r3_view_compiler.ts b/packages/compiler/src/render3/r3_view_compiler.ts index 1dfafd5f5a..04d0aceeb5 100644 --- a/packages/compiler/src/render3/r3_view_compiler.ts +++ b/packages/compiler/src/render3/r3_view_compiler.ts @@ -60,6 +60,9 @@ export function compileDirective( // e.g. 'type: MyDirective` field('type', outputCtx.importExpr(directive.type.reference)); + // e.g. `selector: [[[null, 'someDir', ''], null]]` + field('selector', createDirectiveSelector(directive.selector !)); + // e.g. `factory: () => new MyApp(injectElementRef())` field('factory', createFactory(directive.type, outputCtx, reflector, directive.queries)); @@ -118,13 +121,11 @@ export function compileComponent( // e.g. `type: MyApp` field('type', outputCtx.importExpr(component.type.reference)); - // e.g. `tag: 'my-app'` - // This is optional and only included if the first selector of a component has element. + // e.g. `selector: [[['my-app'], null]]` + field('selector', createDirectiveSelector(component.selector !)); + const selector = component.selector && CssSelector.parse(component.selector); const firstSelector = selector && selector[0]; - if (firstSelector && firstSelector.hasElementSelector()) { - field('tag', o.literal(firstSelector.element)); - } // e.g. `attr: ["class", ".my.app"] // This is optional an only included if the first selector of a component specifies attributes. @@ -916,6 +917,11 @@ type HostBindings = { [key: string]: string }; +// Turn a directive selector into an R3-compatible selector for directive def +function createDirectiveSelector(selector: string): o.Expression { + return asLiteral(parseSelectorsToR3Selector(CssSelector.parse(selector))); +} + function createHostAttributesArray( directiveMetadata: CompileDirectiveMetadata, outputCtx: OutputContext): o.Expression|null { const values: o.Expression[] = []; diff --git a/packages/compiler/test/render3/r3_compiler_compliance_spec.ts b/packages/compiler/test/render3/r3_compiler_compliance_spec.ts index 81c755b570..7b5cba9d77 100644 --- a/packages/compiler/test/render3/r3_compiler_compliance_spec.ts +++ b/packages/compiler/test/render3/r3_compiler_compliance_spec.ts @@ -92,11 +92,12 @@ describe('compiler compliance', () => { const ChildComponentDefinition = ` static ngComponentDef = $r3$.ɵdefineComponent({ type: ChildComponent, - tag: 'child', + selector: [[['child'], null]], factory: function ChildComponent_Factory() { return new ChildComponent(); }, template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { $r3$.ɵT(0, 'child-view'); + } } });`; @@ -105,6 +106,7 @@ describe('compiler compliance', () => { const SomeDirectiveDefinition = ` static ngDirectiveDef = $r3$.ɵdefineDirective({ type: SomeDirective, + selector: [[[null, 'some-directive', ''], null]], factory: function SomeDirective_Factory() {return new SomeDirective(); } }); `; @@ -116,7 +118,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -161,6 +163,7 @@ describe('compiler compliance', () => { const HostBindingDirDeclaration = ` static ngDirectiveDef = $r3$.ɵdefineDirective({ type: HostBindingDir, + selector: [[[null, 'hostBindingDir', ''], null]], factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, hostBindings: function HostBindingDir_HostBindings( dirIndex: $number$, elIndex: $number$) { @@ -203,6 +206,7 @@ describe('compiler compliance', () => { const IfDirectiveDefinition = ` static ngDirectiveDef = $r3$.ɵdefineDirective({ type: IfDirective, + selector: [[[null, 'if', ''], null]], factory: function IfDirective_Factory() { return new IfDirective($r3$.ɵinjectTemplateRef()); } });`; const MyComponentDefinition = ` @@ -211,7 +215,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -283,7 +287,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { @@ -361,7 +365,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { @@ -421,7 +425,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { @@ -485,7 +489,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { @@ -542,7 +546,7 @@ describe('compiler compliance', () => { const SimpleComponentDefinition = ` static ngComponentDef = $r3$.ɵdefineComponent({ type: SimpleComponent, - tag: 'simple', + selector: [[['simple'], null]], factory: function SimpleComponent_Factory() { return new SimpleComponent(); }, template: function SimpleComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -561,7 +565,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: ComplexComponent, - tag: 'complex', + selector: [[['complex'], null]], factory: function ComplexComponent_Factory() { return new ComplexComponent(); }, template: function ComplexComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -627,7 +631,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: ViewQueryComponent, - tag: 'view-query-component', + selector: [[['view-query-component'], null]], factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); }, template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) { var $tmp$: $any$; @@ -685,7 +689,7 @@ describe('compiler compliance', () => { const ContentQueryComponentDefinition = ` static ngComponentDef = $r3$.ɵdefineComponent({ type: ContentQueryComponent, - tag: 'content-query-component', + selector: [[['content-query-component'], null]], factory: function ContentQueryComponent_Factory() { return [new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, true)]; }, @@ -768,7 +772,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -809,7 +813,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -877,7 +881,7 @@ describe('compiler compliance', () => { const LifecycleCompDefinition = ` static ngComponentDef = $r3$.ɵdefineComponent({ type: LifecycleComp, - tag: 'lifecycle-comp', + selector: [[['lifecycle-comp'], null]], factory: function LifecycleComp_Factory() { return new LifecycleComp(); }, template: function LifecycleComp_Template(ctx: IDENT, cm: IDENT) {}, inputs: {nameMin: 'name'}, @@ -889,7 +893,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: SimpleLayout, - tag: 'simple-layout', + selector: [[['simple-layout'], null]], factory: function SimpleLayout_Factory() { return new SimpleLayout(); }, template: function SimpleLayout_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -997,6 +1001,7 @@ describe('compiler compliance', () => { const ForDirectiveDefinition = ` static ngDirectiveDef = $r3$.ɵdefineDirective({ type: ForOfDirective, + selector: [[[null, 'forOf', ''], null]], factory: function ForOfDirective_Factory() { return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); }, @@ -1010,7 +1015,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -1088,7 +1093,7 @@ describe('compiler compliance', () => { … static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { if (cm) { @@ -1141,4 +1146,4 @@ describe('compiler compliance', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index dee96afb82..2c20602679 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -14,7 +14,7 @@ import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_facto import {assertComponentType, assertNotNull} from './assert'; import {queueInitHooks, queueLifecycleHooks} from './hooks'; -import {CLEAN_PROMISE, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, enterView, getRootView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderComponentOrTemplate} from './instructions'; +import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings} from './instructions'; import {ComponentDef, ComponentType} from './interfaces/definition'; import {LElementNode, TNodeFlags} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; @@ -123,7 +123,9 @@ export function renderComponent( const componentDef = (componentType as ComponentType).ngComponentDef as ComponentDef; if (componentDef.type != componentType) componentDef.type = componentType; let component: T; - const hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag); + // TODO: Replace when flattening CssSelector type + const componentTag = componentDef.selector ![0] ![0] ![0]; + const hostNode = locateHostElement(rendererFactory, opts.host || componentTag); const rootContext: RootContext = { // Incomplete initialization due to circular reference. component: null !, @@ -131,28 +133,32 @@ export function renderComponent( clean: CLEAN_PROMISE, }; const rootView = createLView( - -1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView(), null, - rootContext, componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); + -1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView(null), + null, rootContext, componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); const oldView = enterView(rootView, null !); - let elementNode: LElementNode; try { + if (rendererFactory.begin) rendererFactory.begin(); + // Create element node at index 0 in data array - elementNode = hostElement(hostNode, componentDef); + elementNode = hostElement(componentTag, hostNode, componentDef); + // Create directive instance with factory() and store at index 0 in directives array component = rootContext.component = baseDirectiveCreate(0, componentDef.factory(), componentDef) as T; - initChangeDetectorIfExisting(elementNode.nodeInjector, component); + initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); + + opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef)); + + executeInitAndContentHooks(); + setHostBindings(ROOT_DIRECTIVE_INDICES); + detectChangesInternal(elementNode.data as LView, elementNode, componentDef, component); } finally { - // We must not use leaveView here because it will set creationMode to false too early, - // causing init-only hooks not to run. The detectChanges call below will execute - // leaveView at the appropriate time in the lifecycle. - enterView(oldView, null); + leaveView(oldView); + if (rendererFactory.end) rendererFactory.end(); } - opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef)); - renderComponentOrTemplate(elementNode, rootView, component); return component; } diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 358411c703..354ebfd1c9 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -16,7 +16,8 @@ import {Type} from '../type'; import {resolveRendererType2} from '../view/util'; import {diPublic} from './di'; -import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, PipeDef} from './interfaces/definition'; +import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, DirectiveDefListOrFactory, PipeDef} from './interfaces/definition'; +import {CssSelector} from './interfaces/projection'; @@ -41,6 +42,9 @@ export function defineComponent(componentDefinition: { */ type: Type; + /** The selector that will be used to match nodes to this component. */ + selector: CssSelector; + /** * Factory method used to create an instance of directive. */ @@ -90,11 +94,6 @@ export function defineComponent(componentDefinition: { */ exportAs?: string; - /** - * HTML tag name to use in place where this component should be instantiated. - */ - tag: string; - /** * Template function use for rendering DOM. * @@ -147,13 +146,20 @@ export function defineComponent(componentDefinition: { * Defines the set of injectable objects that are visible to its view DOM children. */ viewProviders?: Provider[]; + + /** + * Registry of directives and components that may be found in this component's view. + * + * The property is either an array of `DirectiveDef`s or a function which returns the array of + * `DirectiveDef`s. The function is necessary to be able to support forward declarations. + */ + directiveDefs?: DirectiveDefListOrFactory | null; }): ComponentDef { const type = componentDefinition.type; const def = >{ type: type, diPublic: null, factory: componentDefinition.factory, - tag: componentDefinition.tag || null !, template: componentDefinition.template || null !, hostBindings: componentDefinition.hostBindings || null, attributes: componentDefinition.attributes || null, @@ -168,7 +174,9 @@ export function defineComponent(componentDefinition: { afterViewInit: type.prototype.ngAfterViewInit || null, afterViewChecked: type.prototype.ngAfterViewChecked || null, onDestroy: type.prototype.ngOnDestroy || null, - onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush + onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, + directiveDefs: componentDefinition.directiveDefs || null, + selector: componentDefinition.selector }; const feature = componentDefinition.features; feature && feature.forEach((fn) => fn(def)); @@ -300,6 +308,9 @@ export const defineDirective = defineComponent as any as(directiveDefinition: */ type: Type; + /** The selector that will be used to match nodes to this directive. */ + selector: CssSelector; + /** * Factory method used to create an instance of directive. */ diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 64e3107903..074ea3200e 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -19,7 +19,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie import {Type} from '../type'; import {assertLessThan, assertNotNull} from './assert'; -import {assertPreviousIsParent, getDirectiveInstance, getPreviousOrParentNode, getRenderer, renderEmbeddedTemplate} from './instructions'; +import {assertPreviousIsParent, enterView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate} from './instructions'; import {ComponentTemplate, DirectiveDef} from './interfaces/definition'; import {LInjector} from './interfaces/injector'; import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node'; @@ -299,12 +299,10 @@ export function getOrCreateChangeDetectorRef( if (di.changeDetectorRef) return di.changeDetectorRef; const currentNode = di.node; - if (currentNode.data === null) { - // if data is null, this node is a regular element node (not a component) - return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node); - } else if (currentNode.type === LNodeType.Element) { - // if it's an element node with data, it's a component and context will be set later + if (isComponent(currentNode.tNode !)) { return di.changeDetectorRef = createViewRef(currentNode.data as LView, context); + } else if (currentNode.type === LNodeType.Element) { + return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node); } return null !; } @@ -388,7 +386,7 @@ export function getOrCreateInjectable( // The size of the node's directive's list is stored in certain bits of the node's flags, // so exact it with a mask and shift it back such that the bits reflect the real value. const flags = node.tNode !.flags; - const size = flags & TNodeFlags.SIZE_MASK; + const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT; if (size !== 0) { // The start index of the directives list is also part of the node's flags, but there is @@ -574,6 +572,9 @@ class ViewContainerRef implements viewEngine_ViewContainerRef { createEmbeddedView( templateRef: viewEngine_TemplateRef, context?: C|undefined, index?: number|undefined): viewEngine_EmbeddedViewRef { + // set current view to container node's view + enterView(this._node.view, null); + const viewRef = templateRef.createEmbeddedView(context !); this.insert(viewRef, index); return viewRef; diff --git a/packages/core/src/render3/hooks.ts b/packages/core/src/render3/hooks.ts index 6c7adb0151..1c6a873d24 100644 --- a/packages/core/src/render3/hooks.ts +++ b/packages/core/src/render3/hooks.ts @@ -45,7 +45,8 @@ export function queueLifecycleHooks(flags: number, currentView: LView): void { const tView = currentView.tView; if (tView.firstTemplatePass === true) { const start = flags >> TNodeFlags.INDX_SHIFT; - const end = start + (flags & TNodeFlags.SIZE_MASK); + const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT; + const end = start + size; // It's necessary to loop through the directives at elementEnd() (rather than processing in // directiveCreate) so we can preserve the current hook order. Content, view, and destroy diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index cb31edb42b..90635cfbe1 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -18,8 +18,8 @@ import {LView, LViewFlags, LifecycleStage, RootContext, TData, TView} from './in import {LContainerNode, LElementNode, LNode, LNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './interfaces/node'; import {assertNodeType} from './node_assert'; import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode} from './node_manipulation'; -import {matchingSelectorIndex} from './node_selector_matcher'; -import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition'; +import {isNodeMatchingSelector, matchingSelectorIndex} from './node_selector_matcher'; +import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType} from './interfaces/definition'; import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; import {isDifferent, stringify} from './util'; import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks'; @@ -48,7 +48,7 @@ export type Sanitizer = (value: any) => string; * * Saved here to avoid re-instantiating an array on every change detection run. */ -const rootDirectiveIndices = [0, 0]; +export const _ROOT_DIRECTIVE_INDICES = [0, 0]; /** @@ -243,7 +243,7 @@ function refreshDirectives() { } /** Sets the host bindings for the current view. */ -function setHostBindings(bindings: number[] | null): void { +export function setHostBindings(bindings: number[] | null): void { if (bindings != null) { const defs = currentView.tView.directives !; for (let i = 0; i < bindings.length; i += 2) { @@ -263,7 +263,7 @@ function refreshChildComponents(components: number[] | null): void { } } -function executeInitAndContentHooks(): void { +export function executeInitAndContentHooks(): void { if (!checkNoChangesMode) { const tView = currentView.tView; executeInitHooks(currentView, tView, creationMode); @@ -395,21 +395,25 @@ function resetApplicationState() { /** * - * @param host Existing node to render into. + * @param hostNode Existing node to render into. * @param template Template function with the instructions. * @param context to pass into the template. + * @param providedRendererFactory renderer factory to use + * @param host The host element node to use + * @param directiveRegistry Any directive defs that should be used to match nodes to directives */ export function renderTemplate( hostNode: RElement, template: ComponentTemplate, context: T, - providedRendererFactory: RendererFactory3, host: LElementNode | null): LElementNode { + providedRendererFactory: RendererFactory3, host: LElementNode | null, + directiveRegistry: DirectiveDefListOrFactory | null = null): LElementNode { if (host == null) { resetApplicationState(); rendererFactory = providedRendererFactory; host = createLNode( null, LNodeType.Element, hostNode, createLView( - -1, providedRendererFactory.createRenderer(null, null), getOrCreateTView(template), - null, {}, LViewFlags.CheckAlways)); + -1, providedRendererFactory.createRenderer(null, null), + getOrCreateTView(template, directiveRegistry), null, {}, LViewFlags.CheckAlways)); } const hostView = host.data !; ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.'); @@ -427,8 +431,9 @@ export function renderEmbeddedTemplate( previousOrParentNode = null !; let cm: boolean = false; if (viewNode == null) { - const view = - createLView(-1, renderer, createTView(), template, context, LViewFlags.CheckAlways); + const view = createLView( + -1, renderer, createTView(currentView.tView.directiveRegistry), template, context, + LViewFlags.CheckAlways); viewNode = createLNode(null, LNodeType.View, null, view); cm = true; } @@ -460,7 +465,7 @@ export function renderComponentOrTemplate( // Element was stored at 0 in data and directive was stored at 0 in directives // in renderComponent() - setHostBindings(rootDirectiveIndices); + setHostBindings(_ROOT_DIRECTIVE_INDICES); componentRefresh(0, 0); } } finally { @@ -479,9 +484,8 @@ export function renderComponentOrTemplate( * Create DOM element. The instruction must later be followed by `elementEnd()` call. * * @param index Index of the element in the data array - * @param nameOrComponentType Name of the DOM Node or `ComponentType` to create. + * @param name Name of the DOM Node * @param attrs Statically bound set of attributes to be written into the DOM element on creation. - * @param directiveTypes A set of directives declared on this element. * @param localRefs A set of local reference bindings on the element. * * Attributes and localRefs are passed as an array of strings where elements with an even index @@ -489,77 +493,71 @@ export function renderComponentOrTemplate( * ['id', 'warning5', 'class', 'alert'] */ export function elementStart( - index: number, nameOrComponentType?: string | ComponentType, attrs?: string[] | null, - directiveTypes?: DirectiveType[] | null, localRefs?: string[] | null): RElement { + index: number, name?: string, attrs?: string[] | null, localRefs?: string[] | null): RElement { let node: LElementNode; let native: RElement; - if (nameOrComponentType == null) { + if (name == null) { // native node retrieval - used for exporting elements as tpl local variables (
) const node = data[index] !; native = node && (node as LElementNode).native; } else { ngDevMode && assertNull(currentView.bindingStartIndex, 'elements should be created before any bindings'); - const isHostElement = typeof nameOrComponentType !== 'string'; - let hostComponentDef: ComponentDef|null = null; - let name = nameOrComponentType as string; + native = renderer.createElement(name); + node = createLNode(index, LNodeType.Element, native !, null); - let directiveIndex = getNextDirectiveIndex(index); + if (attrs) setUpAttributes(native, attrs); + appendChild(node.parent !, native, currentView); - if (isHostElement) { - hostComponentDef = firstTemplatePass ? - (nameOrComponentType as ComponentType).ngComponentDef : - currentView.tView.directives ![directiveIndex] as ComponentDef; + if (firstTemplatePass) { + const tNode = createTNode(name, attrs || null, null, null); + cacheMatchingDirectivesForNode(tNode); - name = hostComponentDef !.tag; - } - - if (name === null) { - // TODO: future support for nameless components. - throw 'for now name is required'; - } else { - native = renderer.createElement(name); - - let componentView: LView|null = null; - if (isHostElement) { - const tView = getOrCreateTView(hostComponentDef !.template); - const hostView = createLView( - -1, rendererFactory.createRenderer(native, hostComponentDef !.rendererType), tView, - null, null, hostComponentDef !.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); - componentView = addToViewTree(hostView); - } - - // Only component views should be added to the view tree directly. Embedded views are - // accessed through their containers because they may be removed / re-added later. - node = createLNode(index, LNodeType.Element, native, componentView); - - if (node.tNode == null) { - const localNames: (string | number)[]|null = findMatchingLocalNames( - hostComponentDef, localRefs, isHostElement ? directiveIndex : -1, ''); - ngDevMode && assertDataInRange(index - 1); - node.tNode = tData[index] = createTNode(name, attrs || null, null, localNames); - } - - if (attrs) setUpAttributes(native, attrs); - appendChild(node.parent !, native, currentView); - - if (hostComponentDef) { - // TODO(mhevery): This assumes that the directives come in correct order, which - // is not guaranteed. Must be refactored to take it into account. - const instance = hostComponentDef.factory(); - directiveCreate(directiveIndex, index, instance, hostComponentDef, null); - initChangeDetectorIfExisting(node.nodeInjector, instance); - queueComponentIndexForCheck(directiveIndex, index); - directiveIndex++; - } - hack_declareDirectives(directiveIndex, index, directiveTypes, localRefs); + ngDevMode && assertDataInRange(index - 1); + node.tNode = tData[index] = tNode; + + if (!isComponent(tNode)) { + tNode.localNames = findMatchingLocalNames(null, localRefs, -1, ''); + } } + hack_declareDirectives(index, localRefs); } return native; } +function cacheMatchingDirectivesForNode(tNode: TNode): void { + const registry = currentView.tView.directiveRegistry; + const startIndex = directives ? directives.length : 0; + + if (registry) { + let componentFlag = 0; + let size = 0; + + for (let i = 0; i < registry.length; i++) { + const def = registry[i]; + if (isNodeMatchingSelector(tNode, def.selector !)) { + if ((def as ComponentDef).template) { + if (componentFlag) throwMultipleComponentError(tNode); + componentFlag |= TNodeFlags.Component; + } + (currentView.tView.directives || (currentView.tView.directives = [])).push(def); + size++; + } + } + if (size > 0) buildTNodeFlags(tNode, startIndex, size, componentFlag); + } +} + +function buildTNodeFlags(tNode: TNode, index: number, size: number, component: number): void { + tNode.flags = (index << TNodeFlags.INDX_SHIFT) | (size << TNodeFlags.SIZE_SHIFT) | component; +} + +function throwMultipleComponentError(tNode: TNode): never { + throw new Error(`Multiple components match node with tagname ${tNode.tagName}`); +} + /** Stores index of component's host element so it will be queued for view refresh during CD. */ function queueComponentIndexForCheck(dirIndex: number, elIndex: number): void { if (firstTemplatePass) { @@ -576,30 +574,35 @@ function queueHostBindingForCheck(dirIndex: number, elIndex: number): void { } /** Sets the context for a ChangeDetectorRef to the given instance. */ -export function initChangeDetectorIfExisting(injector: LInjector | null, instance: any): void { +export function initChangeDetectorIfExisting( + injector: LInjector | null, instance: any, view: LView): void { if (injector && injector.changeDetectorRef != null) { - (injector.changeDetectorRef as ViewRef)._setComponentContext(instance); + (injector.changeDetectorRef as ViewRef)._setComponentContext(view, instance); } } +export function isComponent(tNode: TNode): boolean { + return (tNode.flags & TNodeFlags.Component) === TNodeFlags.Component; +} + /** * This function instantiates the given directives. It is a hack since it assumes the directives * come in the correct order for DI. */ -function hack_declareDirectives( - index: number, elementIndex: number, directiveTypes: DirectiveType[] | null | undefined, - localRefs: string[] | null | undefined, ) { - if (directiveTypes) { - const defs = currentView.tView.directives !; +function hack_declareDirectives(elementIndex: number, localRefs: string[] | null | undefined) { + const size = (previousOrParentNode.tNode !.flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT; + + if (size > 0) { + let startIndex = previousOrParentNode.tNode !.flags >> TNodeFlags.INDX_SHIFT; + const endIndex = startIndex + size; + const tDirectives = currentView.tView.directives !; // TODO(mhevery): This assumes that the directives come in correct order, which // is not guaranteed. Must be refactored to take it into account. - for (let i = 0; i < directiveTypes.length; i++) { - const directiveType = directiveTypes[i]; - const directiveDef = - firstTemplatePass ? directiveType.ngDirectiveDef : defs[index] as DirectiveDef; - directiveCreate(index, elementIndex, directiveDef.factory(), directiveDef, localRefs); - index++; + for (let i = startIndex; i < endIndex; i++) { + const def = tDirectives[i] as DirectiveDef; + directiveCreate(elementIndex, def.factory(), def, localRefs); + startIndex++; } } } @@ -633,12 +636,13 @@ function findMatchingLocalNames( * @param template The template from which to get static data * @returns TView */ -function getOrCreateTView(template: ComponentTemplate): TView { - return template.ngPrivateData || (template.ngPrivateData = createTView() as never); +function getOrCreateTView( + template: ComponentTemplate, defs: DirectiveDefListOrFactory | null): TView { + return template.ngPrivateData || (template.ngPrivateData = createTView(defs) as never); } /** Creates a TView instance */ -export function createTView(): TView { +export function createTView(defs: DirectiveDefListOrFactory | null): TView { return { data: [], directives: null, @@ -652,7 +656,8 @@ export function createTView(): TView { destroyHooks: null, pipeDestroyHooks: null, hostBindings: null, - components: null + components: null, + directiveRegistry: typeof defs === 'function' ? defs() : defs }; } @@ -708,13 +713,22 @@ export function locateHostElement( * * @returns LElementNode created */ -export function hostElement(rNode: RElement | null, def: ComponentDef): LElementNode { +export function hostElement( + tag: string, rNode: RElement | null, def: ComponentDef): LElementNode { resetApplicationState(); const node = createLNode( - 0, LNodeType.Element, rNode, createLView( - -1, renderer, getOrCreateTView(def.template), null, null, - def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways)); - if (firstTemplatePass) node.tNode = createTNode(def.tag, null, null, null); + 0, LNodeType.Element, rNode, + createLView( + -1, renderer, getOrCreateTView(def.template, def.directiveDefs), null, null, + def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways)); + + if (firstTemplatePass) { + node.tNode = createTNode(tag as string, null, null, null); + // Root directive is stored at index 0, size 1 + buildTNodeFlags(node.tNode, 0, 1, TNodeFlags.Component); + currentView.tView.directives = [def]; + } + return node; } @@ -898,7 +912,7 @@ function setInputsForProperty(inputs: PropertyAliasValue, value: any): void { */ function generatePropertyAliases( tNodeFlags: TNodeFlags, direction: BindingDirection): PropertyAliases|null { - const size = tNodeFlags & TNodeFlags.SIZE_MASK; + const size = (tNodeFlags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT; let propStore: PropertyAliases|null = null; if (size > 0) { @@ -1101,20 +1115,25 @@ export function textBinding(index: number, value: T | NO_CHANGE): void { * NOTE: directives can be created in order other than the index order. They can also * be retrieved before they are created in which case the value will be null. * - * @param index Index to save the directive in the directives array * @param elementIndex Index of the host element in the data array * @param directive The directive instance. * @param directiveDef DirectiveDef object which contains information about the template. * @param localRefs Names under which a query can retrieve the directive instance */ export function directiveCreate( - index: number, elementIndex: number, directive: T, directiveDef: DirectiveDef, + elementIndex: number, directive: T, directiveDef: DirectiveDef| ComponentDef, localRefs?: string[] | null): T { + const index = directives ? directives.length : 0; const instance = baseDirectiveCreate(index, directive, directiveDef); ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode'); const tNode: TNode|null = previousOrParentNode.tNode !; + const isComponent = (directiveDef as ComponentDef).template; + if (isComponent) { + addComponentLogic(index, elementIndex, directive, directiveDef as ComponentDef); + } + if (firstTemplatePass) { // Init hooks are queued now so ngOnInit is called in host components before // any projected components. @@ -1123,7 +1142,8 @@ export function directiveCreate( if (directiveDef.hostBindings) queueHostBindingForCheck(index, elementIndex); if (localRefs) { - const localNames = findMatchingLocalNames(directiveDef, localRefs, index); + const localNames = + findMatchingLocalNames(directiveDef, localRefs, index, isComponent ? '' : undefined); tNode.localNames = localNames && tNode.localNames ? tNode.localNames.concat(localNames) : localNames; } @@ -1136,6 +1156,26 @@ export function directiveCreate( return instance; } +function addComponentLogic( + index: number, elementIndex: number, instance: T, def: ComponentDef): void { + const tView = getOrCreateTView(def.template, def.directiveDefs); + + // Only component views should be added to the view tree directly. Embedded views are + // accessed through their containers because they may be removed / re-added later. + const hostView = addToViewTree(createLView( + -1, rendererFactory.createRenderer(previousOrParentNode.native as RElement, def.rendererType), + tView, null, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways)); + + (previousOrParentNode.data as any) = hostView; + (hostView.node as any) = previousOrParentNode; + + initChangeDetectorIfExisting(previousOrParentNode.nodeInjector, instance, hostView); + + if (firstTemplatePass) { + queueComponentIndexForCheck(index, elementIndex); + } +} + /** * A lighter version of directiveCreate() that is used for the root component * @@ -1143,7 +1183,7 @@ export function directiveCreate( * current Angular. Example: local refs and inputs on root component. */ export function baseDirectiveCreate( - index: number, directive: T, directiveDef: DirectiveDef): T { + index: number, directive: T, directiveDef: DirectiveDef| ComponentDef): T { ngDevMode && assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings'); ngDevMode && assertPreviousIsParent(); @@ -1156,17 +1196,6 @@ export function baseDirectiveCreate( ngDevMode && assertDataNext(index, directives); directives[index] = directive; - if (firstTemplatePass) { - const defs = currentView.tView.directives || (currentView.tView.directives = []); - - ngDevMode && assertDataNext(index, defs); - defs[index] = (directiveDef); - - const flags = previousOrParentNode.tNode !.flags; - previousOrParentNode.tNode !.flags = - (flags & TNodeFlags.SIZE_MASK) === 0 ? (index << TNodeFlags.INDX_SHIFT) | 1 : flags + 1; - } - const diPublic = directiveDef !.diPublic; if (diPublic) { diPublic(directiveDef !); @@ -1253,8 +1282,8 @@ function generateInitialInputs( * @param localRefs A set of local reference bindings on the element. */ export function container( - index: number, directiveTypes?: DirectiveType[], template?: ComponentTemplate, - tagName?: string, attrs?: string[], localRefs?: string[] | null): void { + index: number, template?: ComponentTemplate, tagName?: string, attrs?: string[], + localRefs?: string[] | null): void { ngDevMode && assertNull( currentView.bindingStartIndex, 'container nodes should be created before any bindings'); @@ -1285,7 +1314,9 @@ export function container( // Containers are added to the current view tree instead of their embedded views // because views can be removed and re-inserted. addToViewTree(node.data); - hack_declareDirectives(getNextDirectiveIndex(index), index, directiveTypes, localRefs); + + if (firstTemplatePass) cacheMatchingDirectivesForNode(node.tNode); + hack_declareDirectives(index, localRefs); isParent = false; ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container); @@ -1298,12 +1329,6 @@ export function container( } } -/** Retrieves the next directive index to write */ -function getNextDirectiveIndex(index: number): number { - return firstTemplatePass ? (directives ? directives.length : 0) : - (tData[index] as TNode).flags >> TNodeFlags.INDX_SHIFT; -} - /** * Sets a container up to receive views. * @@ -1421,7 +1446,6 @@ export function embeddedViewStart(viewBlockId: number): boolean { enterView(newView, createLNode(null, LNodeType.View, null, newView)); } - return !existingViewNode; } @@ -1441,7 +1465,7 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV ngDevMode && assertNodeType(parent, LNodeType.Container); const tContainer = (parent !.tNode as TContainerNode).data; if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) { - tContainer[viewIndex] = createTView(); + tContainer[viewIndex] = createTView(currentView.tView.directiveRegistry); } return tContainer[viewIndex]; } @@ -1487,9 +1511,10 @@ export function componentRefresh(directiveIndex: number, elementIndex: number // Only attached CheckAlways components or attached, dirty OnPush components should be checked if (viewAttached(hostView) && hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) { ngDevMode && assertDataInRange(directiveIndex, directives !); - const template = (currentView.tView.directives ![directiveIndex] as ComponentDef).template; + const def = currentView.tView.directives ![directiveIndex] as ComponentDef; + detectChangesInternal( - hostView, element, template, getDirectiveInstance(directives ![directiveIndex])); + hostView, element, def, getDirectiveInstance(directives ![directiveIndex])); } } @@ -1795,8 +1820,8 @@ export function detectChanges(component: T): void { const hostNode = _getComponentHostLElementNode(component); ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView'); const componentIndex = hostNode.tNode !.flags >> TNodeFlags.INDX_SHIFT; - const template = (hostNode.view.tView.directives ![componentIndex] as ComponentDef).template; - detectChangesInternal(hostNode.data as LView, hostNode, template, component); + const def = hostNode.view.tView.directives ![componentIndex] as ComponentDef; + detectChangesInternal(hostNode.data as LView, hostNode, def, component); } @@ -1833,8 +1858,10 @@ function throwErrorIfNoChangesMode(oldValue: any, currValue: any): never|void { /** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */ export function detectChangesInternal( - hostView: LView, hostNode: LElementNode, template: ComponentTemplate, component: T) { + hostView: LView, hostNode: LElementNode, def: ComponentDef, component: T) { const oldView = enterView(hostView, hostNode); + const template = def.template; + try { template(component, creationMode); refreshDynamicChildren(); @@ -2150,3 +2177,4 @@ export function _getComponentHostLElementNode(component: T): LElementNode { } export const CLEAN_PROMISE = _CLEAN_PROMISE; +export const ROOT_DIRECTIVE_INDICES = _ROOT_DIRECTIVE_INDICES; diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 0ddb8db6d9..3b81790aee 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -12,7 +12,7 @@ import {Provider} from '../../core'; import {RendererType2} from '../../render/api'; import {Type} from '../../type'; import {resolveRendererType2} from '../../view/util'; - +import {CssSelector} from './projection'; /** @@ -61,6 +61,9 @@ export interface DirectiveDef { /** Function that makes a directive public to the DI system. */ diPublic: ((def: DirectiveDef) => void)|null; + /** The selector that will be used to match nodes to this directive. */ + selector: CssSelector; + /** * A dictionary mapping the inputs' minified property names to their public API names, which * are their aliases if any, or their original unminified property names @@ -122,13 +125,6 @@ export interface DirectiveDef { * See: {@link defineComponent} */ export interface ComponentDef extends DirectiveDef { - /** - * The tag name which should be used by the component. - * - * NOTE: only used with component directives. - */ - readonly tag: string; - /** * The View template of the component. * @@ -157,6 +153,14 @@ export interface ComponentDef extends DirectiveDef { * children only. */ readonly viewProviders?: Provider[]; + + /** + * Registry of directives and components that may be found in this view. + * + * The property is either an array of `DirectiveDef`s or a function which returns the array of + * `DirectiveDef`s. The function is necessary to be able to support forward declarations. + */ + directiveDefs: DirectiveDefListOrFactory|null; } /** @@ -195,6 +199,15 @@ export interface PipeDef { export type DirectiveDefFeature = (directiveDef: DirectiveDef) => void; export type ComponentDefFeature = (componentDef: ComponentDef) => void; +/** + * Type used for directiveDefs on component definition. + * + * The function is necessary to be able to support forward declarations. + */ +export type DirectiveDefListOrFactory = (() => DirectiveDefList) | DirectiveDefList; + +export type DirectiveDefList = (DirectiveDef| ComponentDef)[]; + // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. export const unusedValueExportToPlacateAjd = 1; diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index 95bce4163d..338e984dcc 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -32,7 +32,19 @@ export const enum LNodeType { * on how to map a particular set of bits to the node's first directive index * (with INDX_SHIFT) or the node's directive count (with SIZE_MASK) */ -export const enum TNodeFlags {INDX_SHIFT = 12, SIZE_MASK = 0b00000000000000000000111111111111} +export const enum TNodeFlags { + /** Whether or not this node is a component */ + Component = 0b001, + + /** How far to shift the flags to get the first directive index on this node */ + INDX_SHIFT = 13, + + /** How far to shift the flags to get the number of directives on this node */ + SIZE_SHIFT = 1, + + /** Mask to get the number of directives on this node */ + SIZE_MASK = 0b00000000000000000001111111111110 +} /** * LNode is an internal data structure which is used for the incremental DOM algorithm. diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 3f57c34b8d..6166d53918 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -7,7 +7,7 @@ */ import {LContainer} from './container'; -import {ComponentDef, ComponentTemplate, DirectiveDef, PipeDef} from './definition'; +import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef} from './definition'; import {LElementNode, LViewNode, TNode} from './node'; import {LQueries} from './query'; import {Renderer3} from './renderer'; @@ -219,19 +219,31 @@ export interface LViewOrLContainer { * Stored on the template function as ngPrivateData. */ export interface TView { + /** Whether or not this template has been processed. */ + firstTemplatePass: boolean; + /** Static data equivalent of LView.data[]. Contains TNodes. */ data: TData; /** - * Directive and component defs for this view + * Directive and component defs that have already been matched to nodes on + * this view. * * Defs are stored at the same index in TView.directives[] as their instances * are stored in LView.directives[]. This simplifies lookup in DI. */ - directives: (ComponentDef|DirectiveDef)[]|null; + directives: DirectiveDefList|null; - /** Whether or not this template has been processed. */ - firstTemplatePass: boolean; + /** + * Full registry of directives and components that may be found in this view. + * + * The property is either an array of `DirectiveDef`s or a function which returns the array of + * `DirectiveDef`s. The function is necessary to be able to support forward declarations. + * + * It's necessary to keep a copy of the full def list on the TView so it's possible + * to render template functions without a host component. + */ + directiveRegistry: DirectiveDefList|null; /** * Array of ngOnInit and ngDoCheck hooks that should be executed for this view in diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 9052c9a394..413c585cd0 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -196,8 +196,8 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null { function geIdxOfMatchingDirective(node: LNode, type: Type): number|null { const defs = node.view.tView.directives !; const flags = node.tNode !.flags; - for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + (flags & TNodeFlags.SIZE_MASK); i < ii; - i++) { + const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT; + for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + size; i < ii; i++) { const def = defs[i] as DirectiveDef; if (def.diPublic && def.type === type) { return i; diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index b98a688fa4..44b7682927 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -21,7 +21,10 @@ export class ViewRef implements viewEngine_EmbeddedViewRef { constructor(private _view: LView, context: T|null, ) { this.context = context !; } /** @internal */ - _setComponentContext(context: T) { this.context = context; } + _setComponentContext(view: LView, context: T) { + this._view = view; + this.context = context; + } destroy(): void { notImplemented(); } destroyed: boolean; @@ -223,9 +226,9 @@ export class EmbeddedViewRef extends ViewRef { * @param context The context for this view * @returns The ViewRef */ -export function createViewRef(view: LView, context: T): ViewRef { +export function createViewRef(view: LView | null, context: T): ViewRef { // TODO: add detectChanges back in when implementing ChangeDetectorRef.detectChanges - return addDestroyable(new ViewRef(view, context)); + return addDestroyable(new ViewRef(view !, context)); } /** Interface for destroy logic. Implemented by addDestroyable. */ diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 05fda153fd..ae402bdc49 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -44,6 +44,9 @@ { "name": "QueueAction" }, + { + "name": "ROOT_DIRECTIVE_INDICES" + }, { "name": "SafeSubscriber" }, @@ -107,6 +110,9 @@ { "name": "baseDirectiveCreate" }, + { + "name": "buildTNodeFlags" + }, { "name": "callHooks" }, @@ -239,9 +245,6 @@ { "name": "renderComponent" }, - { - "name": "renderComponentOrTemplate" - }, { "name": "renderEmbeddedTemplate" }, @@ -251,9 +254,6 @@ { "name": "resolveRendererType2" }, - { - "name": "rootDirectiveIndices" - }, { "name": "runIfPresent" }, diff --git a/packages/core/test/render3/basic_perf.ts b/packages/core/test/render3/basic_perf.ts index 94e3b77f1b..31659ba6ea 100644 --- a/packages/core/test/render3/basic_perf.ts +++ b/packages/core/test/render3/basic_perf.ts @@ -34,7 +34,7 @@ describe('iv perf test', () => { class Component { static ngComponentDef = defineComponent({ type: Component, - tag: 'div', + selector: [[['div'], null]], template: function Template(ctx: any, cm: any) { if (cm) { container(0); diff --git a/packages/core/test/render3/change_detection_spec.ts b/packages/core/test/render3/change_detection_spec.ts index 15b275262c..d5da985842 100644 --- a/packages/core/test/render3/change_detection_spec.ts +++ b/packages/core/test/render3/change_detection_spec.ts @@ -13,7 +13,7 @@ import {getRenderedText, whenRendered} from '../../src/render3/component'; import {LifecycleHooksFeature, defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, text, textBinding, tick} from '../../src/render3/instructions'; -import {containerEl, renderComponent, requestAnimationFrame} from './render_util'; +import {containerEl, createComponent, renderComponent, requestAnimationFrame} from './render_util'; describe('change detection', () => { @@ -25,7 +25,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: () => new MyComponent(), template: (ctx: MyComponent, cm: boolean) => { if (cm) { @@ -96,7 +96,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: () => comp = new MyComponent(), /** * {{ doCheckCount }} - {{ name }} @@ -123,16 +123,17 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(), /** */ template: (ctx: MyApp, cm: boolean) => { if (cm) { - elementStart(0, MyComponent); + elementStart(0, 'my-comp'); elementEnd(); } elementProperty(0, 'name', bind(ctx.name)); - } + }, + directiveDefs: () => [MyComponent.ngComponentDef] }); } @@ -192,28 +193,18 @@ describe('change detection', () => { }); it('should not check OnPush components in update mode when parent events occur', () => { - class ButtonParent { - noop() {} + function noop() {} + + const ButtonParent = createComponent('button-parent', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'my-comp'); + elementEnd(); + elementStart(1, 'button', ['id', 'parent']); + { listener('click', () => noop()); } + elementEnd(); + } + }, [MyComponent.ngComponentDef]); - static ngComponentDef = defineComponent({ - type: ButtonParent, - tag: 'button-parent', - factory: () => new ButtonParent(), - /** - * - * - */ - template: (ctx: ButtonParent, cm: boolean) => { - if (cm) { - elementStart(0, MyComponent); - elementEnd(); - elementStart(1, 'button', ['id', 'parent']); - { listener('click', () => ctx.noop()); } - elementEnd(); - } - } - }); - } const buttonParent = renderComponent(ButtonParent); expect(getRenderedText(buttonParent)).toEqual('1 - Nancy'); @@ -232,35 +223,28 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: ButtonParent, - tag: 'button-parent', + selector: [[['button-parent'], null]], factory: () => parent = new ButtonParent(), /** {{ doCheckCount }} - */ template: (ctx: ButtonParent, cm: boolean) => { if (cm) { text(0); - elementStart(1, MyComponent); + elementStart(1, 'my-comp'); elementEnd(); } textBinding(0, interpolation1('', ctx.doCheckCount, ' - ')); }, + directiveDefs: () => [MyComponent.ngComponentDef], changeDetection: ChangeDetectionStrategy.OnPush }); } - class MyButtonApp { - static ngComponentDef = defineComponent({ - type: MyButtonApp, - tag: 'my-button-app', - factory: () => new MyButtonApp(), - /** */ - template: (ctx: MyButtonApp, cm: boolean) => { - if (cm) { - elementStart(0, ButtonParent); - elementEnd(); - } - } - }); - } + const MyButtonApp = createComponent('my-button-app', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'button-parent'); + elementEnd(); + } + }, [ButtonParent.ngComponentDef]); const myButtonApp = renderComponent(MyButtonApp); expect(parent !.doCheckCount).toEqual(1); @@ -298,7 +282,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: () => myComp = new MyComp(injectChangeDetectorRef()), /** {{ name }} */ template: (ctx: MyComp, cm: boolean) => { @@ -320,7 +304,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: ParentComp, - tag: 'parent-comp', + selector: [[['parent-comp'], null]], factory: () => new ParentComp(injectChangeDetectorRef()), /** * {{ doCheckCount}} - @@ -329,19 +313,23 @@ describe('change detection', () => { template: (ctx: ParentComp, cm: boolean) => { if (cm) { text(0); - elementStart(1, MyComp); + elementStart(1, 'my-comp'); elementEnd(); } textBinding(0, interpolation1('', ctx.doCheckCount, ' - ')); - } + }, + directiveDefs: () => [MyComp.ngComponentDef] }); } class Dir { constructor(public cdr: ChangeDetectorRef) {} - static ngDirectiveDef = - defineDirective({type: Dir, factory: () => dir = new Dir(injectChangeDetectorRef())}); + static ngDirectiveDef = defineDirective({ + type: Dir, + selector: [[['', 'dir', ''], null]], + factory: () => dir = new Dir(injectChangeDetectorRef()) + }); } @@ -399,20 +387,13 @@ describe('change detection', () => { }); it('should check component view when called by directive on component node', () => { - class MyApp { - static ngComponentDef = defineComponent({ - type: MyApp, - tag: 'my-app', - factory: () => new MyApp(), - /** */ - template: (ctx: MyApp, cm: boolean) => { - if (cm) { - elementStart(0, MyComp, ['dir', ''], [Dir]); - elementEnd(); - } - } - }); - } + /** */ + const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'my-comp', ['dir', '']); + elementEnd(); + } + }, [MyComp.ngComponentDef, Dir.ngDirectiveDef]); const app = renderComponent(MyApp); expect(getRenderedText(app)).toEqual('Nancy'); @@ -423,32 +404,25 @@ describe('change detection', () => { }); it('should check host component when called by directive on element node', () => { - class MyApp { - name = 'Frank'; - - static ngComponentDef = defineComponent({ - type: MyApp, - tag: 'my-app', - factory: () => new MyApp(), - /** - * {{ name }} - *
- */ - template: (ctx: MyApp, cm: boolean) => { - if (cm) { - text(0); - elementStart(1, 'div', ['dir', ''], [Dir]); - elementEnd(); - } - textBinding(1, bind(ctx.name)); - } - }); - } + /** + * {{ name }} + *
+ */ + const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) { + if (cm) { + text(0); + elementStart(1, 'div', ['dir', '']); + elementEnd(); + } + textBinding(1, bind(ctx.value)); + }, [Dir.ngDirectiveDef]); const app = renderComponent(MyApp); + app.value = 'Frank'; + tick(app); expect(getRenderedText(app)).toEqual('Frank'); - app.name = 'Joe'; + app.value = 'Joe'; dir !.cdr.detectChanges(); expect(getRenderedText(app)).toEqual('Joe'); }); @@ -462,7 +436,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /** * {{ name}} @@ -480,14 +454,15 @@ describe('change detection', () => { { if (ctx.showing) { if (embeddedViewStart(0)) { - elementStart(0, 'div', ['dir', ''], [Dir]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } } embeddedViewEnd(); } containerRefreshEnd(); - } + }, + directiveDefs: [Dir.ngDirectiveDef] }); } @@ -512,7 +487,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetectChangesComp, - tag: 'detect-changes-comp', + selector: [[['detect-changes-comp'], null]], factory: () => new DetectChangesComp(injectChangeDetectorRef()), /** {{ value }} */ template: (ctx: DetectChangesComp, cm: boolean) => { @@ -541,7 +516,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetectChangesComp, - tag: 'detect-changes-comp', + selector: [[['detect-changes-comp'], null]], factory: () => new DetectChangesComp(injectChangeDetectorRef()), /** {{ doCheckCount }} */ template: (ctx: DetectChangesComp, cm: boolean) => { @@ -567,15 +542,16 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /** */ template: (ctx: MyApp, cm: boolean) => { if (cm) { - elementStart(0, DetachedComp); + elementStart(0, 'detached-comp'); elementEnd(); } - } + }, + directiveDefs: () => [DetachedComp.ngComponentDef] }); } @@ -589,7 +565,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetachedComp, - tag: 'detached-comp', + selector: [[['detached-comp'], null]], factory: () => comp = new DetachedComp(injectChangeDetectorRef()), /** {{ value }} */ template: (ctx: DetachedComp, cm: boolean) => { @@ -685,7 +661,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: OnPushComp, - tag: 'on-push-comp', + selector: [[['on-push-comp'], null]], factory: () => onPushComp = new OnPushComp(injectChangeDetectorRef()), /** {{ value }} */ template: (ctx: OnPushComp, cm: boolean) => { @@ -699,25 +675,18 @@ describe('change detection', () => { }); } - class OnPushApp { - value = 'one'; - - static ngComponentDef = defineComponent({ - type: OnPushApp, - tag: 'on-push-app', - factory: () => new OnPushApp(), - /** */ - template: (ctx: OnPushApp, cm: boolean) => { - if (cm) { - elementStart(0, OnPushComp); - elementEnd(); - } - elementProperty(0, 'value', bind(ctx.value)); - } - }); - } + /** */ + const OnPushApp = createComponent('on-push-app', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'on-push-comp'); + elementEnd(); + } + elementProperty(0, 'value', bind(ctx.value)); + }, [OnPushComp.ngComponentDef]); const app = renderComponent(OnPushApp); + app.value = 'one'; + tick(app); expect(getRenderedText(app)).toEqual('one'); onPushComp !.cdr.detach(); @@ -748,7 +717,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: OnPushComp, - tag: 'on-push-comp', + selector: [[['on-push-comp'], null]], factory: () => comp = new OnPushComp(injectChangeDetectorRef()), /** {{ value }} */ template: (ctx: OnPushComp, cm: boolean) => { @@ -766,7 +735,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: OnPushParent, - tag: 'on-push-parent', + selector: [[['on-push-parent'], null]], factory: () => new OnPushParent(), /** * {{ value }} - @@ -775,11 +744,12 @@ describe('change detection', () => { template: (ctx: OnPushParent, cm: boolean) => { if (cm) { text(0); - elementStart(1, OnPushComp); + elementStart(1, 'on-push-comp'); elementEnd(); } textBinding(0, interpolation1('', ctx.value, ' - ')); }, + directiveDefs: () => [OnPushComp.ngComponentDef], changeDetection: ChangeDetectionStrategy.OnPush }); } @@ -832,7 +802,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: EmbeddedViewParent, - tag: 'embedded-view-parent', + selector: [[['embedded-view-parent'], null]], factory: () => new EmbeddedViewParent(), /** * {{ value }} - @@ -850,7 +820,7 @@ describe('change detection', () => { { if (ctx.showing) { if (embeddedViewStart(0)) { - elementStart(0, OnPushComp); + elementStart(0, 'on-push-comp'); elementEnd(); } embeddedViewEnd(); @@ -858,6 +828,7 @@ describe('change detection', () => { } containerRefreshEnd(); }, + directiveDefs: () => [OnPushComp.ngComponentDef], changeDetection: ChangeDetectionStrategy.OnPush }); } @@ -904,7 +875,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: NoChangesComp, - tag: 'no-changes-comp', + selector: [[['no-changes-comp'], null]], factory: () => comp = new NoChangesComp(injectChangeDetectorRef()), template: (ctx: NoChangesComp, cm: boolean) => { if (cm) { @@ -922,7 +893,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: AppComp, - tag: 'app-comp', + selector: [[['app-comp'], null]], factory: () => new AppComp(injectChangeDetectorRef()), /** * {{ value }} - @@ -931,11 +902,12 @@ describe('change detection', () => { template: (ctx: AppComp, cm: boolean) => { if (cm) { text(0); - elementStart(1, NoChangesComp); + elementStart(1, 'no-changes-comp'); elementEnd(); } textBinding(0, interpolation1('', ctx.value, ' - ')); - } + }, + directiveDefs: () => [NoChangesComp.ngComponentDef] }); } @@ -981,7 +953,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: EmbeddedViewApp, - tag: 'embedded-view-app', + selector: [[['embedded-view-app'], null]], factory: () => new EmbeddedViewApp(injectChangeDetectorRef()), /** * % if (showing) { diff --git a/packages/core/test/render3/common_integration_spec.ts b/packages/core/test/render3/common_integration_spec.ts index d3259e65ea..9960c4ad16 100644 --- a/packages/core/test/render3/common_integration_spec.ts +++ b/packages/core/test/render3/common_integration_spec.ts @@ -23,14 +23,14 @@ describe('@angular/common integration', () => { static ngComponentDef = defineComponent({ type: MyApp, factory: () => new MyApp(), - tag: 'my-app', + selector: [[['my-app'], null]], //
    //
  • {{item}}
  • //
template: (myApp: MyApp, cm: boolean) => { if (cm) { elementStart(0, 'ul'); - { container(1, [NgForOf], liTemplate); } + { container(1, liTemplate, undefined, ['ngForOf', '']); } elementEnd(); } elementProperty(1, 'ngForOf', bind(myApp.items)); @@ -45,7 +45,8 @@ describe('@angular/common integration', () => { } textBinding(1, bind(row.$implicit)); } - } + }, + directiveDefs: () => [NgForOf.ngDirectiveDef] }); } @@ -54,4 +55,4 @@ describe('@angular/common integration', () => { }); // TODO: Test inheritance }); -}); \ No newline at end of file +}); diff --git a/packages/core/test/render3/common_with_def.ts b/packages/core/test/render3/common_with_def.ts index 15d6c4cb0f..a4f6685943 100644 --- a/packages/core/test/render3/common_with_def.ts +++ b/packages/core/test/render3/common_with_def.ts @@ -16,6 +16,7 @@ export const NgForOf: DirectiveType> = NgForOfDef as any; NgForOf.ngDirectiveDef = defineDirective({ type: NgForOfDef, + selector: [[['', 'ngForOf', ''], null]], factory: () => new NgForOfDef( injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)), diff --git a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts index 8bca1859b2..8a280ce71e 100644 --- a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts +++ b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts @@ -28,7 +28,7 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: ChildComponent, - tag: `child`, + selector: [[['child'], null]], factory: () => new ChildComponent(), template: function(ctx: $ChildComponent$, cm: $boolean$) { if (cm) { @@ -47,6 +47,7 @@ describe('components & directives', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: SomeDirective, + selector: [[['', 'some-directive', ''], null]], factory: () => new SomeDirective(), }); // /NORMATIVE @@ -55,7 +56,6 @@ describe('components & directives', () => { // Important: keep arrays outside of function to not create new instances. // NORMATIVE const $e0_attrs$ = ['some-directive', '']; - const $e0_dirs$ = [SomeDirective]; // /NORMATIVE @Component({selector: 'my-component', template: `!`}) @@ -63,11 +63,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: () => new MyComponent(), template: function(ctx: $MyComponent$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, ChildComponent, $e0_attrs$, $e0_dirs$); + $r3$.ɵE(0, 'child', $e0_attrs$); $r3$.ɵe(); $r3$.ɵT(1, '!'); } @@ -76,6 +76,11 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyComponent.ngComponentDef.directiveDefs = + [ChildComponent.ngComponentDef, SomeDirective.ngDirectiveDef]; + // /NON-NORMATIVE + expect(renderComp(MyComponent)).toEqual('child-view!'); expect(log).toEqual(['ChildComponent', 'SomeDirective']); }); @@ -90,6 +95,7 @@ describe('components & directives', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: HostBindingDir, + selector: [[['', 'hostBindingDir', ''], null]], factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { $r3$.ɵp(elIndex, 'id', $r3$.ɵb($r3$.ɵd(dirIndex).dirId)); @@ -99,7 +105,6 @@ describe('components & directives', () => { } const $e0_attrs$ = ['hostBindingDir', '']; - const $e0_dirs$ = [HostBindingDir]; @Component({ selector: 'my-app', @@ -110,17 +115,21 @@ describe('components & directives', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); + $r3$.ɵE(0, 'div', $e0_attrs$); $r3$.ɵe(); } } }); } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`
`); }); @@ -134,6 +143,7 @@ describe('components & directives', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ + selector: [[['', 'hostListenerDir', ''], null]], type: HostListenerDir, factory: function HostListenerDir_Factory() { const $dir$ = new HostListenerDir(); @@ -145,7 +155,6 @@ describe('components & directives', () => { } const $e0_attrs$ = ['hostListenerDir', '']; - const $e0_dirs$ = [HostListenerDir]; @Component({ selector: 'my-app', @@ -156,11 +165,11 @@ describe('components & directives', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'button', $e0_attrs$, $e0_dirs$); + $r3$.ɵE(0, 'button', $e0_attrs$); $r3$.ɵT(1, 'Click'); $r3$.ɵe(); } @@ -168,6 +177,10 @@ describe('components & directives', () => { }); } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [HostListenerDir.ngDirectiveDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(``); }); @@ -179,6 +192,7 @@ describe('components & directives', () => { class HostAttributeDir { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ + selector: [[['', 'hostAttributeDir', ''], null]], type: HostAttributeDir, factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); }, attributes: ['role', 'listbox'] @@ -187,7 +201,6 @@ describe('components & directives', () => { } const $e0_attrs$ = ['hostAttributeDir', '']; - const $e0_dirs$ = [HostAttributeDir]; @Component({ selector: 'my-app', @@ -198,17 +211,21 @@ describe('components & directives', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); + $r3$.ɵE(0, 'div', $e0_attrs$); $r3$.ɵe(); } } }); } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [HostAttributeDir.ngDirectiveDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`
`); }); @@ -222,6 +239,7 @@ describe('components & directives', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: HostBindingDir, + selector: [[['', 'hostBindingDir', ''], null]], factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { $r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵd(dirIndex).label)); @@ -231,7 +249,6 @@ describe('components & directives', () => { } const $e0_attrs$ = ['hostBindingDir', '']; - const $e0_dirs$ = [HostBindingDir]; @Component({ selector: 'my-app', @@ -242,17 +259,21 @@ describe('components & directives', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); + $r3$.ɵE(0, 'div', $e0_attrs$); $r3$.ɵe(); } } }); } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`
`); }); @@ -273,7 +294,7 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return new MyComp(); }, template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { if (cm) { @@ -298,11 +319,11 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyComp); + $r3$.ɵE(0, 'my-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name)); @@ -310,6 +331,10 @@ describe('components & directives', () => { }); } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`some name`); }); @@ -325,6 +350,7 @@ describe('components & directives', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: IfDirective, + selector: [[['', 'if', ''], null]], factory: () => new IfDirective($r3$.ɵinjectTemplateRef()), }); // /NORMATIVE @@ -333,7 +359,6 @@ describe('components & directives', () => { // Important: keep arrays outside of function to not create new instances. // NORMATIVE const $e0_locals$ = ['foo', '']; - const $c1_dirs$ = [IfDirective]; // /NORMATIVE @Component( @@ -343,12 +368,12 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: () => new MyComponent(), template: function(ctx: $MyComponent$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'ul', null, null, $e0_locals$); - $r3$.ɵC(2, $c1_dirs$, C1); + $r3$.ɵE(0, 'ul', null, $e0_locals$); + $r3$.ɵC(2, C1, '', ['if', '']); $r3$.ɵe(); } let $foo$ = $r3$.ɵld(1); @@ -386,7 +411,7 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyArrayComp, - tag: 'my-array-comp', + selector: [[['my-array-comp'], null]], factory: function MyArrayComp_Factory() { return new MyArrayComp(); }, template: function MyArrayComp_Template(ctx: $MyArrayComp$, cm: $boolean$) { if (cm) { @@ -415,11 +440,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyArrayComp); + $r3$.ɵE(0, 'my-array-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'names', cm ? $e0_arr$ : $r3$.ɵNC); @@ -428,6 +453,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`Nancy Bess`); }); @@ -453,11 +482,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyArrayComp); + $r3$.ɵE(0, 'my-array-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$)))); @@ -466,6 +495,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`NANCY Bess`); }); @@ -479,7 +512,7 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return new MyComp(); }, template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { if (cm) { @@ -505,11 +538,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyComp); + $r3$.ɵE(0, 'my-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1)); @@ -518,6 +551,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`3`); }); @@ -541,11 +578,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyArrayComp); + $r3$.ɵE(0, 'my-array-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName))); @@ -554,6 +591,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`Nancy Bess`); }); @@ -581,7 +622,7 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return new MyComp(); }, template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { if (cm) { @@ -642,11 +683,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(c: MyApp, cm: boolean) { if (cm) { - $r3$.ɵE(0, MyComp); + $r3$.ɵE(0, 'my-comp'); $r3$.ɵe(); } $r3$.ɵp( @@ -657,6 +698,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`start-abcde-middle-fghi-end`); }); @@ -676,7 +721,7 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: ObjectComp, - tag: 'object-comp', + selector: [[['object-comp'], null]], factory: function ObjectComp_Factory() { return new ObjectComp(); }, template: function ObjectComp_Template(ctx: $ObjectComp$, cm: $boolean$) { if (cm) { @@ -710,11 +755,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, ObjectComp); + $r3$.ɵE(0, 'object-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name))); @@ -723,6 +768,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [ObjectComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)).toEqual(`

500

slide

`); }); @@ -743,7 +792,7 @@ describe('components & directives', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: NestedComp, - tag: 'nested-comp', + selector: [[['nested-comp'], null]], factory: function NestedComp_Factory() { return new NestedComp(); }, template: function NestedComp_Template(ctx: $NestedComp$, cm: $boolean$) { if (cm) { @@ -786,11 +835,11 @@ describe('components & directives', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, NestedComp); + $r3$.ɵE(0, 'nested-comp'); $r3$.ɵe(); } $r3$.ɵp( @@ -802,6 +851,10 @@ describe('components & directives', () => { // /NORMATIVE } + // NON-NORMATIVE (done by defineNgModule) + MyApp.ngComponentDef.directiveDefs = [NestedComp.ngComponentDef]; + // /NON-NORMATIVE + expect(renderComp(MyApp)) .toEqual(`

slide

0

100

`); }); diff --git a/packages/core/test/render3/compiler_canonical/content_projection_spec.ts b/packages/core/test/render3/compiler_canonical/content_projection_spec.ts index ad2c89ac89..7e06e66608 100644 --- a/packages/core/test/render3/compiler_canonical/content_projection_spec.ts +++ b/packages/core/test/render3/compiler_canonical/content_projection_spec.ts @@ -24,7 +24,7 @@ describe('content projection', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: SimpleComponent, - tag: 'simple', + selector: [[['simple'], null]], factory: () => new SimpleComponent(), template: function(ctx: $SimpleComponent$, cm: $boolean$) { if (cm) { @@ -54,7 +54,7 @@ describe('content projection', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: ComplexComponent, - tag: 'complex', + selector: [[['complex'], null]], factory: () => new ComplexComponent(), template: function(ctx: $ComplexComponent$, cm: $boolean$) { if (cm) { @@ -79,15 +79,16 @@ describe('content projection', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(), template: function(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, SimpleComponent); + $r3$.ɵE(0, 'simple'); $r3$.ɵT(1, 'content'); $r3$.ɵe(); } - } + }, + directiveDefs: () => [SimpleComponent.ngComponentDef] }); } }); diff --git a/packages/core/test/render3/compiler_canonical/elements_spec.ts b/packages/core/test/render3/compiler_canonical/elements_spec.ts index 1a8d66246e..1457764deb 100644 --- a/packages/core/test/render3/compiler_canonical/elements_spec.ts +++ b/packages/core/test/render3/compiler_canonical/elements_spec.ts @@ -32,7 +32,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: () => new MyComponent(), template: function(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -69,7 +69,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: ListenerComp, - tag: 'listener-comp', + selector: [[['listener-comp'], null]], factory: function ListenerComp_Factory() { return new ListenerComp(); }, template: function ListenerComp_Template(ctx: $ListenerComp$, cm: $boolean$) { if (cm) { @@ -101,7 +101,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -131,7 +131,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -161,7 +161,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -195,7 +195,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -243,7 +243,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { @@ -277,7 +277,7 @@ describe('elements', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: StyleComponent, - tag: 'style-comp', + selector: [[['style-comp'], null]], factory: function StyleComponent_Factory() { return new StyleComponent(); }, template: function StyleComponent_Template(ctx: $StyleComponent$, cm: $boolean$) { if (cm) { diff --git a/packages/core/test/render3/compiler_canonical/injection_spec.ts b/packages/core/test/render3/compiler_canonical/injection_spec.ts index d128b669e6..a997a0f764 100644 --- a/packages/core/test/render3/compiler_canonical/injection_spec.ts +++ b/packages/core/test/render3/compiler_canonical/injection_spec.ts @@ -30,7 +30,7 @@ describe('injection', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectChangeDetectorRef()); }, @@ -47,18 +47,20 @@ describe('injection', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, /** */ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyComp); + $r3$.ɵE(0, 'my-comp'); $r3$.ɵe(); } - } + }, + directiveDefs: () => [MyComp.ngComponentDef] }); } + const app = renderComponent(MyApp); // ChangeDetectorRef is the token, ViewRef is historically the constructor expect(toHtml(app)).toEqual('ViewRef'); @@ -75,7 +77,7 @@ describe('injection', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); }, template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { if (cm) { @@ -90,15 +92,16 @@ describe('injection', () => { class MyApp { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, /** */ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, MyComp, e0_attrs); + $r3$.ɵE(0, 'my-comp', e0_attrs); $r3$.ɵe(); } - } + }, + directiveDefs: () => [MyComp.ngComponentDef] }); } const e0_attrs = ['title', 'WORKS']; @@ -139,7 +142,7 @@ describe('injection', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp( $r3$.ɵdirectiveInject(ServiceA), $r3$.ɵdirectiveInject(ServiceB), inject(INJECTOR)); diff --git a/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts b/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts index 5cfb41166e..eb02201d3e 100644 --- a/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts +++ b/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts @@ -41,7 +41,7 @@ describe('lifecycle hooks', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: LifecycleComp, - tag: 'lifecycle-comp', + selector: [[['lifecycle-comp'], null]], factory: function LifecycleComp_Factory() { return new LifecycleComp(); }, template: function LifecycleComp_Template(ctx: $LifecycleComp$, cm: $boolean$) {}, inputs: {nameMin: 'name'}, @@ -64,13 +64,13 @@ describe('lifecycle hooks', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: SimpleLayout, - tag: 'simple-layout', + selector: [[['simple-layout'], null]], factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); }, template: function SimpleLayout_Template(ctx: $SimpleLayout$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, LifecycleComp); + $r3$.ɵE(0, 'lifecycle-comp'); $r3$.ɵe(); - $r3$.ɵE(1, LifecycleComp); + $r3$.ɵE(1, 'lifecycle-comp'); $r3$.ɵe(); } $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1)); @@ -80,6 +80,10 @@ describe('lifecycle hooks', () => { // /NORMATIVE } + // NON-NORMATIVE + SimpleLayout.ngComponentDef.directiveDefs = [LifecycleComp.ngComponentDef]; + // /NON-NORMATIVE + it('should gen hooks with a few simple components', () => { expect(toHtml(renderComponent(SimpleLayout))) .toEqual(``); diff --git a/packages/core/test/render3/compiler_canonical/local_reference_spec.ts b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts index 5e5de98243..6032b236fd 100644 --- a/packages/core/test/render3/compiler_canonical/local_reference_spec.ts +++ b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts @@ -23,11 +23,11 @@ describe('local references', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: () => new MyComponent, template: function(ctx: $MyComponent$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, 'input', null, null, ['user', '']); + $r3$.ɵE(0, 'input', null, ['user', '']); $r3$.ɵe(); $r3$.ɵT(2); } diff --git a/packages/core/test/render3/compiler_canonical/pipes_spec.ts b/packages/core/test/render3/compiler_canonical/pipes_spec.ts index 0c74824475..301d551dbb 100644 --- a/packages/core/test/render3/compiler_canonical/pipes_spec.ts +++ b/packages/core/test/render3/compiler_canonical/pipes_spec.ts @@ -82,7 +82,7 @@ describe('pipes', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { @@ -133,17 +133,13 @@ describe('pipes', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: OneTimeIf, + selector: [[['', 'oneTimeIf', ''], null]], factory: () => new OneTimeIf($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()), inputs: {oneTimeIf: 'oneTimeIf'} }); // /NORMATIVE } - // Important: keep arrays outside of function to not create new instances. - // NORMATIVE - const $c1_dirs$ = [OneTimeIf]; - // /NORMATIVE - @Component({ template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}}
{{name | myPurePipe:size}}
` @@ -156,7 +152,7 @@ describe('pipes', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { let $pi$: $MyPurePipe$; @@ -165,7 +161,7 @@ describe('pipes', () => { $pi$ = $r3$.ɵPp(1, $MyPurePipe_ngPipeDef$); $r3$.ɵT(2); $r3$.ɵPp(3, $MyPurePipe_ngPipeDef$, $pi$); - $r3$.ɵC(4, $c1_dirs$, C4); + $r3$.ɵC(4, C4, '', ['oneTimeIf', '']); } $r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, ctx.name, ctx.size), '')); $r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(3, ctx.name, ctx.size), '')); @@ -187,6 +183,10 @@ describe('pipes', () => { // /NORMATIVE } + // NON-NORMATIVE + MyApp.ngComponentDef.directiveDefs = [OneTimeIf.ngDirectiveDef]; + // /NON-NORMATIVE + let myApp: MyApp = renderComponent(MyApp); expect(toHtml(containerEl)).toEqual('WorldWorld
World
'); expect(myPurePipeTransformCalls).toEqual(3); diff --git a/packages/core/test/render3/compiler_canonical/query_spec.ts b/packages/core/test/render3/compiler_canonical/query_spec.ts index 5c20da4278..6ebb49f965 100644 --- a/packages/core/test/render3/compiler_canonical/query_spec.ts +++ b/packages/core/test/render3/compiler_canonical/query_spec.ts @@ -22,6 +22,7 @@ describe('queries', () => { class SomeDirective { static ngDirectiveDef = $r3$.ɵdefineDirective({ type: SomeDirective, + selector: [[['', 'someDir', ''], null]], factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); }, features: [$r3$.ɵPublicFeature] }); @@ -32,7 +33,6 @@ describe('queries', () => { // NORMATIVE const $e1_attrs$ = ['someDir', '']; - const $e1_dirs$ = [SomeDirective]; // /NORMATIVE @Component({ @@ -48,14 +48,14 @@ describe('queries', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: ViewQueryComponent, - tag: 'view-query-component', + selector: [[['view-query-component'], null]], factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); }, template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) { let $tmp$: any; if (cm) { $r3$.ɵQ(0, SomeDirective, false); $r3$.ɵQ(1, SomeDirective, false); - $r3$.ɵE(2, 'div', $e1_attrs$, $e1_dirs$); + $r3$.ɵE(2, 'div', $e1_attrs$); $r3$.ɵe(); } @@ -67,6 +67,9 @@ describe('queries', () => { // /NORMATIVE } + // NON-NORMATIVE + ViewQueryComponent.ngComponentDef.directiveDefs = [SomeDirective.ngDirectiveDef]; + // /NON-NORMATIVE const viewQueryComp = renderComponent(ViewQueryComponent); expect(viewQueryComp.someDir).toEqual(someDir); @@ -92,7 +95,7 @@ describe('queries', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: ContentQueryComponent, - tag: 'content-query-component', + selector: [[['content-query-component'], null]], factory: function ContentQueryComponent_Factory() { return [ new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false), @@ -120,7 +123,6 @@ describe('queries', () => { } const $e2_attrs$ = ['someDir', '']; - const $e2_dirs$ = [SomeDirective]; @Component({ selector: 'my-app', @@ -134,13 +136,13 @@ describe('queries', () => { // NON-NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: function MyApp_Factory() { return new MyApp(); }, template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { if (cm) { - $r3$.ɵE(0, ContentQueryComponent); + $r3$.ɵE(0, 'content-query-component'); contentQueryComp = $r3$.ɵd(0)[0]; - $r3$.ɵE(1, 'div', $e2_attrs$, $e2_dirs$); + $r3$.ɵE(1, 'div', $e2_attrs$); $r3$.ɵe(); $r3$.ɵe(); } @@ -149,6 +151,10 @@ describe('queries', () => { // /NON-NORMATIVE } + // NON-NORMATIVE + MyApp.ngComponentDef.directiveDefs = + [ContentQueryComponent.ngComponentDef, SomeDirective.ngDirectiveDef]; + // /NON-NORMATIVE expect(toHtml(renderComponent(MyApp))) .toEqual( diff --git a/packages/core/test/render3/compiler_canonical/sanitize_spec.ts b/packages/core/test/render3/compiler_canonical/sanitize_spec.ts index e0671c8902..047c6ad056 100644 --- a/packages/core/test/render3/compiler_canonical/sanitize_spec.ts +++ b/packages/core/test/render3/compiler_canonical/sanitize_spec.ts @@ -38,7 +38,7 @@ describe('compiler sanitization', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { diff --git a/packages/core/test/render3/compiler_canonical/small_app_spec.ts b/packages/core/test/render3/compiler_canonical/small_app_spec.ts index ac4f61e9ed..9079e57e72 100644 --- a/packages/core/test/render3/compiler_canonical/small_app_spec.ts +++ b/packages/core/test/render3/compiler_canonical/small_app_spec.ts @@ -58,7 +58,7 @@ class ToDoAppComponent { // NORMATIVE static ngComponentDef = r3.defineComponent({ type: ToDoAppComponent, - tag: 'todo-app', + selector: [[['todo-app'], null]], factory: function ToDoAppComponent_Factory() { return new ToDoAppComponent(r3.directiveInject(AppState)); }, @@ -67,7 +67,7 @@ class ToDoAppComponent { const ToDoAppComponent_NgForOf_Template = function ToDoAppComponent_NgForOf_Template( ctx1: NgForOfContext, cm: boolean) { if (cm) { - r3.E(0, ToDoItemComponent); + r3.E(0, 'todo'); r3.L('archive', ctx.onArchive.bind(ctx)); r3.e(); } @@ -77,7 +77,7 @@ class ToDoAppComponent { r3.T(1, 'ToDo Application'); r3.e(); r3.E(2, 'div'); - r3.C(3, c3_directives, ToDoAppComponent_NgForOf_Template); + r3.C(3, ToDoAppComponent_NgForOf_Template, '', ['ngForOf', '']); r3.e(); r3.E(4, 'span'); r3.T(5); @@ -89,9 +89,10 @@ class ToDoAppComponent { // /NORMATIVE } -// NORMATIVE -const c3_directives = [NgForOf as r3.DirectiveType>]; -// /NORMATIVE +// NON-NORMATIVE +ToDoAppComponent.ngComponentDef.directiveDefs = () => + [ToDoItemComponent.ngComponentDef, (NgForOf as r3.DirectiveType>).ngDirectiveDef]; +// /NON-NORMATIVE @Component({ selector: 'todo', @@ -122,7 +123,7 @@ class ToDoItemComponent { // NORMATIVE static ngComponentDef = r3.defineComponent({ type: ToDoItemComponent, - tag: 'todo', + selector: [[['todo'], null]], factory: function ToDoItemComponent_Factory() { return new ToDoItemComponent(); }, template: function ToDoItemComponent_Template(ctx: ToDoItemComponent, cm: boolean) { if (cm) { diff --git a/packages/core/test/render3/compiler_canonical/template_variables_spec.ts b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts index 2eebbf21e4..237a19db32 100644 --- a/packages/core/test/render3/compiler_canonical/template_variables_spec.ts +++ b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts @@ -63,6 +63,7 @@ describe('template variables', () => { // NORMATIVE static ngDirectiveDef = $r3$.ɵdefineDirective({ type: ForOfDirective, + selector: [[['', 'forOf', ''], null]], factory: function ForOfDirective_Factory() { return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); }, @@ -80,10 +81,6 @@ describe('template variables', () => { name: string; } - // NORMATIVE - const $c1_dirs$ = [ForOfDirective]; - // /NORMATIVE - @Component({ selector: 'my-component', template: `
  • {{item.name}}
` @@ -94,12 +91,12 @@ describe('template variables', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { $r3$.ɵE(0, 'ul'); - $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1); + $r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, '', ['forOf', '']); $r3$.ɵe(); } $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items)); @@ -120,6 +117,10 @@ describe('template variables', () => { // /NORMATIVE } + // NON-NORMATIVE + MyComponent.ngComponentDef.directiveDefs = [ForOfDirective.ngDirectiveDef]; + // /NON-NORMATIVE + // TODO(chuckj): update when the changes to enable ngForOf lands. expect(toHtml(renderComponent(MyComponent))).toEqual('
    '); }); @@ -135,10 +136,6 @@ describe('template variables', () => { infos: Info[]; } - // NORMATIVE - const $c1_dirs$ = [ForOfDirective]; - // /NORMATIVE - @Component({ selector: 'my-component', template: ` @@ -162,12 +159,12 @@ describe('template variables', () => { // NORMATIVE static ngComponentDef = $r3$.ɵdefineComponent({ type: MyComponent, - tag: 'my-component', + selector: [[['my-component'], null]], factory: function MyComponent_Factory() { return new MyComponent(); }, template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { if (cm) { $r3$.ɵE(0, 'ul'); - $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1); + $r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, '', ['forOf', '']); $r3$.ɵe(); } $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items)); @@ -181,7 +178,7 @@ describe('template variables', () => { $r3$.ɵT(2); $r3$.ɵe(); $r3$.ɵE(3, 'ul'); - $r3$.ɵC(4, $c1_dirs$, MyComponent_ForOfDirective_ForOfDirective_Template_3); + $r3$.ɵC(4, MyComponent_ForOfDirective_ForOfDirective_Template_3, '', ['forOf', '']); $r3$.ɵe(); $r3$.ɵe(); } diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index 91c8f92f7d..7f84cf03c8 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -24,7 +24,7 @@ describe('component', () => { static ngComponentDef = defineComponent({ type: CounterComponent, - tag: 'counter', + selector: [[['counter'], null]], template: function(ctx: CounterComponent, cm: boolean) { if (cm) { text(0); @@ -87,7 +87,7 @@ describe('component with a container', () => { items: string[]; static ngComponentDef = defineComponent({ type: WrapperComponent, - tag: 'wrapper', + selector: [[['wrapper'], null]], template: function ChildComponentTemplate(ctx: {items: string[]}, cm: boolean) { if (cm) { container(0); @@ -107,18 +107,20 @@ describe('component with a container', () => { function template(ctx: {items: string[]}, cm: boolean) { if (cm) { - elementStart(0, WrapperComponent); + elementStart(0, 'wrapper'); elementEnd(); } elementProperty(0, 'items', bind(ctx.items)); } + const defs = [WrapperComponent.ngComponentDef]; + it('should re-render on input change', () => { const ctx: {items: string[]} = {items: ['a']}; - expect(renderToHtml(template, ctx)).toEqual('a'); + expect(renderToHtml(template, ctx, defs)).toEqual('a'); ctx.items = [...ctx.items, 'b']; - expect(renderToHtml(template, ctx)).toEqual('ab'); + expect(renderToHtml(template, ctx, defs)).toEqual('ab'); }); }); @@ -129,38 +131,40 @@ describe('encapsulation', () => { class WrapperComponent { static ngComponentDef = defineComponent({ type: WrapperComponent, - tag: 'wrapper', + selector: [[['wrapper'], null]], template: function(ctx: WrapperComponent, cm: boolean) { if (cm) { - elementStart(0, EncapsulatedComponent); + elementStart(0, 'encapsulated'); elementEnd(); } }, factory: () => new WrapperComponent, + directiveDefs: () => [EncapsulatedComponent.ngComponentDef] }); } class EncapsulatedComponent { static ngComponentDef = defineComponent({ type: EncapsulatedComponent, - tag: 'encapsulated', + selector: [[['encapsulated'], null]], template: function(ctx: EncapsulatedComponent, cm: boolean) { if (cm) { text(0, 'foo'); - elementStart(1, LeafComponent); + elementStart(1, 'leaf'); elementEnd(); } }, factory: () => new EncapsulatedComponent, rendererType: createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}), + directiveDefs: () => [LeafComponent.ngComponentDef] }); } class LeafComponent { static ngComponentDef = defineComponent({ type: LeafComponent, - tag: 'leaf', + selector: [[['leaf'], null]], template: function(ctx: LeafComponent, cm: boolean) { if (cm) { elementStart(0, 'span'); @@ -190,23 +194,24 @@ describe('encapsulation', () => { class WrapperComponentWith { static ngComponentDef = defineComponent({ type: WrapperComponentWith, - tag: 'wrapper', + selector: [[['wrapper'], null]], template: function(ctx: WrapperComponentWith, cm: boolean) { if (cm) { - elementStart(0, LeafComponentwith); + elementStart(0, 'leaf'); elementEnd(); } }, factory: () => new WrapperComponentWith, rendererType: createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}), + directiveDefs: () => [LeafComponentwith.ngComponentDef] }); } class LeafComponentwith { static ngComponentDef = defineComponent({ type: LeafComponentwith, - tag: 'leaf', + selector: [[['leaf'], null]], template: function(ctx: LeafComponentwith, cm: boolean) { if (cm) { elementStart(0, 'span'); @@ -245,7 +250,7 @@ describe('recursive components', () => { static ngComponentDef = defineComponent({ type: TreeComponent, - tag: 'tree-comp', + selector: [[['tree-comp'], null]], factory: () => new TreeComponent(), template: (ctx: TreeComponent, cm: boolean) => { if (cm) { @@ -258,7 +263,7 @@ describe('recursive components', () => { { if (ctx.data.left != null) { if (embeddedViewStart(0)) { - elementStart(0, TreeComponent); + elementStart(0, 'tree-comp'); elementEnd(); } elementProperty(0, 'data', bind(ctx.data.left)); @@ -270,7 +275,7 @@ describe('recursive components', () => { { if (ctx.data.right != null) { if (embeddedViewStart(0)) { - elementStart(0, TreeComponent); + elementStart(0, 'tree-comp'); elementEnd(); } elementProperty(0, 'data', bind(ctx.data.right)); @@ -283,6 +288,7 @@ describe('recursive components', () => { }); } + TreeComponent.ngComponentDef.directiveDefs = () => [TreeComponent.ngComponentDef]; function _buildTree(currDepth: number): TreeNode { const children = currDepth < 2 ? _buildTree(currDepth + 1) : null; diff --git a/packages/core/test/render3/content_spec.ts b/packages/core/test/render3/content_spec.ts index 3cefa17733..74f0781009 100644 --- a/packages/core/test/render3/content_spec.ts +++ b/packages/core/test/render3/content_spec.ts @@ -31,11 +31,12 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    content
    '); }); @@ -49,11 +50,12 @@ describe('content projection', () => { }); const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('content'); }); @@ -70,14 +72,14 @@ describe('content projection', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { projectionDef(0); - elementStart(1, GrandChild); + elementStart(1, 'grand-child'); { projection(2, 0); } elementEnd(); } - }); + }, [GrandChild.ngComponentDef]); const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'b'); text(2, 'Hello'); @@ -86,7 +88,8 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)) .toEqual('
    HelloWorld!
    '); @@ -117,14 +120,15 @@ describe('content projection', () => { */ const Parent = createComponent('parent', (ctx: any, cm: boolean) => { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected-comp'); elementEnd(); } elementEnd(); } - }); + }, [Child.ngComponentDef, ProjectedComp.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)) .toEqual('
    content
    '); @@ -141,7 +145,7 @@ describe('content projection', () => { }); const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { text(1, '('); container(2); @@ -159,7 +163,8 @@ describe('content projection', () => { } } containerRefreshEnd(); - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    ()
    '); parent.value = true; @@ -179,7 +184,7 @@ describe('content projection', () => { }); const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { container(1); } elementEnd(); } @@ -193,7 +198,8 @@ describe('content projection', () => { } } containerRefreshEnd(); - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual(''); @@ -217,7 +223,7 @@ describe('content projection', () => { }); const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { text(1, '('); container(2); @@ -240,7 +246,8 @@ describe('content projection', () => { } } containerRefreshEnd(); - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    (else)
    '); parent.value = true; @@ -289,14 +296,15 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { childCmptInstance = loadDirective(0); text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    content
    '); @@ -340,14 +348,15 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { childCmptInstance = loadDirective(0); text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    content
    '); @@ -393,14 +402,15 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { childCmptInstance = loadDirective(0); text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    before-content-after
    '); @@ -431,11 +441,12 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    content
    '); }); @@ -484,14 +495,15 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { childCmptInstance = loadDirective(0); text(1, 'content'); } elementEnd(); } - }); + }, [Child.ngComponentDef]); + const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('content
    '); @@ -529,7 +541,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span', ['title', 'toFirst']); { text(2, '1'); } @@ -540,7 +552,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -575,7 +587,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span', ['class', 'toFirst']); { text(2, '1'); } @@ -586,7 +598,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -621,7 +633,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span', ['class', 'other toFirst']); { text(2, '1'); } @@ -632,7 +644,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -667,7 +679,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span', ['class', 'toFirst']); { text(2, '1'); } @@ -678,7 +690,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -711,7 +723,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span', ['class', 'toFirst']); { text(2, '1'); } @@ -723,7 +735,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -757,7 +769,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span'); { text(2, '1'); } @@ -769,7 +781,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -807,7 +819,7 @@ describe('content projection', () => { const Child = createComponent('child', function(ctx: any, cm: boolean) { if (cm) { projectionDef(0); - elementStart(1, GrandChild); + elementStart(1, 'grand-child'); { projection(2, 0); elementStart(3, 'span'); @@ -816,7 +828,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [GrandChild.ngComponentDef]); /** * @@ -827,7 +839,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'span'); { text(2, 'parent content'); } @@ -835,7 +847,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)) @@ -871,7 +883,7 @@ describe('content projection', () => { const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) { if (cm) { projectionDef(0); - elementStart(1, Card); + elementStart(1, 'card'); { elementStart(2, 'h1', ['card-title', '']); { text(3, 'Title'); } @@ -880,7 +892,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Card.ngComponentDef]); /** * @@ -889,11 +901,11 @@ describe('content projection', () => { */ const App = createComponent('app', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, CardWithTitle); + elementStart(0, 'card-with-title'); { text(1, 'content'); } elementEnd(); } - }); + }, [CardWithTitle.ngComponentDef]); const app = renderComponent(App); expect(toHtml(app)) @@ -930,7 +942,7 @@ describe('content projection', () => { const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) { if (cm) { projectionDef(0); - elementStart(1, Card); + elementStart(1, 'card'); { elementStart(2, 'h1', ['ngProjectAs', '[card-title]']); { text(3, 'Title'); } @@ -939,7 +951,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Card.ngComponentDef]); /** * @@ -948,11 +960,11 @@ describe('content projection', () => { */ const App = createComponent('app', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, CardWithTitle); + elementStart(0, 'card-with-title'); { text(1, 'content'); } elementEnd(); } - }); + }, [CardWithTitle.ngComponentDef]); const app = renderComponent(App); expect(toHtml(app)) @@ -980,7 +992,7 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Child); + elementStart(0, 'child'); { elementStart(1, 'div', ['ngProjectAs', 'span']); { text(2, 'should not project'); } @@ -991,7 +1003,7 @@ describe('content projection', () => { } elementEnd(); } - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    should project
    '); @@ -1020,8 +1032,8 @@ describe('content projection', () => { */ const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { if (cm) { - elementStart(0, Child); - { container(1, undefined, undefined, 'div'); } + elementStart(0, 'child'); + { container(1, undefined, 'div'); } elementEnd(); } containerRefreshStart(1); @@ -1036,7 +1048,7 @@ describe('content projection', () => { } } containerRefreshEnd(); - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Parent); expect(toHtml(parent)).toEqual('
    content
    '); }); diff --git a/packages/core/test/render3/control_flow_spec.ts b/packages/core/test/render3/control_flow_spec.ts index 462722988f..246717ddce 100644 --- a/packages/core/test/render3/control_flow_spec.ts +++ b/packages/core/test/render3/control_flow_spec.ts @@ -6,9 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ +import {defineComponent} from '../../src/render3/definition'; import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding} from '../../src/render3/instructions'; -import {renderToHtml} from './render_util'; +import {ComponentFixture, createComponent, renderToHtml} from './render_util'; describe('JS control flow', () => { it('should work with if block', () => { @@ -531,6 +532,136 @@ describe('JS control flow', () => { ctx.condition = true; expect(renderToHtml(Template, ctx)).toEqual('
    Hello
    '); }); + + it('should work with sibling if blocks with children', () => { + let log: string[] = []; + + // Intentionally duplicating the templates in test below so we are + // testing the behavior on firstTemplatePass for each of these tests + class Comp { + static ngComponentDef = defineComponent({ + type: Comp, + selector: [[['comp'], null]], + factory: () => { + log.push('comp!'); + return new Comp(); + }, + template: function(ctx: Comp, cm: boolean) {} + }); + } + + class App { + condition = true; + condition2 = true; + + static ngComponentDef = defineComponent({ + type: App, + selector: [[['app'], null]], + factory: () => new App(), + template: function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'div'); + elementEnd(); + container(1); + container(2); + } + containerRefreshStart(1); + { + if (ctx.condition) { + if (embeddedViewStart(0)) { + elementStart(0, 'comp'); + elementEnd(); + } + embeddedViewEnd(); + } + } + containerRefreshEnd(); + containerRefreshStart(2); + { + if (ctx.condition2) { + if (embeddedViewStart(0)) { + elementStart(0, 'comp'); + elementEnd(); + } + embeddedViewEnd(); + } + } + containerRefreshEnd(); + }, + directiveDefs: () => [Comp.ngComponentDef] + }); + } + + const fixture = new ComponentFixture(App); + expect(log).toEqual(['comp!', 'comp!']); + }); + + it('should work with a sibling if block that starts closed', () => { + let log: string[] = []; + + // Intentionally duplicating the templates from above so we are + // testing the behavior on firstTemplatePass for each of these tests + class Comp { + static ngComponentDef = defineComponent({ + type: Comp, + selector: [[['comp'], null]], + factory: () => { + log.push('comp!'); + return new Comp(); + }, + template: function(ctx: Comp, cm: boolean) {} + }); + } + + class App { + condition = false; + condition2 = true; + + static ngComponentDef = defineComponent({ + type: App, + selector: [[['app'], null]], + factory: () => new App(), + template: function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'div'); + elementEnd(); + container(1); + container(2); + } + containerRefreshStart(1); + { + if (ctx.condition) { + if (embeddedViewStart(0)) { + elementStart(0, 'comp'); + elementEnd(); + } + embeddedViewEnd(); + } + } + containerRefreshEnd(); + containerRefreshStart(2); + { + if (ctx.condition2) { + if (embeddedViewStart(0)) { + elementStart(0, 'comp'); + elementEnd(); + } + embeddedViewEnd(); + } + } + containerRefreshEnd(); + }, + directiveDefs: () => [Comp.ngComponentDef] + }); + } + + const fixture = new ComponentFixture(App); + expect(log).toEqual(['comp!']); + + fixture.component.condition = true; + fixture.update(); + expect(log).toEqual(['comp!', 'comp!']); + }); }); describe('JS for loop', () => { diff --git a/packages/core/test/render3/define_spec.ts b/packages/core/test/render3/define_spec.ts index 059af2f523..02aec27408 100644 --- a/packages/core/test/render3/define_spec.ts +++ b/packages/core/test/render3/define_spec.ts @@ -29,6 +29,7 @@ describe('define', () => { static ngDirectiveDef = defineDirective({ type: MyDirective, + selector: [[['', 'myDir', ''], null]], factory: () => new MyDirective(), features: [NgOnChangesFeature()], inputs: {valA: 'valA', valB: 'valB'} diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 65fee2110d..589d210611 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -17,19 +17,20 @@ import {LNodeType} from '../../src/render3/interfaces/node'; import {LViewFlags} from '../../src/render3/interfaces/view'; import {ViewRef} from '../../src/render3/view_ref'; -import {renderComponent, renderToHtml, toHtml} from './render_util'; +import {createComponent, createDirective, renderComponent, renderToHtml, toHtml} from './render_util'; describe('di', () => { describe('no dependencies', () => { it('should create directive with no deps', () => { class Directive { value: string = 'Created'; - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive}); + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive}); } function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); { text(1); } elementEnd(); } @@ -37,7 +38,8 @@ describe('di', () => { textBinding(1, bind(loadDirective(0).value)); } - expect(renderToHtml(Template, {})).toEqual('
    Created
    '); + expect(renderToHtml(Template, {}, [Directive.ngDirectiveDef])) + .toEqual('
    Created
    '); }); }); @@ -45,14 +47,22 @@ describe('di', () => { it('should create directive with inter view dependencies', () => { class DirectiveA { value: string = 'A'; - static ngDirectiveDef = defineDirective( - {type: DirectiveA, factory: () => new DirectiveA, features: [PublicFeature]}); + static ngDirectiveDef = defineDirective({ + type: DirectiveA, + selector: [[['', 'dirA', ''], null]], + factory: () => new DirectiveA, + features: [PublicFeature] + }); } class DirectiveB { value: string = 'B'; - static ngDirectiveDef = defineDirective( - {type: DirectiveB, factory: () => new DirectiveB, features: [PublicFeature]}); + static ngDirectiveDef = defineDirective({ + type: DirectiveB, + selector: [[['', 'dirB', ''], null]], + factory: () => new DirectiveB, + features: [PublicFeature] + }); } class DirectiveC { @@ -60,15 +70,16 @@ describe('di', () => { constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; } static ngDirectiveDef = defineDirective({ type: DirectiveC, + selector: [[['', 'dirC', ''], null]], factory: () => new DirectiveC(directiveInject(DirectiveA), directiveInject(DirectiveB)) }); } function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [DirectiveA]); + elementStart(0, 'div', ['dirA', '']); { - elementStart(1, 'span', null, [DirectiveB, DirectiveC]); + elementStart(1, 'span', ['dirB', '', 'dirC', '']); { text(2); } elementEnd(); } @@ -78,7 +89,10 @@ describe('di', () => { textBinding(2, bind(loadDirective(2).value)); } - expect(renderToHtml(Template, {})).toEqual('
    AB
    '); + const defs = + [DirectiveA.ngDirectiveDef, DirectiveB.ngDirectiveDef, DirectiveC.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)) + .toEqual('
    AB
    '); }); }); @@ -91,6 +105,7 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => new Directive(injectElementRef()), features: [PublicFeature] }); @@ -103,13 +118,14 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: DirectiveSameInstance, + selector: [[['', 'dirSame', ''], null]], factory: () => new DirectiveSameInstance(injectElementRef(), directiveInject(Directive)) }); } function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive, DirectiveSameInstance]); + elementStart(0, 'div', ['dir', '', 'dirSame', '']); { text(1); } elementEnd(); } @@ -120,7 +136,9 @@ describe('di', () => { loadDirective(1).value, '')); } - expect(renderToHtml(Template, {})).toEqual('
    ElementRef-true
    '); + const defs = [Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)) + .toEqual('
    ElementRef-true
    '); }); }); @@ -133,6 +151,7 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => new Directive(injectTemplateRef()), features: [PublicFeature] }); @@ -145,6 +164,7 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: DirectiveSameInstance, + selector: [[['', 'dirSame', ''], null]], factory: () => new DirectiveSameInstance(injectTemplateRef(), directiveInject(Directive)) }); } @@ -152,7 +172,7 @@ describe('di', () => { function Template(ctx: any, cm: any) { if (cm) { - container(0, [Directive, DirectiveSameInstance], function() {}); + container(0, function() {}, undefined, ['dir', '', 'dirSame', '']); text(1); } // TODO: remove loadDirective when removing directive references @@ -162,7 +182,8 @@ describe('di', () => { loadDirective(1).value, '')); } - expect(renderToHtml(Template, {})).toEqual('TemplateRef-true'); + const defs = [Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)).toEqual('TemplateRef-true'); }); }); @@ -175,6 +196,7 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => new Directive(injectViewContainerRef()), features: [PublicFeature] }); @@ -187,6 +209,7 @@ describe('di', () => { } static ngDirectiveDef = defineDirective({ type: DirectiveSameInstance, + selector: [[['', 'dirSame', ''], null]], factory: () => new DirectiveSameInstance(injectViewContainerRef(), directiveInject(Directive)) }); @@ -194,7 +217,7 @@ describe('di', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive, DirectiveSameInstance]); + elementStart(0, 'div', ['dir', '', 'dirSame', '']); { text(1); } elementEnd(); } @@ -205,7 +228,9 @@ describe('di', () => { loadDirective(1).value, '')); } - expect(renderToHtml(Template, {})).toEqual('
    ViewContainerRef-true
    '); + const defs = [Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)) + .toEqual('
    ViewContainerRef-true
    '); }); }); @@ -219,7 +244,7 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: () => comp = new MyComp(injectChangeDetectorRef()), template: function(ctx: MyComp, cm: boolean) { if (cm) { @@ -235,6 +260,7 @@ describe('di', () => { constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => dir = new Directive(injectChangeDetectorRef()), features: [PublicFeature], exportAs: 'dir' @@ -246,34 +272,53 @@ describe('di', () => { static ngDirectiveDef = defineDirective({ type: DirectiveSameInstance, + selector: [[['', 'dirSame', ''], null]], factory: () => dirSameInstance = new DirectiveSameInstance(injectChangeDetectorRef()) }); } - const $e0_attrs$ = ['dir', '', 'dirSameInstance', '']; + class IfDirective { + /* @Input */ + myIf = true; + + constructor(public template: TemplateRef, public vcr: ViewContainerRef) {} + + ngOnChanges() { + if (this.myIf) { + this.vcr.createEmbeddedView(this.template); + } + } + + static ngDirectiveDef = defineDirective({ + type: IfDirective, + selector: [[['', 'myIf', ''], null]], + factory: () => new IfDirective(injectTemplateRef(), injectViewContainerRef()), + inputs: {myIf: 'myIf'}, + features: [PublicFeature, NgOnChangesFeature()] + }); + } + + + const defs = [ + MyComp.ngComponentDef, Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef, + IfDirective.ngDirectiveDef + ]; it('should inject current component ChangeDetectorRef into directives on components', () => { - class MyApp { - static ngComponentDef = defineComponent({ - type: MyApp, - tag: 'my-app', - factory: () => new MyApp(), - /** {{ dir.value }} */ - template: function(ctx: any, cm: boolean) { - if (cm) { - elementStart(0, MyComp, $e0_attrs$, [Directive, DirectiveSameInstance]); - elementEnd(); - text(1); - } - // TODO: remove loadDirective when removing directive references - textBinding(1, bind(loadDirective(1).value)); - } - }); - } + /** {{ dir.value }} */ + const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']); + elementEnd(); + text(1); + } + // TODO: remove loadDirective when removing directive references + textBinding(1, bind(loadDirective(1).value)); + }, defs); const app = renderComponent(MyApp); // ChangeDetectorRef is the token, ViewRef has historically been the constructor - expect(toHtml(app)).toEqual('ViewRef'); + expect(toHtml(app)).toEqual('ViewRef'); expect((comp !.cdr as ViewRef).context).toBe(comp); expect(dir !.cdr).toBe(comp !.cdr); @@ -287,23 +332,24 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /**
    {{ dir.value }}
    */ template: function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); { text(1); } elementEnd(); } // TODO: remove loadDirective when removing directive references textBinding(1, bind(loadDirective(0).value)); - } + }, + directiveDefs: defs }); } const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
    ViewRef
    '); + expect(toHtml(app)).toEqual('
    ViewRef
    '); expect((app !.cdr as ViewRef).context).toBe(app); expect(dir !.cdr).toBe(app.cdr); @@ -316,7 +362,7 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /** * @@ -326,9 +372,9 @@ describe('di', () => { */ template: function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); { - elementStart(1, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); + elementStart(1, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); elementEnd(); } elementEnd(); @@ -336,13 +382,13 @@ describe('di', () => { } // TODO: remove loadDirective when removing directive references textBinding(2, bind(loadDirective(1).value)); - } + }, + directiveDefs: defs }); } const app = renderComponent(MyApp); - expect(toHtml(app)) - .toEqual('
    ViewRef'); + expect(toHtml(app)).toEqual('
    ViewRef'); expect((app !.cdr as ViewRef).context).toBe(app); expect(dir !.cdr).toBe(app !.cdr); @@ -358,7 +404,7 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /** * % if (showing) { @@ -373,7 +419,7 @@ describe('di', () => { { if (ctx.showing) { if (embeddedViewStart(0)) { - elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); { text(1); } elementEnd(); } @@ -383,12 +429,13 @@ describe('di', () => { embeddedViewEnd(); } containerRefreshEnd(); - } + }, + directiveDefs: defs }); } const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
    ViewRef
    '); + expect(toHtml(app)).toEqual('
    ViewRef
    '); expect((app !.cdr as ViewRef).context).toBe(app); expect(dir !.cdr).toBe(app.cdr); @@ -396,26 +443,6 @@ describe('di', () => { }); it('should inject host component ChangeDetectorRef into directives on containers', () => { - class IfDirective { - /* @Input */ - myIf = true; - - constructor(public template: TemplateRef, public vcr: ViewContainerRef) {} - - ngOnChanges() { - if (this.myIf) { - this.vcr.createEmbeddedView(this.template); - } - } - - static ngDirectiveDef = defineDirective({ - type: IfDirective, - factory: () => new IfDirective(injectTemplateRef(), injectViewContainerRef()), - inputs: {myIf: 'myIf'}, - features: [PublicFeature, NgOnChangesFeature()] - }); - } - class MyApp { showing = true; @@ -423,31 +450,32 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp(injectChangeDetectorRef()), /**
    {{ dir.value }}
    */ template: function(ctx: MyApp, cm: boolean) { if (cm) { - container(0, [IfDirective], C1); + container(0, C1, undefined, ['myIf', 'showing']); } containerRefreshStart(0); containerRefreshEnd(); function C1(ctx1: any, cm1: boolean) { if (cm1) { - elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); { text(1); } elementEnd(); } // TODO: remove loadDirective when removing directive references textBinding(1, bind(loadDirective(0).value)); } - } + }, + directiveDefs: defs }); } const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
    ViewRef
    '); + expect(toHtml(app)).toEqual('
    ViewRef
    '); expect((app !.cdr as ViewRef).context).toBe(app); expect(dir !.cdr).toBe(app.cdr); @@ -457,20 +485,14 @@ describe('di', () => { it('should injectAttribute', () => { let exist: string|undefined = 'wrong'; let nonExist: string|undefined = 'wrong'; - class MyApp { - static ngComponentDef = defineComponent({ - type: MyApp, - tag: 'my-app', - factory: () => new MyApp(), - template: function(ctx: MyApp, cm: boolean) { - if (cm) { - elementStart(0, 'div', ['exist', 'existValue', 'other', 'ignore']); - exist = injectAttribute('exist'); - nonExist = injectAttribute('nonExist'); - } - } - }); - } + + const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'div', ['exist', 'existValue', 'other', 'ignore']); + exist = injectAttribute('exist'); + nonExist = injectAttribute('nonExist'); + } + }); const app = renderComponent(MyApp); expect(exist).toEqual('existValue'); @@ -557,7 +579,7 @@ describe('di', () => { static ngComponentDef = defineComponent({ type: MyApp, - tag: 'my-app', + selector: [[['my-app'], null]], factory: () => new MyApp( directiveInject(String as any, InjectFlags.Default, 'DefaultValue')), template: () => null @@ -569,21 +591,14 @@ describe('di', () => { }); it('should inject from parent view', () => { - class ParentDirective { - static ngDirectiveDef = defineDirective({ - type: ParentDirective, - factory: () => new ParentDirective(), - features: [PublicFeature] - }); - } + const ParentDirective = createDirective('parentDir'); class ChildDirective { value: string; - constructor(public parent: ParentDirective) { - this.value = (parent.constructor as any).name; - } + constructor(public parent: any) { this.value = (parent.constructor as any).name; } static ngDirectiveDef = defineDirective({ type: ChildDirective, + selector: [[['', 'childDir', ''], null]], factory: () => new ChildDirective(directiveInject(ParentDirective)), features: [PublicFeature] }); @@ -591,10 +606,9 @@ describe('di', () => { class Child2Directive { value: boolean; - constructor(parent: ParentDirective, child: ChildDirective) { - this.value = parent === child.parent; - } + constructor(parent: any, child: ChildDirective) { this.value = parent === child.parent; } static ngDirectiveDef = defineDirective({ + selector: [[['', 'child2Dir', ''], null]], type: Child2Directive, factory: () => new Child2Directive( directiveInject(ParentDirective), directiveInject(ChildDirective)) @@ -603,14 +617,14 @@ describe('di', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [ParentDirective]); + elementStart(0, 'div', ['parentDir', '']); { container(1); } elementEnd(); } containerRefreshStart(1); { if (embeddedViewStart(0)) { - elementStart(0, 'span', null, [ChildDirective, Child2Directive]); + elementStart(0, 'span', ['childDir', '', 'child2Dir', '']); { text(1); } elementEnd(); } @@ -624,7 +638,12 @@ describe('di', () => { containerRefreshEnd(); } - expect(renderToHtml(Template, {})).toEqual('
    ParentDirective-true
    '); + const defs = [ + ChildDirective.ngDirectiveDef, Child2Directive.ngDirectiveDef, + ParentDirective.ngDirectiveDef + ]; + expect(renderToHtml(Template, {}, defs)) + .toEqual('
    Directive-true
    '); }); it('should inject from module Injector', () => { @@ -635,7 +654,7 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = - createLView(-1, null !, createTView(), null, null, LViewFlags.CheckAlways); + createLView(-1, null !, createTView(null), null, null, LViewFlags.CheckAlways); const oldView = enterView(contentView, null !); try { const parent = createLNode(0, LNodeType.Element, null, null); diff --git a/packages/core/test/render3/directive_spec.ts b/packages/core/test/render3/directive_spec.ts index 6837cb1a9b..d7901ae07f 100644 --- a/packages/core/test/render3/directive_spec.ts +++ b/packages/core/test/render3/directive_spec.ts @@ -22,6 +22,7 @@ describe('directive', () => { klass = 'foo'; static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => directiveInstance = new Directive, hostBindings: (directiveIndex: number, elementIndex: number) => { elementProperty( @@ -32,14 +33,15 @@ describe('directive', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'span', null, [Directive]); + elementStart(0, 'span', ['dir', '']); elementEnd(); } } - expect(renderToHtml(Template, {})).toEqual(''); + const defs = [Directive.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)).toEqual(''); directiveInstance !.klass = 'bar'; - expect(renderToHtml(Template, {})).toEqual(''); + expect(renderToHtml(Template, {}, defs)).toEqual(''); }); }); diff --git a/packages/core/test/render3/exports_spec.ts b/packages/core/test/render3/exports_spec.ts index 14acd575b7..821a6724e2 100644 --- a/packages/core/test/render3/exports_spec.ts +++ b/packages/core/test/render3/exports_spec.ts @@ -17,7 +17,7 @@ describe('exports', () => { /** {{ myInput.value }} */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(0, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); text(1); } @@ -33,7 +33,7 @@ describe('exports', () => { /** {{ myComp.name }} */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComponent, null, null, ['myComp', '']); + elementStart(0, 'comp', null, ['myComp', '']); elementEnd(); text(1); } @@ -46,13 +46,13 @@ describe('exports', () => { static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'comp', + selector: [[['comp'], null]], template: function() {}, factory: () => new MyComponent }); } - expect(renderToHtml(Template, {})).toEqual('Nancy'); + expect(renderToHtml(Template, {}, [MyComponent.ngComponentDef])).toEqual('Nancy'); }); it('should support component instance fed into directive', () => { @@ -63,7 +63,7 @@ describe('exports', () => { constructor() { myComponent = this; } static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'comp', + selector: [[['comp'], null]], template: function() {}, factory: () => new MyComponent }); @@ -72,23 +72,29 @@ describe('exports', () => { class MyDir { myDir: MyComponent; constructor() { myDir = this; } - static ngDirectiveDef = - defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}}); + static ngDirectiveDef = defineDirective({ + type: MyDir, + selector: [[['', 'myDir', ''], null]], + factory: () => new MyDir, + inputs: {myDir: 'myDir'} + }); } + const defs = [MyComponent.ngComponentDef, MyDir.ngDirectiveDef]; + /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComponent, null, null, ['myComp', '']); + elementStart(0, 'comp', null, ['myComp', '']); elementEnd(); - elementStart(1, 'div', null, [MyDir]); + elementStart(1, 'div', ['myDir', '']); elementEnd(); } // TODO: replace loadDirective when removing directive refs elementProperty(1, 'myDir', bind(loadDirective(0))); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(myDir !.myDir).toEqual(myComponent !); }); @@ -97,7 +103,7 @@ describe('exports', () => { /**
    {{ myDir.name }} */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [SomeDir], ['myDir', 'someDir']); + elementStart(0, 'div', ['someDir', ''], ['myDir', 'someDir']); elementEnd(); text(1); } @@ -107,11 +113,16 @@ describe('exports', () => { class SomeDir { name = 'Drew'; - static ngDirectiveDef = - defineDirective({type: SomeDir, factory: () => new SomeDir, exportAs: 'someDir'}); + static ngDirectiveDef = defineDirective({ + type: SomeDir, + selector: [[['', 'someDir', ''], null]], + factory: () => new SomeDir, + exportAs: 'someDir' + }); } - expect(renderToHtml(Template, {})).toEqual('
    Drew'); + expect(renderToHtml(Template, {}, [SomeDir.ngDirectiveDef])) + .toEqual('
    Drew'); }); describe('forward refs', () => { @@ -120,7 +131,7 @@ describe('exports', () => { function Template(ctx: any, cm: boolean) { if (cm) { text(0); - elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(1, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); } let myInput = elementStart(1); @@ -137,7 +148,7 @@ describe('exports', () => { if (cm) { elementStart(0, 'div'); elementEnd(); - elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(1, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); } let myInput = elementStart(1); @@ -153,7 +164,7 @@ describe('exports', () => { if (cm) { elementStart(0, 'div'); elementEnd(); - elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(1, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); } let myInput = elementStart(1); @@ -169,7 +180,7 @@ describe('exports', () => { if (cm) { elementStart(0, 'div'); elementEnd(); - elementStart(1, 'input', ['type', 'checkbox', 'checked', 'true'], null, ['myInput', '']); + elementStart(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']); elementEnd(); } let myInput = elementStart(1); @@ -190,7 +201,7 @@ describe('exports', () => { static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'comp', + selector: [[['comp'], null]], template: function(ctx: MyComponent, cm: boolean) {}, factory: () => new MyComponent }); @@ -201,23 +212,27 @@ describe('exports', () => { constructor() { myDir = this; } - static ngDirectiveDef = - defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}}); + static ngDirectiveDef = defineDirective({ + type: MyDir, + selector: [[['', 'myDir', ''], null]], + factory: () => new MyDir, + inputs: {myDir: 'myDir'} + }); } /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [MyDir]); + elementStart(0, 'div', ['myDir', '']); elementEnd(); - elementStart(1, MyComponent, null, null, ['myComp', '']); + elementStart(1, 'comp', null, ['myComp', '']); elementEnd(); } // TODO: replace loadDirective when removing directive refs elementProperty(0, 'myDir', bind(loadDirective(1))); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, [MyComponent.ngComponentDef, MyDir.ngDirectiveDef]); expect(myDir !.myDir).toEqual(myComponent !); }); @@ -228,9 +243,9 @@ describe('exports', () => { if (cm) { text(0); text(1); - elementStart(2, MyComponent, null, null, ['myComp', '']); + elementStart(2, 'comp', null, ['myComp', '']); elementEnd(); - elementStart(3, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(3, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); } let myInput = elementStart(3); @@ -249,12 +264,13 @@ describe('exports', () => { static ngComponentDef = defineComponent({ type: MyComponent, - tag: 'comp', + selector: [[['comp'], null]], template: function() {}, factory: () => new MyComponent }); } - expect(renderToHtml(Template, {})).toEqual('oneNancy'); + expect(renderToHtml(Template, {}, [MyComponent.ngComponentDef])) + .toEqual('oneNancy'); }); it('should work inside a view container', () => { @@ -271,7 +287,7 @@ describe('exports', () => { { if (cm1) { text(0); - elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); + elementStart(1, 'input', ['value', 'one'], ['myInput', '']); elementEnd(); } let myInput = elementStart(1); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index a4966d69ea..d1f2bda289 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -178,7 +178,7 @@ describe('render3 integration test', () => { static ngComponentDef = defineComponent({ type: TodoComponent, - tag: 'todo', + selector: [[['todo'], null]], template: function TodoTemplate(ctx: any, cm: boolean) { if (cm) { elementStart(0, 'p'); @@ -194,26 +194,28 @@ describe('render3 integration test', () => { }); } + const defs = [TodoComponent.ngComponentDef]; + it('should support a basic component template', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, TodoComponent); + elementStart(0, 'todo'); elementEnd(); } } - expect(renderToHtml(Template, null)).toEqual('

    Todo one

    '); + expect(renderToHtml(Template, null, defs)).toEqual('

    Todo one

    '); }); it('should support a component template with sibling', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, TodoComponent); + elementStart(0, 'todo'); elementEnd(); text(1, 'two'); } } - expect(renderToHtml(Template, null)).toEqual('

    Todo one

    two'); + expect(renderToHtml(Template, null, defs)).toEqual('

    Todo one

    two'); }); it('should support a component template with component sibling', () => { @@ -223,13 +225,13 @@ describe('render3 integration test', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, TodoComponent); + elementStart(0, 'todo'); elementEnd(); - elementStart(1, TodoComponent); + elementStart(1, 'todo'); elementEnd(); } } - expect(renderToHtml(Template, null)) + expect(renderToHtml(Template, null, defs)) .toEqual('

    Todo one

    Todo one

    '); }); @@ -240,7 +242,7 @@ describe('render3 integration test', () => { title = 'one'; static ngComponentDef = defineComponent({ type: TodoComponentHostBinding, - tag: 'todo', + selector: [[['todo'], null]], template: function TodoComponentHostBindingTemplate( ctx: TodoComponentHostBinding, cm: boolean) { if (cm) { @@ -260,21 +262,22 @@ describe('render3 integration test', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, TodoComponentHostBinding); + elementStart(0, 'todo'); elementEnd(); } } - expect(renderToHtml(Template, {})).toEqual('one'); + const defs = [TodoComponentHostBinding.ngComponentDef]; + expect(renderToHtml(Template, {}, defs)).toEqual('one'); cmptInstance !.title = 'two'; - expect(renderToHtml(Template, {})).toEqual('two'); + expect(renderToHtml(Template, {}, defs)).toEqual('two'); }); it('should support root component with host attribute', () => { class HostAttributeComp { static ngComponentDef = defineComponent({ type: HostAttributeComp, - tag: 'host-attr-comp', + selector: [[['host-attr-comp'], null]], factory: () => new HostAttributeComp(), template: (ctx: HostAttributeComp, cm: boolean) => {}, attributes: ['role', 'button'] @@ -291,7 +294,7 @@ describe('render3 integration test', () => { name = 'Bess'; static ngComponentDef = defineComponent({ type: MyComp, - tag: 'comp', + selector: [[['comp'], null]], template: function MyCompTemplate(ctx: any, cm: boolean) { if (cm) { elementStart(0, 'p'); @@ -306,12 +309,13 @@ describe('render3 integration test', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'comp'); elementEnd(); } } - expect(renderToHtml(Template, null)).toEqual('

    Bess

    '); + expect(renderToHtml(Template, null, [MyComp.ngComponentDef])) + .toEqual('

    Bess

    '); }); it('should support a component with sub-views', () => { @@ -324,7 +328,7 @@ describe('render3 integration test', () => { condition: boolean; static ngComponentDef = defineComponent({ type: MyComp, - tag: 'comp', + selector: [[['comp'], null]], template: function MyCompTemplate(ctx: any, cm: boolean) { if (cm) { container(0); @@ -350,14 +354,16 @@ describe('render3 integration test', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'condition', bind(ctx.condition)); } - expect(renderToHtml(Template, {condition: true})).toEqual('
    text
    '); - expect(renderToHtml(Template, {condition: false})).toEqual(''); + const defs = [MyComp.ngComponentDef]; + expect(renderToHtml(Template, {condition: true}, defs)) + .toEqual('
    text
    '); + expect(renderToHtml(Template, {condition: false}, defs)).toEqual(''); }); @@ -428,7 +434,7 @@ describe('render3 integration test', () => { beforeTree: Tree; afterTree: Tree; static ngComponentDef = defineComponent({ - tag: 'child', + selector: [[['child'], null]], type: ChildComponent, template: function ChildComponentTemplate( ctx: {beforeTree: Tree, afterTree: Tree}, cm: boolean) { @@ -460,7 +466,7 @@ describe('render3 integration test', () => { function parentTemplate(ctx: ParentCtx, cm: boolean) { if (cm) { - elementStart(0, ChildComponent); + elementStart(0, 'child'); { container(1); } elementEnd(); } @@ -482,14 +488,15 @@ describe('render3 integration test', () => { projectedTree: {beforeLabel: 'p'}, afterTree: {afterLabel: 'z'} }; - expect(renderToHtml(parentTemplate, ctx)).toEqual('apz'); + const defs = [ChildComponent.ngComponentDef]; + expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('apz'); ctx.projectedTree = {subTrees: [{}, {}, {subTrees: [{}, {}]}, {}]}; ctx.beforeTree.subTrees !.push({afterLabel: 'b'}); - expect(renderToHtml(parentTemplate, ctx)).toEqual('abz'); + expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('abz'); ctx.projectedTree.subTrees ![1].afterLabel = 'h'; - expect(renderToHtml(parentTemplate, ctx)).toEqual('abhz'); + expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('abhz'); ctx.beforeTree.subTrees !.push({beforeLabel: 'c'}); - expect(renderToHtml(parentTemplate, ctx)).toEqual('abchz'); + expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('abchz'); // To check the context easily: // console.log(JSON.stringify(ctx)); @@ -633,6 +640,7 @@ describe('render3 integration test', () => { static ngDirectiveDef = defineDirective({ type: HostBindingDir, + selector: [[['', 'hostBindingDir', ''], null]], factory: function HostBindingDir_Factory() { return hostBindingDir = new HostBindingDir(); }, @@ -645,16 +653,17 @@ describe('render3 integration test', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['hostBindingDir', ''], [HostBindingDir]); + elementStart(0, 'div', ['hostBindingDir', '']); elementEnd(); } } - expect(renderToHtml(Template, {})) + const defs = [HostBindingDir.ngDirectiveDef]; + expect(renderToHtml(Template, {}, defs)) .toEqual(`
    `); hostBindingDir !.label = 'other label'; - expect(renderToHtml(Template, {})) + expect(renderToHtml(Template, {}, defs)) .toEqual(`
    `); }); }); diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index 554924201f..0a43440886 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -14,10 +14,10 @@ import {containerEl, renderComponent, renderToHtml, requestAnimationFrame} from describe('lifecycles', () => { - function getParentTemplate(type: any) { + function getParentTemplate(name: string) { return (ctx: any, cm: boolean) => { if (cm) { - elementStart(0, type); + elementStart(0, name); elementEnd(); } elementProperty(0, 'val', bind(ctx.val)); @@ -37,23 +37,25 @@ describe('lifecycles', () => { elementEnd(); } }); - let Parent = createOnInitComponent('parent', getParentTemplate(Comp)); + let Parent = createOnInitComponent('parent', getParentTemplate('comp'), [Comp.ngComponentDef]); let ProjectedComp = createOnInitComponent('projected', (ctx: any, cm: boolean) => { if (cm) { text(0, 'content'); } }); - function createOnInitComponent(name: string, template: ComponentTemplate) { + function createOnInitComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { val: string = ''; ngOnInit() { events.push(`${name}${this.val}`); } static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), - inputs: {val: 'val'}, template + inputs: {val: 'val'}, template, + directiveDefs: defs }); }; } @@ -61,24 +63,30 @@ describe('lifecycles', () => { class Directive { ngOnInit() { events.push('dir'); } - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()}); + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive()}); } + const defs = [ + Comp.ngComponentDef, Parent.ngComponentDef, ProjectedComp.ngComponentDef, + Directive.ngDirectiveDef + ]; + it('should call onInit method after inputs are set in creation mode (and not in update mode)', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', bind(ctx.val)); } - renderToHtml(Template, {val: '1'}); + renderToHtml(Template, {val: '1'}, defs); expect(events).toEqual(['comp1']); - renderToHtml(Template, {val: '2'}); + renderToHtml(Template, {val: '2'}, defs); expect(events).toEqual(['comp1']); }); @@ -99,12 +107,12 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['parent', 'comp']); }); @@ -118,16 +126,16 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); - elementStart(1, Parent); + elementStart(1, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); elementProperty(1, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['parent1', 'parent2', 'comp1', 'comp2']); }); @@ -147,7 +155,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } embeddedViewEnd(); @@ -156,50 +164,50 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp', 'comp']); }); it('should call onInit in hosts before their content children', () => { /** * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); - { elementStart(1, ProjectedComp); } + elementStart(0, 'comp'); + { elementStart(1, 'projected'); } elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'projected']); }); it('should call onInit in host and its content children before next host', () => { /** * - * + * * * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); - { elementStart(1, ProjectedComp); } + elementStart(0, 'comp'); + { elementStart(1, 'projected'); } elementEnd(); - elementStart(2, Comp); - { elementStart(3, ProjectedComp); } + elementStart(2, 'comp'); + { elementStart(3, 'projected'); } elementEnd(); } elementProperty(0, 'val', 1); @@ -208,7 +216,7 @@ describe('lifecycles', () => { elementProperty(3, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp1', 'projected1', 'comp2', 'projected2']); }); @@ -216,15 +224,15 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'dir']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'dir']); }); @@ -233,15 +241,15 @@ describe('lifecycles', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir']); }); @@ -256,10 +264,10 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); container(1); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); } elementProperty(0, 'val', 1); @@ -268,7 +276,7 @@ describe('lifecycles', () => { { for (let j = 2; j < 5; j++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', j); @@ -278,7 +286,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); // onInit is called top to bottom, so top level comps (1 and 5) are called // before the comps inside the for loop's embedded view (2, 3, and 4) @@ -296,10 +304,10 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); container(1); - elementStart(2, Parent); + elementStart(2, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); @@ -308,7 +316,7 @@ describe('lifecycles', () => { { for (let j = 2; j < 5; j++) { if (embeddedViewStart(0)) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } elementProperty(0, 'val', j); @@ -318,7 +326,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); // onInit is called top to bottom, so top level comps (1 and 5) are called // before the comps inside the for loop's embedded view (2, 3, and 4) @@ -340,9 +348,10 @@ describe('lifecycles', () => { }); let Comp = createDoCheckComponent('comp', (ctx: any, cm: boolean) => {}); - let Parent = createDoCheckComponent('parent', getParentTemplate(Comp)); + let Parent = createDoCheckComponent('parent', getParentTemplate('comp'), [Comp.ngComponentDef]); - function createDoCheckComponent(name: string, template: ComponentTemplate) { + function createDoCheckComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { ngDoCheck() { events.push(name); @@ -351,30 +360,37 @@ describe('lifecycles', () => { ngOnInit() { allEvents.push('init ' + name); } - static ngComponentDef = - defineComponent({type: Component, tag: name, factory: () => new Component(), template}); + static ngComponentDef = defineComponent({ + type: Component, + selector: [[[name], null]], + factory: () => new Component(), template, + directiveDefs: defs + }); }; } class Directive { ngDoCheck() { events.push('dir'); } - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()}); + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive()}); } + const defs = [Comp.ngComponentDef, Parent.ngComponentDef, Directive.ngDirectiveDef]; + it('should call doCheck on every refresh', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'comp']); }); @@ -395,12 +411,12 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['parent', 'comp']); }); @@ -408,15 +424,15 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['init comp', 'check comp']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['init comp', 'check comp', 'check comp']); }); @@ -424,15 +440,15 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'dir']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'dir', 'comp', 'dir']); }); @@ -441,15 +457,15 @@ describe('lifecycles', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir', 'dir']); }); @@ -474,12 +490,12 @@ describe('lifecycles', () => { let Parent = createAfterContentInitComp('parent', function(ctx: any, cm: boolean) { if (cm) { projectionDef(0); - elementStart(1, Comp); + elementStart(1, 'comp'); { projection(2, 0); } elementEnd(); } elementProperty(1, 'val', bind(ctx.val)); - }); + }, [Comp.ngComponentDef]); let ProjectedComp = createAfterContentInitComp('projected', (ctx: any, cm: boolean) => { if (cm) { @@ -488,7 +504,8 @@ describe('lifecycles', () => { } }); - function createAfterContentInitComp(name: string, template: ComponentTemplate) { + function createAfterContentInitComp( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { val: string = ''; ngAfterContentInit() { @@ -499,28 +516,69 @@ describe('lifecycles', () => { static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), inputs: {val: 'val'}, template: template, + directiveDefs: defs }); }; } + class Directive { + ngAfterContentInit() { events.push('init'); } + ngAfterContentChecked() { events.push('check'); } + + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive()}); + } + + function ForLoopWithChildrenTemplate(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'parent'); + { text(1, 'content'); } + elementEnd(); + container(2); + elementStart(3, 'parent'); + { text(4, 'content'); } + elementEnd(); + } + elementProperty(0, 'val', 1); + elementProperty(3, 'val', 4); + containerRefreshStart(2); + { + for (let i = 2; i < 4; i++) { + if (embeddedViewStart(0)) { + elementStart(0, 'parent'); + { text(1, 'content'); } + elementEnd(); + } + elementProperty(0, 'val', i); + embeddedViewEnd(); + } + } + containerRefreshEnd(); + } + + const defs = [ + Comp.ngComponentDef, Parent.ngComponentDef, ProjectedComp.ngComponentDef, + Directive.ngDirectiveDef + ]; + it('should be called only in creation mode', () => { /** content */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { text(1, 'content'); } elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp']); }); @@ -547,7 +605,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); { text(1, 'content'); } elementEnd(); } @@ -557,13 +615,13 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp', 'comp']); }); @@ -575,13 +633,13 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); { text(1, 'content'); } elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['parent', 'comp']); }); @@ -594,10 +652,10 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); { text(1, 'content'); } elementEnd(); - elementStart(2, Parent); + elementStart(2, 'parent'); { text(3, 'content'); } elementEnd(); } @@ -605,14 +663,14 @@ describe('lifecycles', () => { elementProperty(2, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['parent1', 'parent2', 'comp1', 'comp2']); }); it('should be called in projected components before their hosts', () => { /** * - * content + * content * * * parent template: @@ -622,9 +680,9 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); { text(2, 'content'); } elementEnd(); } @@ -632,17 +690,17 @@ describe('lifecycles', () => { } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['projected', 'parent', 'comp']); }); it('should be called in projected components and hosts before children', () => { /** * - * content + * content * * * - * content + * content * * * parent template: @@ -652,16 +710,16 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); { text(2, 'content'); } elementEnd(); } elementEnd(); - elementStart(3, Parent); + elementStart(3, 'parent'); { - elementStart(4, ProjectedComp); + elementStart(4, 'projected'); { text(5, 'content'); } elementEnd(); } @@ -673,7 +731,7 @@ describe('lifecycles', () => { elementProperty(4, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['projected1', 'parent1', 'projected2', 'parent2', 'comp1', 'comp2']); }); @@ -687,11 +745,11 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { text(1, 'content'); } elementEnd(); container(2); - elementStart(3, Comp); + elementStart(3, 'comp'); { text(4, 'content'); } elementEnd(); } @@ -701,7 +759,7 @@ describe('lifecycles', () => { { for (let i = 2; i < 4; i++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); { text(1, 'content'); } elementEnd(); } @@ -712,37 +770,10 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']); }); - function ForLoopWithChildrenTemplate(ctx: any, cm: boolean) { - if (cm) { - elementStart(0, Parent); - { text(1, 'content'); } - elementEnd(); - container(2); - elementStart(3, Parent); - { text(4, 'content'); } - elementEnd(); - } - elementProperty(0, 'val', 1); - elementProperty(3, 'val', 4); - containerRefreshStart(2); - { - for (let i = 2; i < 4; i++) { - if (embeddedViewStart(0)) { - elementStart(0, Parent); - { text(1, 'content'); } - elementEnd(); - } - elementProperty(0, 'val', i); - embeddedViewEnd(); - } - } - containerRefreshEnd(); - } - it('should be called in correct order in a for loop with children', () => { /** * content @@ -752,7 +783,7 @@ describe('lifecycles', () => { * content */ - renderToHtml(ForLoopWithChildrenTemplate, {}); + renderToHtml(ForLoopWithChildrenTemplate, {}, defs); expect(events).toEqual( ['parent2', 'comp2', 'parent3', 'comp3', 'parent1', 'parent4', 'comp1', 'comp4']); }); @@ -763,16 +794,16 @@ describe('lifecycles', () => { /** content */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { text(1, 'content'); } elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['comp init', 'comp check']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); }); @@ -789,23 +820,16 @@ describe('lifecycles', () => { }); describe('directives', () => { - class Directive { - ngAfterContentInit() { events.push('init'); } - ngAfterContentChecked() { events.push('check'); } - - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()}); - } - it('should be called on directives after component', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'init', 'check']); }); @@ -813,12 +837,12 @@ describe('lifecycles', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['init', 'check']); }); }); @@ -841,7 +865,8 @@ describe('lifecycles', () => { elementEnd(); } }); - let Parent = createAfterViewInitComponent('parent', getParentTemplate(Comp)); + let Parent = + createAfterViewInitComponent('parent', getParentTemplate('comp'), [Comp.ngComponentDef]); let ProjectedComp = createAfterViewInitComponent('projected', (ctx: any, cm: boolean) => { if (cm) { @@ -849,7 +874,8 @@ describe('lifecycles', () => { } }); - function createAfterViewInitComponent(name: string, template: ComponentTemplate) { + function createAfterViewInitComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { val: string = ''; ngAfterViewInit() { @@ -860,27 +886,41 @@ describe('lifecycles', () => { static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), inputs: {val: 'val'}, - template: template + template: template, + directiveDefs: defs }); }; } + class Directive { + ngAfterViewInit() { events.push('init'); } + ngAfterViewChecked() { events.push('check'); } + + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive()}); + } + + const defs = [ + Comp.ngComponentDef, Parent.ngComponentDef, ProjectedComp.ngComponentDef, + Directive.ngDirectiveDef + ]; + it('should be called on init and not in update mode', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp']); }); @@ -907,7 +947,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } embeddedViewEnd(); @@ -916,13 +956,13 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp']); - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp', 'comp']); }); @@ -935,12 +975,12 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'parent']); }); @@ -954,15 +994,15 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); - elementStart(1, Parent); + elementStart(1, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); elementProperty(1, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp1', 'comp2', 'parent1', 'parent2']); }); @@ -970,44 +1010,44 @@ describe('lifecycles', () => { it('should be called in projected components before their hosts', () => { /** * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); elementEnd(); } elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['projected', 'comp']); }); it('should call afterViewInit in content children and host before next host', () => { /** * - * + * * * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); elementEnd(); } elementEnd(); - elementStart(2, Comp); + elementStart(2, 'comp'); { - elementStart(3, ProjectedComp); + elementStart(3, 'projected'); elementEnd(); } elementEnd(); @@ -1018,28 +1058,28 @@ describe('lifecycles', () => { elementProperty(3, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2']); }); it('should call afterViewInit in content children and hosts before parents', () => { /* * - * + * * */ const ParentComp = createAfterViewInitComponent('parent', (ctx: any, cm: boolean) => { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); elementEnd(); } elementEnd(); } elementProperty(0, 'val', bind(ctx.val)); elementProperty(1, 'val', bind(ctx.val)); - }); + }, [Comp.ngComponentDef, ProjectedComp.ngComponentDef]); /** * @@ -1047,16 +1087,16 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ParentComp); + elementStart(0, 'parent'); elementEnd(); - elementStart(1, ParentComp); + elementStart(1, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); elementProperty(1, 'val', 2); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, [ParentComp.ngComponentDef]); expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2', 'parent1', 'parent2']); }); @@ -1070,10 +1110,10 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); container(1); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); } elementProperty(0, 'val', 1); @@ -1082,7 +1122,7 @@ describe('lifecycles', () => { { for (let i = 2; i < 4; i++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', i); @@ -1092,7 +1132,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']); }); @@ -1107,10 +1147,10 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); container(1); - elementStart(2, Parent); + elementStart(2, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); @@ -1119,7 +1159,7 @@ describe('lifecycles', () => { { for (let i = 2; i < 4; i++) { if (embeddedViewStart(0)) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } elementProperty(0, 'val', i); @@ -1129,7 +1169,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual( ['comp2', 'parent2', 'comp3', 'parent3', 'comp1', 'comp4', 'parent1', 'parent4']); @@ -1141,15 +1181,15 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['comp init', 'comp check']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); }); @@ -1166,16 +1206,16 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', bind(ctx.myVal)); } - renderToHtml(Template, {myVal: 5}); + renderToHtml(Template, {myVal: 5}, defs); expect(allEvents).toEqual(['comp5 init', 'comp5 check']); - renderToHtml(Template, {myVal: 6}); + renderToHtml(Template, {myVal: 6}, defs); expect(allEvents).toEqual(['comp5 init', 'comp5 check', 'comp6 check']); }); @@ -1189,10 +1229,10 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); container(1); - elementStart(2, Parent); + elementStart(2, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); @@ -1201,7 +1241,7 @@ describe('lifecycles', () => { { for (let i = 2; i < 4; i++) { if (embeddedViewStart(0)) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } elementProperty(0, 'val', i); @@ -1211,7 +1251,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(allEvents).toEqual([ 'comp2 init', 'comp2 check', 'parent2 init', 'parent2 check', 'comp3 init', 'comp3 check', 'parent3 init', 'parent3 check', 'comp1 init', 'comp1 check', 'comp4 init', 'comp4 check', @@ -1223,23 +1263,16 @@ describe('lifecycles', () => { }); describe('directives', () => { - class Directive { - ngAfterViewInit() { events.push('init'); } - ngAfterViewChecked() { events.push('check'); } - - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()}); - } - it('should be called on directives after component', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['comp', 'init', 'check']); }); @@ -1247,12 +1280,12 @@ describe('lifecycles', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['init', 'check']); }); }); @@ -1269,29 +1302,47 @@ describe('lifecycles', () => { projection(1, 0); } }); - let Parent = createOnDestroyComponent('parent', getParentTemplate(Comp)); + let Parent = + createOnDestroyComponent('parent', getParentTemplate('comp'), [Comp.ngComponentDef]); - function createOnDestroyComponent(name: string, template: ComponentTemplate) { + function createOnDestroyComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { val: string = ''; ngOnDestroy() { events.push(`${name}${this.val}`); } static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), inputs: {val: 'val'}, - template: template + template: template, + directiveDefs: defs }); }; } + let Grandparent = createOnDestroyComponent('grandparent', function(ctx: any, cm: boolean) { + if (cm) { + elementStart(0, 'parent'); + elementEnd(); + } + }, [Parent.ngComponentDef]); + + const ProjectedComp = createOnDestroyComponent('projected', (ctx: any, cm: boolean) => {}); + class Directive { ngOnDestroy() { events.push('dir'); } - static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()}); + static ngDirectiveDef = defineDirective( + {type: Directive, selector: [[['', 'dir', ''], null]], factory: () => new Directive()}); } + const defs = [ + Comp.ngComponentDef, Parent.ngComponentDef, Grandparent.ngComponentDef, + ProjectedComp.ngComponentDef, Directive.ngDirectiveDef + ]; + it('should call destroy when view is removed', () => { /** * % if (condition) { @@ -1307,7 +1358,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } embeddedViewEnd(); @@ -1316,8 +1367,8 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp']); }); @@ -1337,9 +1388,9 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); - elementStart(1, Comp); + elementStart(1, 'comp'); elementEnd(); } elementProperty(0, 'val', bind('1')); @@ -1350,8 +1401,8 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp1', 'comp2']); }); @@ -1372,7 +1423,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } embeddedViewEnd(); @@ -1381,8 +1432,8 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp', 'parent']); }); @@ -1396,13 +1447,6 @@ describe('lifecycles', () => { * parent template: */ - let Grandparent = createOnDestroyComponent('grandparent', function(ctx: any, cm: boolean) { - if (cm) { - elementStart(0, Parent); - elementEnd(); - } - }); - function Template(ctx: any, cm: boolean) { if (cm) { container(0); @@ -1411,7 +1455,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Grandparent); + elementStart(0, 'grandparent'); elementEnd(); } embeddedViewEnd(); @@ -1420,21 +1464,19 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp', 'parent', 'grandparent']); }); it('should be called in projected components before their hosts', () => { - const ProjectedComp = createOnDestroyComponent('projected', (ctx: any, cm: boolean) => {}); - /** * % if (showing) { * - * + * * * - * + * * * } */ @@ -1446,15 +1488,15 @@ describe('lifecycles', () => { { if (ctx.showing) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); { - elementStart(1, ProjectedComp); + elementStart(1, 'projected'); elementEnd(); } elementEnd(); - elementStart(2, Comp); + elementStart(2, 'comp'); { - elementStart(3, ProjectedComp); + elementStart(3, 'projected'); elementEnd(); } elementEnd(); @@ -1469,8 +1511,8 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {showing: true}); - renderToHtml(Template, {showing: false}); + renderToHtml(Template, {showing: true}, defs); + renderToHtml(Template, {showing: false}, defs); expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2']); }); @@ -1495,10 +1537,10 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); container(1); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); } elementProperty(0, 'val', bind('1')); @@ -1507,7 +1549,7 @@ describe('lifecycles', () => { { if (ctx.condition2) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', bind('2')); @@ -1521,8 +1563,8 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true, condition2: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, condition2: true}, defs); + renderToHtml(Template, {condition: false}, defs); /** * Current angular will process in this same order (root is the top-level removed view): @@ -1537,13 +1579,13 @@ describe('lifecycles', () => { expect(events).toEqual(['comp2', 'comp1', 'comp3']); events = []; - renderToHtml(Template, {condition: true, condition2: false}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, condition2: false}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp1', 'comp3']); events = []; - renderToHtml(Template, {condition: true, condition2: true}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, condition2: true}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp2', 'comp1', 'comp3']); }); @@ -1565,10 +1607,10 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); container(1); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); } elementProperty(0, 'val', bind('1')); @@ -1577,7 +1619,7 @@ describe('lifecycles', () => { { for (let j = 2; j < ctx.len; j++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', bind(j)); @@ -1605,18 +1647,18 @@ describe('lifecycles', () => { * embeddedView.next.next -> container -> root * root onDestroy: [comp1, comp5] */ - renderToHtml(Template, {condition: true, len: 5}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, len: 5}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp2', 'comp3', 'comp4', 'comp1', 'comp5']); events = []; - renderToHtml(Template, {condition: true, len: 4}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, len: 4}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp5']); events = []; - renderToHtml(Template, {condition: true, len: 5}); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: true, len: 5}, defs); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp2', 'comp3', 'comp4', 'comp1', 'comp5']); }); @@ -1643,7 +1685,7 @@ describe('lifecycles', () => { text(1, 'Click me'); } elementEnd(); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); elementStart(3, 'button'); { @@ -1665,7 +1707,7 @@ describe('lifecycles', () => { } const ctx: {counter: number} = new App(); - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, defs); const buttons = containerEl.querySelectorAll('button') !; buttons[0].click(); @@ -1673,7 +1715,7 @@ describe('lifecycles', () => { buttons[1].click(); expect(ctx.counter).toEqual(2); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); buttons[0].click(); buttons[1].click(); @@ -1696,7 +1738,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } embeddedViewEnd(); @@ -1705,10 +1747,10 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual([]); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp', 'dir']); }); @@ -1728,7 +1770,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } embeddedViewEnd(); @@ -1737,10 +1779,10 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual([]); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['dir']); }); @@ -1761,12 +1803,12 @@ describe('lifecycles', () => { }); const Parent = createOnChangesComponent('parent', (ctx: any, cm: boolean) => { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val1', bind(ctx.a)); elementProperty(0, 'publicName', bind(ctx.b)); - }); + }, [Comp.ngComponentDef]); const ProjectedComp = createOnChangesComponent('projected', (ctx: any, cm: boolean) => { if (cm) { text(0, 'content'); @@ -1774,7 +1816,8 @@ describe('lifecycles', () => { }); - function createOnChangesComponent(name: string, template: ComponentTemplate) { + function createOnChangesComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { // @Input() val1: string; // @Input('publicName') val2: string; @@ -1787,10 +1830,11 @@ describe('lifecycles', () => { static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), features: [NgOnChangesFeature({b: 'val2'})], - inputs: {a: 'val1', b: 'publicName'}, template + inputs: {a: 'val1', b: 'publicName'}, template, + directiveDefs: defs }); }; } @@ -1807,27 +1851,33 @@ describe('lifecycles', () => { static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', 'dir', ''], null]], factory: () => new Directive(), features: [NgOnChangesFeature({b: 'val2'})], inputs: {a: 'val1', b: 'publicName'} }); } + const defs = [ + Comp.ngComponentDef, Parent.ngComponentDef, Directive.ngDirectiveDef, + ProjectedComp.ngComponentDef + ]; + it('should call onChanges method after inputs are set in creation and update mode', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val1', bind(ctx.val1)); elementProperty(0, 'publicName', bind(ctx.val2)); } - renderToHtml(Template, {val1: '1', val2: 'a'}); + renderToHtml(Template, {val1: '1', val2: 'a'}, defs); expect(events).toEqual(['comp=comp val1=1 val2=a - changed=[val1,val2]']); - renderToHtml(Template, {val1: '2', val2: 'b'}); + renderToHtml(Template, {val1: '2', val2: 'b'}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=a - changed=[val1,val2]', 'comp=comp val1=2 val2=b - changed=[val1,val2]' @@ -1842,14 +1892,14 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } elementProperty(0, 'val1', bind(ctx.val1)); elementProperty(0, 'publicName', bind(ctx.val2)); } - renderToHtml(Template, {val1: '1', val2: 'a'}); + renderToHtml(Template, {val1: '1', val2: 'a'}, defs); expect(events).toEqual([ 'comp=parent val1=1 val2=a - changed=[val1,val2]', 'comp=comp val1=1 val2=a - changed=[val1,val2]' @@ -1866,9 +1916,9 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); - elementStart(1, Parent); + elementStart(1, 'parent'); elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -1877,7 +1927,7 @@ describe('lifecycles', () => { elementProperty(1, 'publicName', bind(2)); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'comp=parent val1=1 val2=1 - changed=[val1,val2]', 'comp=parent val1=2 val2=2 - changed=[val1,val2]', @@ -1902,7 +1952,7 @@ describe('lifecycles', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -1913,13 +1963,13 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual(['comp=comp val1=1 val2=1 - changed=[val1,val2]']); - renderToHtml(Template, {condition: false}); + renderToHtml(Template, {condition: false}, defs); expect(events).toEqual(['comp=comp val1=1 val2=1 - changed=[val1,val2]']); - renderToHtml(Template, {condition: true}); + renderToHtml(Template, {condition: true}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=1 - changed=[val1,val2]', 'comp=comp val1=1 val2=1 - changed=[val1,val2]' @@ -1929,13 +1979,13 @@ describe('lifecycles', () => { it('should call onChanges in hosts before their content children', () => { /** * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); - { elementStart(1, ProjectedComp); } + elementStart(0, 'comp'); + { elementStart(1, 'projected'); } elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -1944,7 +1994,7 @@ describe('lifecycles', () => { elementProperty(1, 'publicName', bind(2)); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=1 - changed=[val1,val2]', 'comp=projected val1=2 val2=2 - changed=[val1,val2]' @@ -1954,19 +2004,19 @@ describe('lifecycles', () => { it('should call onChanges in host and its content children before next host', () => { /** * - * + * * * - * + * * */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); - { elementStart(1, ProjectedComp); } + elementStart(0, 'comp'); + { elementStart(1, 'projected'); } elementEnd(); - elementStart(2, Comp); - { elementStart(3, ProjectedComp); } + elementStart(2, 'comp'); + { elementStart(3, 'projected'); } elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -1979,7 +2029,7 @@ describe('lifecycles', () => { elementProperty(3, 'publicName', bind(4)); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=1 - changed=[val1,val2]', 'comp=projected val1=2 val2=2 - changed=[val1,val2]', @@ -1992,19 +2042,19 @@ describe('lifecycles', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp, null, [Directive]); + elementStart(0, 'comp', ['dir', '']); elementEnd(); } elementProperty(0, 'val1', bind(1)); elementProperty(0, 'publicName', bind(1)); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=1 - changed=[val1,val2]', 'dir - val1=1 val2=1 - changed=[val1,val2]' ]); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'comp=comp val1=1 val2=1 - changed=[val1,val2]', 'dir - val1=1 val2=1 - changed=[val1,val2]' ]); @@ -2015,17 +2065,17 @@ describe('lifecycles', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [Directive]); + elementStart(0, 'div', ['dir', '']); elementEnd(); } elementProperty(0, 'val1', bind(1)); elementProperty(0, 'publicName', bind(1)); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir - val1=1 val2=1 - changed=[val1,val2]']); - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual(['dir - val1=1 val2=1 - changed=[val1,val2]']); }); @@ -2040,10 +2090,10 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); container(1); - elementStart(2, Comp); + elementStart(2, 'comp'); elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -2054,7 +2104,7 @@ describe('lifecycles', () => { { for (let j = 2; j < 5; j++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val1', bind(j)); @@ -2065,7 +2115,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); // onChanges is called top to bottom, so top level comps (1 and 5) are called // before the comps inside the for loop's embedded view (2, 3, and 4) @@ -2089,10 +2139,10 @@ describe('lifecycles', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); container(1); - elementStart(2, Parent); + elementStart(2, 'parent'); elementEnd(); } elementProperty(0, 'val1', bind(1)); @@ -2103,7 +2153,7 @@ describe('lifecycles', () => { { for (let j = 2; j < 5; j++) { if (embeddedViewStart(0)) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); } elementProperty(0, 'val1', bind(j)); @@ -2114,7 +2164,7 @@ describe('lifecycles', () => { containerRefreshEnd(); } - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); // onChanges is called top to bottom, so top level comps (1 and 5) are called // before the comps inside the for loop's embedded view (2, 3, and 4) @@ -2139,7 +2189,8 @@ describe('lifecycles', () => { beforeEach(() => { events = []; }); - function createAllHooksComponent(name: string, template: ComponentTemplate) { + function createAllHooksComponent( + name: string, template: ComponentTemplate, defs: any[] = []) { return class Component { val: string = ''; @@ -2156,10 +2207,11 @@ describe('lifecycles', () => { static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component(), inputs: {val: 'val'}, template, - features: [NgOnChangesFeature()] + features: [NgOnChangesFeature()], + directiveDefs: defs }); }; } @@ -2174,16 +2226,17 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); - elementStart(1, Comp); + elementStart(1, 'comp'); elementEnd(); } elementProperty(0, 'val', 1); elementProperty(1, 'val', 2); } - renderToHtml(Template, {}); + const defs = [Comp.ngComponentDef]; + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'changes comp1', 'init comp1', 'check comp1', 'changes comp2', 'init comp2', 'check comp2', 'contentInit comp1', 'contentCheck comp1', 'contentInit comp2', 'contentCheck comp2', @@ -2191,7 +2244,7 @@ describe('lifecycles', () => { ]); events = []; - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'changes comp1', 'check comp1', 'changes comp2', 'check comp2', 'contentCheck comp1', 'contentCheck comp2', 'viewCheck comp1', 'viewCheck comp2' @@ -2204,11 +2257,11 @@ describe('lifecycles', () => { /** */ const Parent = createAllHooksComponent('parent', (ctx: any, cm: boolean) => { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'val', bind(ctx.val)); - }); + }, [Comp.ngComponentDef]); /** * @@ -2216,16 +2269,17 @@ describe('lifecycles', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Parent); + elementStart(0, 'parent'); elementEnd(); - elementStart(1, Parent); + elementStart(1, 'parent'); elementEnd(); } elementProperty(0, 'val', 1); elementProperty(1, 'val', 2); } - renderToHtml(Template, {}); + const defs = [Parent.ngComponentDef]; + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'changes parent1', 'init parent1', 'check parent1', 'changes parent2', 'init parent2', 'check parent2', @@ -2240,7 +2294,7 @@ describe('lifecycles', () => { ]); events = []; - renderToHtml(Template, {}); + renderToHtml(Template, {}, defs); expect(events).toEqual([ 'changes parent1', 'check parent1', 'changes parent2', 'check parent2', 'contentCheck parent1', 'contentCheck parent2', 'check comp1', 'contentCheck comp1', diff --git a/packages/core/test/render3/listeners_spec.ts b/packages/core/test/render3/listeners_spec.ts index 46318eb85d..5f7b9b9278 100644 --- a/packages/core/test/render3/listeners_spec.ts +++ b/packages/core/test/render3/listeners_spec.ts @@ -24,7 +24,7 @@ describe('event listeners', () => { static ngComponentDef = defineComponent({ type: MyComp, - tag: 'comp', + selector: [[['comp'], null]], /** */ template: function CompTemplate(ctx: any, cm: boolean) { if (cm) { @@ -61,7 +61,7 @@ describe('event listeners', () => { static ngComponentDef = defineComponent({ type: PreventDefaultComp, - tag: 'prevent-default-comp', + selector: [[['prevent-default-comp'], null]], factory: () => new PreventDefaultComp(), /** */ template: (ctx: PreventDefaultComp, cm: boolean) => { @@ -235,6 +235,7 @@ describe('event listeners', () => { static ngDirectiveDef = defineDirective({ type: HostListenerDir, + selector: [[['', 'hostListenerDir', ''], null]], factory: function HostListenerDir_Factory() { const $dir$ = new HostListenerDir(); listener('click', function() { return $dir$.onClick(); }); @@ -245,13 +246,13 @@ describe('event listeners', () => { function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', ['hostListenerDir', ''], [HostListenerDir]); + elementStart(0, 'button', ['hostListenerDir', '']); text(1, 'Click'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, [HostListenerDir.ngDirectiveDef]); const button = containerEl.querySelector('button') !; button.click(); expect(events).toEqual(['click!']); @@ -337,9 +338,9 @@ describe('event listeners', () => { if (ctx.showing) { if (embeddedViewStart(0)) { text(0, 'Hello'); - elementStart(1, MyComp); + elementStart(1, 'comp'); elementEnd(); - elementStart(2, MyComp); + elementStart(2, 'comp'); elementEnd(); } embeddedViewEnd(); @@ -349,7 +350,7 @@ describe('event listeners', () => { } const ctx = {showing: true}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, [MyComp.ngComponentDef]); const buttons = containerEl.querySelectorAll('button') !; buttons[0].click(); @@ -360,7 +361,7 @@ describe('event listeners', () => { // the child view listener should be removed when the parent view is removed ctx.showing = false; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, [MyComp.ngComponentDef]); buttons[0].click(); buttons[1].click(); expect(comps[0] !.counter).toEqual(1); diff --git a/packages/core/test/render3/outputs_spec.ts b/packages/core/test/render3/outputs_spec.ts index 60e9733b3c..8fc31f2949 100644 --- a/packages/core/test/render3/outputs_spec.ts +++ b/packages/core/test/render3/outputs_spec.ts @@ -15,6 +15,8 @@ import {containerEl, renderToHtml} from './render_util'; describe('outputs', () => { let buttonToggle: ButtonToggle; + let destroyComp: DestroyComp; + let buttonDir: MyButton; class ButtonToggle { change = new EventEmitter(); @@ -22,7 +24,7 @@ describe('outputs', () => { static ngComponentDef = defineComponent({ type: ButtonToggle, - tag: 'button-toggle', + selector: [[['button-toggle'], null]], template: function(ctx: any, cm: boolean) {}, factory: () => buttonToggle = new ButtonToggle(), outputs: {change: 'change', resetStream: 'reset'} @@ -36,16 +38,47 @@ describe('outputs', () => { static ngDirectiveDef = defineDirective({ type: OtherDir, + selector: [[['', 'otherDir', ''], null]], factory: () => otherDir = new OtherDir, outputs: {changeStream: 'change'} }); } + class DestroyComp { + events: string[] = []; + ngOnDestroy() { this.events.push('destroy'); } + + static ngComponentDef = defineComponent({ + type: DestroyComp, + selector: [[['destroy-comp'], null]], + template: function(ctx: any, cm: boolean) {}, + factory: () => destroyComp = new DestroyComp() + }); + } + + /** */ + class MyButton { + click = new EventEmitter(); + + static ngDirectiveDef = defineDirective({ + type: MyButton, + selector: [[['', 'myButton', ''], null]], + factory: () => buttonDir = new MyButton, + outputs: {click: 'click'} + }); + } + + + const deps = [ + ButtonToggle.ngComponentDef, OtherDir.ngDirectiveDef, DestroyComp.ngComponentDef, + MyButton.ngDirectiveDef + ]; + it('should call component output function when event is emitted', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); } @@ -55,7 +88,7 @@ describe('outputs', () => { let counter = 0; const ctx = {onChange: () => counter++}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); @@ -68,7 +101,7 @@ describe('outputs', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); listener('reset', function() { return ctx.onReset(); }); @@ -80,7 +113,7 @@ describe('outputs', () => { let counter = 0; let resetCounter = 0; const ctx = {onChange: () => counter++, onReset: () => resetCounter++}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); @@ -93,7 +126,7 @@ describe('outputs', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.counter++; }); } @@ -102,7 +135,7 @@ describe('outputs', () => { } const ctx = {counter: 0}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(ctx.counter).toEqual(1); @@ -127,7 +160,7 @@ describe('outputs', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); } @@ -141,13 +174,13 @@ describe('outputs', () => { let counter = 0; const ctx = {onChange: () => counter++, condition: true}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); ctx.condition = false; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); @@ -177,7 +210,7 @@ describe('outputs', () => { { if (ctx.condition2) { if (embeddedViewStart(0)) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); } @@ -195,33 +228,19 @@ describe('outputs', () => { let counter = 0; const ctx = {onChange: () => counter++, condition: true, condition2: true}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); ctx.condition = false; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); }); it('should work properly when view also has listeners and destroys', () => { - let destroyComp: DestroyComp; - - class DestroyComp { - events: string[] = []; - ngOnDestroy() { this.events.push('destroy'); } - - static ngComponentDef = defineComponent({ - type: DestroyComp, - tag: 'destroy-comp', - template: function(ctx: any, cm: boolean) {}, - factory: () => destroyComp = new DestroyComp() - }); - } - /** * % if (condition) { * @@ -243,12 +262,12 @@ describe('outputs', () => { text(1, 'Click me'); } elementEnd(); - elementStart(2, ButtonToggle); + elementStart(2, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); } elementEnd(); - elementStart(3, DestroyComp); + elementStart(3, 'destroy-comp'); elementEnd(); } embeddedViewEnd(); @@ -260,7 +279,7 @@ describe('outputs', () => { let clickCounter = 0; let changeCounter = 0; const ctx = {condition: true, onChange: () => changeCounter++, onClick: () => clickCounter++}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(changeCounter).toEqual(1); @@ -272,7 +291,7 @@ describe('outputs', () => { expect(clickCounter).toEqual(1); ctx.condition = false; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); expect(destroyComp !.events).toEqual(['destroy']); @@ -283,19 +302,9 @@ describe('outputs', () => { }); it('should fire event listeners along with outputs if they match', () => { - let buttonDir: MyButton; - - /** */ - class MyButton { - click = new EventEmitter(); - - static ngDirectiveDef = defineDirective( - {type: MyButton, factory: () => buttonDir = new MyButton, outputs: {click: 'click'}}); - } - function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [MyButton]); + elementStart(0, 'button', ['myButton', '']); { listener('click', function() { return ctx.onClick(); }); } @@ -304,7 +313,7 @@ describe('outputs', () => { } let counter = 0; - renderToHtml(Template, {counter, onClick: () => counter++}); + renderToHtml(Template, {counter, onClick: () => counter++}, deps); // To match current Angular behavior, the click listener is still // set up in addition to any matching outputs. @@ -320,7 +329,7 @@ describe('outputs', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ButtonToggle, null, [OtherDir]); + elementStart(0, 'button-toggle', ['otherDir', '']); { listener('change', function() { return ctx.onChange(); }); } @@ -329,7 +338,7 @@ describe('outputs', () => { } let counter = 0; - renderToHtml(Template, {counter, onChange: () => counter++}); + renderToHtml(Template, {counter, onChange: () => counter++}, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); @@ -339,19 +348,23 @@ describe('outputs', () => { }); it('should work with an input and output of the same name', () => { - let otherDir: OtherDir; + let otherDir: OtherChangeDir; - class OtherDir { + class OtherChangeDir { change: boolean; - static ngDirectiveDef = defineDirective( - {type: OtherDir, factory: () => otherDir = new OtherDir, inputs: {change: 'change'}}); + static ngDirectiveDef = defineDirective({ + type: OtherChangeDir, + selector: [[['', 'otherChangeDir', ''], null]], + factory: () => otherDir = new OtherChangeDir, + inputs: {change: 'change'} + }); } - /** */ + /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ButtonToggle, null, [OtherDir]); + elementStart(0, 'button-toggle', ['otherChangeDir', '']); { listener('change', function() { return ctx.onChange(); }); } @@ -361,10 +374,11 @@ describe('outputs', () => { } let counter = 0; - renderToHtml(Template, {counter, onChange: () => counter++, change: true}); + const deps = [ButtonToggle.ngComponentDef, OtherChangeDir.ngDirectiveDef]; + renderToHtml(Template, {counter, onChange: () => counter++, change: true}, deps); expect(otherDir !.change).toEqual(true); - renderToHtml(Template, {counter, onChange: () => counter++, change: false}); + renderToHtml(Template, {counter, onChange: () => counter++, change: false}, deps); expect(otherDir !.change).toEqual(false); buttonToggle !.change.next(); @@ -395,7 +409,7 @@ describe('outputs', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, ButtonToggle); + elementStart(0, 'button-toggle'); { listener('change', function() { return ctx.onChange(); }); } @@ -404,7 +418,7 @@ describe('outputs', () => { embeddedViewEnd(); } else { if (embeddedViewStart(1)) { - elementStart(0, 'div', null, [OtherDir]); + elementStart(0, 'div', ['otherDir', '']); { listener('change', function() { return ctx.onChange(); }); } @@ -418,13 +432,13 @@ describe('outputs', () => { let counter = 0; const ctx = {condition: true, onChange: () => counter++, onClick: () => {}}; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); buttonToggle !.change.next(); expect(counter).toEqual(1); ctx.condition = false; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); expect(counter).toEqual(1); otherDir !.changeStream.next(); diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts index 0db4dff3d9..aa4b51ca14 100644 --- a/packages/core/test/render3/pipe_spec.ts +++ b/packages/core/test/render3/pipe_spec.ts @@ -6,15 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform, SimpleChange, SimpleChanges, WrappedValue} from '@angular/core'; +import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform} from '@angular/core'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {NgOnChangesFeature, defineComponent, defineDirective, definePipe} from '../../src/render3/definition'; +import {defineDirective, definePipe} from '../../src/render3/definition'; import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, text, textBinding} from '../../src/render3/instructions'; import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe'; import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2'; -import {renderComponent, renderToHtml} from './render_util'; +import {renderToHtml} from './render_util'; let log: string[] = []; @@ -51,8 +51,12 @@ describe('pipe', () => { constructor() { this.dirProp = ''; } - static ngDirectiveDef = - defineDirective({type: MyDir, factory: () => new MyDir(), inputs: {dirProp: 'elprop'}}); + static ngDirectiveDef = defineDirective({ + type: MyDir, + selector: [[['', 'myDir', ''], null]], + factory: () => new MyDir(), + inputs: {dirProp: 'elprop'} + }); } @Pipe({name: 'double'}) @@ -67,14 +71,14 @@ describe('pipe', () => { function Template(ctx: string, cm: boolean) { if (cm) { - elementStart(0, 'div', null, [MyDir]); + elementStart(0, 'div', ['myDir', '']); pipe(1, DoublePipe.ngPipeDef); elementEnd(); } elementProperty(0, 'elprop', bind(pipeBind1(1, ctx))); directive = loadDirective(0); } - renderToHtml(Template, 'a'); + renderToHtml(Template, 'a', [MyDir.ngDirectiveDef]); expect(directive !.dirProp).toEqual('aa'); }); @@ -124,11 +128,11 @@ describe('pipe', () => { } elementProperty(0, 'someProp', bind(pipeBind1(1, 'Megatron'))); } - renderToHtml(Template, person, rendererFactory2); + renderToHtml(Template, person, [], rendererFactory2); expect(renderLog.log).toEqual(['someProp=Megatron']); renderLog.clear(); - renderToHtml(Template, person, rendererFactory2); + renderToHtml(Template, person, [], rendererFactory2); expect(renderLog.log).toEqual([]); }); @@ -192,7 +196,7 @@ describe('pipe', () => { containerRefreshEnd(); } const pipeInstances: CountingPipe[] = []; - renderToHtml(Template, {}, rendererFactory2); + renderToHtml(Template, {}, [], rendererFactory2); expect(pipeInstances.length).toEqual(4); expect(pipeInstances[0]).toBeAnInstanceOf(CountingPipe); expect(pipeInstances[1]).toBe(pipeInstances[0]); @@ -249,7 +253,7 @@ describe('pipe', () => { containerRefreshEnd(); } const pipeInstances: CountingImpurePipe[] = []; - renderToHtml(Template, {}, rendererFactory2); + renderToHtml(Template, {}, [], rendererFactory2); expect(pipeInstances.length).toEqual(4); expect(pipeInstances[0]).toBeAnInstanceOf(CountingImpurePipe); expect(pipeInstances[1]).toBeAnInstanceOf(CountingImpurePipe); diff --git a/packages/core/test/render3/properties_spec.ts b/packages/core/test/render3/properties_spec.ts index 131c19bb1c..fb78c8b622 100644 --- a/packages/core/test/render3/properties_spec.ts +++ b/packages/core/test/render3/properties_spec.ts @@ -11,7 +11,7 @@ import {EventEmitter} from '@angular/core'; import {defineComponent, defineDirective, tick} from '../../src/render3/index'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, loadDirective, text, textBinding} from '../../src/render3/instructions'; -import {ComponentFixture, renderToHtml} from './render_util'; +import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; describe('elementProperty', () => { @@ -68,7 +68,7 @@ describe('elementProperty', () => { static ngComponentDef = defineComponent({ type: HostBindingComp, - tag: 'host-binding-comp', + selector: [[['host-binding-comp'], null]], factory: () => new HostBindingComp(), hostBindings: (dirIndex: number, elIndex: number) => { const instance = loadDirective(dirIndex) as HostBindingComp; @@ -89,12 +89,18 @@ describe('elementProperty', () => { describe('input properties', () => { let button: MyButton; let otherDir: OtherDir; + let otherDisabledDir: OtherDisabledDir; + let idDir: IdDir; class MyButton { disabled: boolean; - static ngDirectiveDef = defineDirective( - {type: MyButton, factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}}); + static ngDirectiveDef = defineDirective({ + type: MyButton, + selector: [[['', 'myButton', ''], null]], + factory: () => button = new MyButton(), + inputs: {disabled: 'disabled'} + }); } class OtherDir { @@ -103,18 +109,47 @@ describe('elementProperty', () => { static ngDirectiveDef = defineDirective({ type: OtherDir, + selector: [[['', 'otherDir', ''], null]], factory: () => otherDir = new OtherDir(), inputs: {id: 'id'}, outputs: {clickStream: 'click'} }); } + class OtherDisabledDir { + disabled: boolean; + + static ngDirectiveDef = defineDirective({ + type: OtherDisabledDir, + selector: [[['', 'otherDisabledDir', ''], null]], + factory: () => otherDisabledDir = new OtherDisabledDir(), + inputs: {disabled: 'disabled'} + }); + } + + class IdDir { + idNumber: number; + + static ngDirectiveDef = defineDirective({ + type: IdDir, + selector: [[['', 'idDir', ''], null]], + factory: () => idDir = new IdDir(), + inputs: {idNumber: 'id'} + }); + } + + + const deps = [ + MyButton.ngDirectiveDef, OtherDir.ngDirectiveDef, OtherDisabledDir.ngDirectiveDef, + IdDir.ngDirectiveDef + ]; + it('should check input properties before setting (directives)', () => { - /** */ + /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [MyButton, OtherDir]); + elementStart(0, 'button', ['otherDir', '', 'myButton', '']); { text(1, 'Click me'); } elementEnd(); } @@ -124,13 +159,15 @@ describe('elementProperty', () => { } const ctx: any = {isDisabled: true, id: 0}; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(true); expect(otherDir !.id).toEqual(0); ctx.isDisabled = false; ctx.id = 1; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(false); expect(otherDir !.id).toEqual(1); }); @@ -140,7 +177,7 @@ describe('elementProperty', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [MyButton]); + elementStart(0, 'button', ['myButton', '']); { text(1, 'Click me'); } elementEnd(); } @@ -150,23 +187,27 @@ describe('elementProperty', () => { } const ctx: any = {isDisabled: true, id: 0}; - expect(renderToHtml(Template, ctx)).toEqual(``); + + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(true); ctx.isDisabled = false; ctx.id = 1; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(false); }); it('should check that property is not an input property before setting (component)', () => { let comp: Comp; + class Comp { id: number; static ngComponentDef = defineComponent({ type: Comp, - tag: 'comp', + selector: [[['comp'], null]], template: function(ctx: any, cm: boolean) {}, factory: () => comp = new Comp(), inputs: {id: 'id'} @@ -176,36 +217,26 @@ describe('elementProperty', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } elementProperty(0, 'id', bind(ctx.id)); } - expect(renderToHtml(Template, {id: 1})).toEqual(``); + const deps = [Comp.ngComponentDef]; + expect(renderToHtml(Template, {id: 1}, deps)).toEqual(``); expect(comp !.id).toEqual(1); - expect(renderToHtml(Template, {id: 2})).toEqual(``); + expect(renderToHtml(Template, {id: 2}, deps)).toEqual(``); expect(comp !.id).toEqual(2); }); it('should support two input properties with the same name', () => { - let otherDisabledDir: OtherDisabledDir; - - class OtherDisabledDir { - disabled: boolean; - - static ngDirectiveDef = defineDirective({ - type: OtherDisabledDir, - factory: () => otherDisabledDir = new OtherDisabledDir(), - inputs: {disabled: 'disabled'} - }); - } /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [MyButton, OtherDisabledDir]); + elementStart(0, 'button', ['myButton', '', 'otherDisabledDir', '']); { text(1, 'Click me'); } elementEnd(); } @@ -213,12 +244,14 @@ describe('elementProperty', () => { } const ctx: any = {isDisabled: true}; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(true); expect(otherDisabledDir !.disabled).toEqual(true); ctx.isDisabled = false; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)) + .toEqual(``); expect(button !.disabled).toEqual(false); expect(otherDisabledDir !.disabled).toEqual(false); }); @@ -227,7 +260,7 @@ describe('elementProperty', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [OtherDir]); + elementStart(0, 'button', ['otherDir', '']); { listener('click', ctx.onClick.bind(ctx)); text(1, 'Click me'); @@ -239,27 +272,18 @@ describe('elementProperty', () => { let counter = 0; const ctx: any = {id: 1, onClick: () => counter++}; - expect(renderToHtml(Template, ctx)).toEqual(``); + expect(renderToHtml(Template, ctx, deps)).toEqual(``); expect(otherDir !.id).toEqual(1); otherDir !.clickStream.next(); expect(counter).toEqual(1); ctx.id = 2; - renderToHtml(Template, ctx); + renderToHtml(Template, ctx, deps); expect(otherDir !.id).toEqual(2); }); it('should support unrelated element properties at same index in if-else block', () => { - let idDir: IdDir; - - class IdDir { - idNumber: number; - - static ngDirectiveDef = defineDirective( - {type: IdDir, factory: () => idDir = new IdDir(), inputs: {idNumber: 'id'}}); - } - /** * // inputs: {'id': [0, 'idNumber']} * % if (condition) { @@ -270,7 +294,7 @@ describe('elementProperty', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'button', null, [IdDir]); + elementStart(0, 'button', ['idDir', '']); { text(1, 'Click me'); } elementEnd(); container(2); @@ -288,7 +312,7 @@ describe('elementProperty', () => { embeddedViewEnd(); } else { if (embeddedViewStart(1)) { - elementStart(0, 'button', null, [OtherDir]); + elementStart(0, 'button', ['otherDir', '']); { text(1, 'Click me too'); } elementEnd(); } @@ -299,12 +323,13 @@ describe('elementProperty', () => { containerRefreshEnd(); } - expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'})) - .toEqual(``); + expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'}, deps)) + .toEqual(``); expect(idDir !.idNumber).toEqual('one'); - expect(renderToHtml(Template, {condition: false, id1: 'four', id2: 'two', id3: 'three'})) - .toEqual(``); + expect( + renderToHtml(Template, {condition: false, id1: 'four', id2: 'two', id3: 'three'}, deps)) + .toEqual(``); expect(idDir !.idNumber).toEqual('four'); expect(otherDir !.id).toEqual('three'); }); @@ -320,6 +345,7 @@ describe('elementProperty', () => { static ngDirectiveDef = defineDirective({ type: MyDir, + selector: [[['', 'myDir', ''], null]], factory: () => myDir = new MyDir(), inputs: {role: 'role', direction: 'dir'}, outputs: {changeStream: 'change'}, @@ -331,21 +357,27 @@ describe('elementProperty', () => { class MyDirB { roleB: string; - static ngDirectiveDef = defineDirective( - {type: MyDirB, factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}}); + static ngDirectiveDef = defineDirective({ + type: MyDirB, + selector: [[['', 'myDirB', ''], null]], + factory: () => dirB = new MyDirB(), + inputs: {roleB: 'role'} + }); } + const deps = [MyDir.ngDirectiveDef, MyDirB.ngDirectiveDef]; + it('should set input property based on attribute if existing', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button'], [MyDir]); + elementStart(0, 'div', ['role', 'button', 'myDir', '']); elementEnd(); } } - expect(renderToHtml(Template, {})).toEqual(`
    `); + expect(renderToHtml(Template, {}, deps)).toEqual(`
    `); expect(myDir !.role).toEqual('button'); }); @@ -354,16 +386,17 @@ describe('elementProperty', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button'], [MyDir]); + elementStart(0, 'div', ['role', 'button', 'myDir', '']); elementEnd(); } elementProperty(0, 'role', bind(ctx.role)); } - expect(renderToHtml(Template, {role: 'listbox'})).toEqual(`
    `); + expect(renderToHtml(Template, {role: 'listbox'}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('listbox'); - renderToHtml(Template, {role: 'button'}); + renderToHtml(Template, {role: 'button'}, deps); expect(myDir !.role).toEqual('button'); }); @@ -372,12 +405,13 @@ describe('elementProperty', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button'], [MyDir, MyDirB]); + elementStart(0, 'div', ['role', 'button', 'myDir', '', 'myDirB', '']); elementEnd(); } } - expect(renderToHtml(Template, {})).toEqual(`
    `); + expect(renderToHtml(Template, {}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('button'); expect(dirB !.roleB).toEqual('button'); }); @@ -387,12 +421,13 @@ describe('elementProperty', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]); + elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']); elementEnd(); } } - expect(renderToHtml(Template, {})).toEqual(`
    `); + expect(renderToHtml(Template, {}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('button'); expect(myDir !.direction).toEqual('rtl'); }); @@ -402,16 +437,15 @@ describe('elementProperty', () => { /**
    */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button'], [MyDir]); + elementStart(0, 'div', ['role', 'button', 'myDir', '']); { listener('change', ctx.onChange.bind(ctx)); } elementEnd(); } } let counter = 0; - expect(renderToHtml(Template, { - onChange: () => counter++ - })).toEqual(`
    `); + expect(renderToHtml(Template, {onChange: () => counter++}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('button'); myDir !.changeStream.next(); @@ -426,15 +460,16 @@ describe('elementProperty', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]); + elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']); elementEnd(); - elementStart(1, 'div', ['role', 'listbox'], [MyDirB]); + elementStart(1, 'div', ['role', 'listbox', 'myDirB', '']); elementEnd(); } } - expect(renderToHtml(Template, {})) - .toEqual(`
    `); + expect(renderToHtml(Template, {}, deps)) + .toEqual( + `
    `); expect(myDir !.role).toEqual('button'); expect(myDir !.direction).toEqual('rtl'); expect(dirB !.roleB).toEqual('listbox'); @@ -452,7 +487,7 @@ describe('elementProperty', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'listbox'], [MyDir]); + elementStart(0, 'div', ['role', 'listbox', 'myDir', '']); elementEnd(); container(1); } @@ -460,7 +495,7 @@ describe('elementProperty', () => { { if (ctx.condition) { if (embeddedViewStart(0)) { - elementStart(0, 'div', ['role', 'button'], [MyDirB]); + elementStart(0, 'div', ['role', 'button', 'myDirB', '']); elementEnd(); } embeddedViewEnd(); @@ -476,16 +511,14 @@ describe('elementProperty', () => { containerRefreshEnd(); } - expect(renderToHtml(Template, { - condition: true - })).toEqual(`
    `); + expect(renderToHtml(Template, {condition: true}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('listbox'); expect(dirB !.roleB).toEqual('button'); expect((dirB !as any).role).toBeUndefined(); - expect(renderToHtml(Template, { - condition: false - })).toEqual(`
    `); + expect(renderToHtml(Template, {condition: false}, deps)) + .toEqual(`
    `); expect(myDir !.role).toEqual('listbox'); }); @@ -494,18 +527,19 @@ describe('elementProperty', () => { class Comp { static ngComponentDef = defineComponent({ type: Comp, - tag: 'comp', + selector: [[['comp'], null]], /**
    {{ dir.role }} */ template: function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, 'div', ['role', 'button'], [MyDir], ['dir', 'myDir']); + elementStart(0, 'div', ['role', 'button', 'myDir', ''], ['dir', 'myDir']); elementEnd(); text(1); } // TODO: remove this loadDirective when removing MyDir textBinding(1, bind(loadDirective(0).role)); }, - factory: () => new Comp() + factory: () => new Comp(), + directiveDefs: () => [MyDir.ngDirectiveDef] }); } @@ -522,7 +556,7 @@ describe('elementProperty', () => { { for (let i = 0; i < 2; i++) { if (embeddedViewStart(0)) { - elementStart(0, Comp); + elementStart(0, 'comp'); elementEnd(); } embeddedViewEnd(); @@ -531,9 +565,9 @@ describe('elementProperty', () => { containerRefreshEnd(); } - expect(renderToHtml(Template, {})) + expect(renderToHtml(Template, {}, [Comp.ngComponentDef])) .toEqual( - `
    button
    button
    `); + `
    button
    button
    `); }); }); diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index 36bd8c64ee..889607c9f1 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -18,34 +18,36 @@ describe('array literals', () => { static ngComponentDef = defineComponent({ type: MyComp, - tag: 'my-comp', + selector: [[['my-comp'], null]], factory: function MyComp_Factory() { return myComp = new MyComp(); }, template: function MyComp_Template(ctx: MyComp, cm: boolean) {}, inputs: {names: 'names'} }); } + const defs = [MyComp.ngComponentDef]; + it('should support an array literal with a binding', () => { const e0_ff = (v: any) => ['Nancy', v, 'Bess']; /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); elementEnd(); } elementProperty(0, 'names', bind(pureFunction1(e0_ff, ctx.customName))); } - renderToHtml(Template, {customName: 'Carson'}); + renderToHtml(Template, {customName: 'Carson'}, defs); const firstArray = myComp !.names; expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']); - renderToHtml(Template, {customName: 'Carson'}); + renderToHtml(Template, {customName: 'Carson'}, defs); expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']); expect(firstArray).toBe(myComp !.names); - renderToHtml(Template, {customName: 'Hannah'}); + renderToHtml(Template, {customName: 'Hannah'}, defs); expect(myComp !.names).toEqual(['Nancy', 'Hannah', 'Bess']); // Identity must change if binding changes @@ -54,7 +56,7 @@ describe('array literals', () => { // The property should not be set if the exp value is the same, so artificially // setting the property to ensure it's not overwritten. myComp !.names = ['should not be overwritten']; - renderToHtml(Template, {customName: 'Hannah'}); + renderToHtml(Template, {customName: 'Hannah'}, defs); expect(myComp !.names).toEqual(['should not be overwritten']); }); @@ -67,7 +69,7 @@ describe('array literals', () => { static ngComponentDef = defineComponent({ type: ManyPropComp, - tag: 'many-prop-comp', + selector: [[['many-prop-comp'], null]], factory: function ManyPropComp_Factory() { return manyPropComp = new ManyPropComp(); }, template: function ManyPropComp_Template(ctx: ManyPropComp, cm: boolean) {}, inputs: {names1: 'names1', names2: 'names2'} @@ -83,18 +85,19 @@ describe('array literals', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ManyPropComp); + elementStart(0, 'many-prop-comp'); elementEnd(); } elementProperty(0, 'names1', bind(pureFunction1(e0_ff, ctx.customName))); elementProperty(0, 'names2', bind(pureFunction1(e0_ff_1, ctx.customName2))); } - renderToHtml(Template, {customName: 'Carson', customName2: 'George'}); + const defs = [ManyPropComp.ngComponentDef]; + renderToHtml(Template, {customName: 'Carson', customName2: 'George'}, defs); expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']); expect(manyPropComp !.names2).toEqual(['George']); - renderToHtml(Template, {customName: 'George', customName2: 'Carson'}); + renderToHtml(Template, {customName: 'George', customName2: 'Carson'}, defs); expect(manyPropComp !.names1).toEqual(['Nancy', 'George']); expect(manyPropComp !.names2).toEqual(['Carson']); }); @@ -115,36 +118,37 @@ describe('array literals', () => { static ngComponentDef = defineComponent({ type: ParentComp, - tag: 'parent-comp', + selector: [[['parent-comp'], null]], factory: () => new ParentComp(), template: function(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); myComps.push(loadDirective(0)); elementEnd(); } elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(e0_ff, ctx.customName)))); - } + }, + directiveDefs: defs }); } function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ParentComp); + elementStart(0, 'parent-comp'); elementEnd(); - elementStart(1, ParentComp); + elementStart(1, 'parent-comp'); elementEnd(); } } - renderToHtml(Template, {}); + renderToHtml(Template, {}, [ParentComp.ngComponentDef]); const firstArray = myComps[0].names; const secondArray = myComps[1].names; expect(firstArray).toEqual(['NANCY', 'Bess']); expect(secondArray).toEqual(['NANCY', 'Bess']); expect(firstArray).not.toBe(secondArray); - renderToHtml(Template, {}); + renderToHtml(Template, {}, [ParentComp.ngComponentDef]); expect(firstArray).toEqual(['NANCY', 'Bess']); expect(secondArray).toEqual(['NANCY', 'Bess']); expect(firstArray).toBe(myComps[0].names); @@ -157,31 +161,31 @@ describe('array literals', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); elementEnd(); } elementProperty(0, 'names', bind(pureFunction2(e0_ff, ctx.customName, ctx.customName2))); } - renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}); + renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}, defs); const firstArray = myComp !.names; expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']); - renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}); + renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}, defs); expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']); expect(firstArray).toBe(myComp !.names); - renderToHtml(Template, {customName: 'George', customName2: 'Hannah'}); + renderToHtml(Template, {customName: 'George', customName2: 'Hannah'}, defs); expect(myComp !.names).toEqual(['Nancy', 'George', 'Bess', 'Hannah']); expect(firstArray).not.toBe(myComp !.names); - renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}); + renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}, defs); expect(myComp !.names).toEqual(['Nancy', 'Frank', 'Bess', 'Ned']); // The property should not be set if the exp value is the same, so artificially // setting the property to ensure it's not overwritten. myComp !.names = ['should not be overwritten']; - renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}); + renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}, defs); expect(myComp !.names).toEqual(['should not be overwritten']); }); @@ -210,22 +214,22 @@ describe('array literals', () => { function Template(c: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); f3Comp = loadDirective(0); elementEnd(); - elementStart(1, MyComp); + elementStart(1, 'my-comp'); f4Comp = loadDirective(1); elementEnd(); - elementStart(2, MyComp); + elementStart(2, 'my-comp'); f5Comp = loadDirective(2); elementEnd(); - elementStart(3, MyComp); + elementStart(3, 'my-comp'); f6Comp = loadDirective(3); elementEnd(); - elementStart(4, MyComp); + elementStart(4, 'my-comp'); f7Comp = loadDirective(4); elementEnd(); - elementStart(5, MyComp); + elementStart(5, 'my-comp'); f8Comp = loadDirective(5); elementEnd(); } @@ -239,7 +243,7 @@ describe('array literals', () => { 5, 'names', bind(pureFunction8(e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]))); } - renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']); + renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs); expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); @@ -247,7 +251,7 @@ describe('array literals', () => { expect(f7Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); expect(f8Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); - renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1', 'i1']); + renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1', 'i1'], defs); expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h1']); expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h1']); expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h1']); @@ -255,7 +259,7 @@ describe('array literals', () => { expect(f7Comp !.names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']); expect(f8Comp !.names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']); - renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2', 'i1']); + renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2', 'i1'], defs); expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h2']); expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h2']); expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h2']); @@ -270,14 +274,14 @@ describe('array literals', () => { v8: any) => ['start', v0, v1, v2, v3, v4, v5, v6, v7, v8, 'end']; const e0_ff_1 = (v: any) => { return {name: v}; }; - renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']); + renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs); /** * * */ function Template(c: any, cm: boolean) { if (cm) { - elementStart(0, MyComp); + elementStart(0, 'my-comp'); elementEnd(); } elementProperty(0, 'names', bind(pureFunctionV(e0_ff, [ @@ -289,12 +293,12 @@ describe('array literals', () => { 'start', 'a', 'b', 'c', 'd', {name: 'e'}, 'f', 'g', 'h', 'i', 'end' ]); - renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']); + renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs); expect(myComp !.names).toEqual([ 'start', 'a1', 'b', 'c', 'd', {name: 'e'}, 'f', 'g', 'h', 'i', 'end' ]); - renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e5', 'f', 'g', 'h', 'i']); + renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e5', 'f', 'g', 'h', 'i'], defs); expect(myComp !.names).toEqual([ 'start', 'a1', 'b', 'c', 'd', {name: 'e5'}, 'f', 'g', 'h', 'i', 'end' ]); @@ -309,34 +313,36 @@ describe('object literals', () => { static ngComponentDef = defineComponent({ type: ObjectComp, - tag: 'object-comp', + selector: [[['object-comp'], null]], factory: function ObjectComp_Factory() { return objectComp = new ObjectComp(); }, template: function ObjectComp_Template(ctx: ObjectComp, cm: boolean) {}, inputs: {config: 'config'} }); } + const defs = [ObjectComp.ngComponentDef]; + it('should support an object literal', () => { const e0_ff = (v: any) => { return {duration: 500, animation: v}; }; /** */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ObjectComp); + elementStart(0, 'object-comp'); elementEnd(); } elementProperty(0, 'config', bind(pureFunction1(e0_ff, ctx.name))); } - renderToHtml(Template, {name: 'slide'}); + renderToHtml(Template, {name: 'slide'}, defs); const firstObj = objectComp !.config; expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'}); - renderToHtml(Template, {name: 'slide'}); + renderToHtml(Template, {name: 'slide'}, defs); expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'}); expect(firstObj).toBe(objectComp !.config); - renderToHtml(Template, {name: 'tap'}); + renderToHtml(Template, {name: 'tap'}, defs); expect(objectComp !.config).toEqual({duration: 500, animation: 'tap'}); // Identity must change if binding changes @@ -355,7 +361,7 @@ describe('object literals', () => { */ function Template(ctx: any, cm: boolean) { if (cm) { - elementStart(0, ObjectComp); + elementStart(0, 'object-comp'); elementEnd(); } elementProperty( @@ -364,34 +370,34 @@ describe('object literals', () => { e0_ff, ctx.name, pureFunction1(e0_ff_1, pureFunction1(e0_ff_2, ctx.duration))))); } - renderToHtml(Template, {name: 'slide', duration: 100}); + renderToHtml(Template, {name: 'slide', duration: 100}, defs); expect(objectComp !.config).toEqual({ animation: 'slide', actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}] }); const firstConfig = objectComp !.config; - renderToHtml(Template, {name: 'slide', duration: 100}); + renderToHtml(Template, {name: 'slide', duration: 100}, defs); expect(objectComp !.config).toEqual({ animation: 'slide', actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}] }); expect(objectComp !.config).toBe(firstConfig); - renderToHtml(Template, {name: 'slide', duration: 50}); + renderToHtml(Template, {name: 'slide', duration: 50}, defs); expect(objectComp !.config).toEqual({ animation: 'slide', actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}] }); expect(objectComp !.config).not.toBe(firstConfig); - renderToHtml(Template, {name: 'tap', duration: 50}); + renderToHtml(Template, {name: 'tap', duration: 50}, defs); expect(objectComp !.config).toEqual({ animation: 'tap', actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}] }); - renderToHtml(Template, {name: 'drag', duration: 500}); + renderToHtml(Template, {name: 'drag', duration: 500}, defs); expect(objectComp !.config).toEqual({ animation: 'drag', actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 500}] @@ -400,7 +406,7 @@ describe('object literals', () => { // The property should not be set if the exp value is the same, so artificially // setting the property to ensure it's not overwritten. objectComp !.config = ['should not be overwritten']; - renderToHtml(Template, {name: 'drag', duration: 500}); + renderToHtml(Template, {name: 'drag', duration: 500}, defs); expect(objectComp !.config).toEqual(['should not be overwritten']); }); @@ -421,7 +427,7 @@ describe('object literals', () => { { for (let i = 0; i < 2; i++) { if (embeddedViewStart(0)) { - elementStart(0, ObjectComp); + elementStart(0, 'object-comp'); objectComps.push(loadDirective(0)); elementEnd(); } @@ -437,12 +443,12 @@ describe('object literals', () => { const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; }; const configs = [{opacity: 0, duration: 500}, {opacity: 1, duration: 600}]; - renderToHtml(Template, {configs}); + renderToHtml(Template, {configs}, defs); expect(objectComps[0].config).toEqual({opacity: 0, duration: 500}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); configs[0].duration = 1000; - renderToHtml(Template, {configs}); + renderToHtml(Template, {configs}, defs); expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); }); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index bed8d6834a..e5a464415d 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -62,10 +62,10 @@ describe('query', () => { if (cm) { query(0, Child, false); query(1, Child, true); - elementStart(2, Child); + elementStart(2, 'child'); { child1 = loadDirective(0); - elementStart(3, Child); + elementStart(3, 'child'); { child2 = loadDirective(1); } elementEnd(); } @@ -73,7 +73,7 @@ describe('query', () => { } queryRefresh(tmp = load>(0)) && (ctx.query0 = tmp as QueryList); queryRefresh(tmp = load>(1)) && (ctx.query1 = tmp as QueryList); - }); + }, [Child.ngComponentDef]); const parent = renderComponent(Cmp); expect((parent.query0 as QueryList).toArray()).toEqual([child1]); @@ -83,7 +83,7 @@ describe('query', () => { describe('types predicate', () => { it('should query using type predicate and read a specified token', () => { - const Child = createDirective(); + const Child = createDirective('child'); let elToQuery; /** *
    @@ -95,11 +95,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, Child, false, QUERY_READ_ELEMENT_REF); - elToQuery = elementStart(1, 'div', null, [Child]); + elToQuery = elementStart(1, 'div', ['child', '']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -110,8 +110,8 @@ describe('query', () => { it('should query using type predicate and read another directive type', () => { - const Child = createDirective(); - const OtherChild = createDirective(); + const Child = createDirective('child'); + const OtherChild = createDirective('otherChild'); let otherChildInstance; /** *
    @@ -123,12 +123,12 @@ describe('query', () => { let tmp: any; if (cm) { query(0, Child, false, OtherChild); - elementStart(1, 'div', null, [Child, OtherChild]); + elementStart(1, 'div', ['child', '', 'otherChild', '']); { otherChildInstance = loadDirective(1); } elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef, OtherChild.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -137,8 +137,8 @@ describe('query', () => { }); it('should not add results to query if a requested token cant be read', () => { - const Child = createDirective(); - const OtherChild = createDirective(); + const Child = createDirective('child'); + const OtherChild = createDirective('otherChild'); /** *
    * class Cmpt { @@ -149,11 +149,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, Child, false, OtherChild); - elementStart(1, 'div', null, [Child]); + elementStart(1, 'div', ['child', '']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef, OtherChild.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -177,7 +177,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_FROM_NODE); - elToQuery = elementStart(1, 'div', null, null, ['foo', '']); + elToQuery = elementStart(1, 'div', null, ['foo', '']); elementEnd(); elementStart(2, 'div'); elementEnd(); @@ -207,7 +207,7 @@ describe('query', () => { if (cm) { query(0, ['foo'], false, QUERY_READ_FROM_NODE); query(1, ['bar'], false, QUERY_READ_FROM_NODE); - elToQuery = elementStart(2, 'div', null, null, ['foo', '', 'bar', '']); + elToQuery = elementStart(2, 'div', null, ['foo', '', 'bar', '']); elementEnd(); elementStart(3, 'div'); elementEnd(); @@ -243,11 +243,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); - el1ToQuery = elementStart(1, 'div', null, null, ['foo', '']); + el1ToQuery = elementStart(1, 'div', null, ['foo', '']); elementEnd(); elementStart(2, 'div'); elementEnd(); - el2ToQuery = elementStart(3, 'div', null, null, ['bar', '']); + el2ToQuery = elementStart(3, 'div', null, ['bar', '']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); @@ -274,7 +274,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); - elToQuery = elementStart(1, 'div', null, null, ['foo', '']); + elToQuery = elementStart(1, 'div', null, ['foo', '']); elementEnd(); elementStart(2, 'div'); elementEnd(); @@ -300,7 +300,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); - elementStart(1, 'div', null, null, ['foo', '']); + elementStart(1, 'div', null, ['foo', '']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); @@ -323,7 +323,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); - container(1, undefined, undefined, undefined, undefined, ['foo', '']); + container(1, undefined, undefined, undefined, ['foo', '']); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); }); @@ -346,7 +346,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); - container(1, undefined, undefined, undefined, undefined, ['foo', '']); + container(1, undefined, undefined, undefined, ['foo', '']); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); }); @@ -369,7 +369,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], undefined, QUERY_READ_FROM_NODE); - container(1, undefined, undefined, undefined, undefined, ['foo', '']); + container(1, undefined, undefined, undefined, ['foo', '']); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); }); @@ -392,7 +392,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF); - container(1, undefined, undefined, undefined, undefined, ['foo', '']); + container(1, undefined, undefined, undefined, ['foo', '']); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); }); @@ -417,12 +417,12 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); - elementStart(1, Child, null, null, ['foo', '']); + elementStart(1, 'child', null, ['foo', '']); { childInstance = loadDirective(0); } elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngComponentDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -436,7 +436,7 @@ describe('query', () => { class Child { static ngComponentDef = defineComponent({ type: Child, - tag: 'child', + selector: [[['child'], null]], factory: () => childInstance = new Child(), template: (ctx: Child, cm: boolean) => {}, exportAs: 'child' @@ -453,11 +453,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); - elementStart(1, Child, null, null, ['foo', 'child']); + elementStart(1, 'child', null, ['foo', 'child']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngComponentDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -467,7 +467,7 @@ describe('query', () => { it('should read directive instance if element queried for has an exported directive with a matching name', () => { - const Child = createDirective({exportAs: 'child'}); + const Child = createDirective('child', {exportAs: 'child'}); let childInstance; /** @@ -480,12 +480,12 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); - elementStart(1, 'div', null, [Child], ['foo', 'child']); + elementStart(1, 'div', ['child', ''], ['foo', 'child']); childInstance = loadDirective(0); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -494,8 +494,8 @@ describe('query', () => { }); it('should read all matching directive instances from a given element', () => { - const Child1 = createDirective({exportAs: 'child1'}); - const Child2 = createDirective({exportAs: 'child2'}); + const Child1 = createDirective('child1', {exportAs: 'child1'}); + const Child2 = createDirective('child2', {exportAs: 'child2'}); let child1Instance, child2Instance; /** @@ -508,7 +508,7 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE); - elementStart(1, 'div', null, [Child1, Child2], ['foo', 'child1', 'bar', 'child2']); + elementStart(1, 'div', ['child1', '', 'child2', ''], ['foo', 'child1', 'bar', 'child2']); { child1Instance = loadDirective(0); child2Instance = loadDirective(1); @@ -516,7 +516,7 @@ describe('query', () => { elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child1.ngDirectiveDef, Child2.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -526,7 +526,7 @@ describe('query', () => { }); it('should read multiple locals exporting the same directive from a given element', () => { - const Child = createDirective({exportAs: 'child'}); + const Child = createDirective('child', {exportAs: 'child'}); let childInstance; /** @@ -541,13 +541,13 @@ describe('query', () => { if (cm) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(1, ['bar'], true, QUERY_READ_FROM_NODE); - elementStart(2, 'div', null, [Child], ['foo', 'child', 'bar', 'child']); + elementStart(2, 'div', ['child', ''], ['foo', 'child', 'bar', 'child']); { childInstance = loadDirective(0); } elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.fooQuery = tmp as QueryList); queryRefresh(tmp = load>(1)) && (ctx.barQuery = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); @@ -561,7 +561,7 @@ describe('query', () => { }); it('should match on exported directive name and read a requested token', () => { - const Child = createDirective({exportAs: 'child'}); + const Child = createDirective('child', {exportAs: 'child'}); let div; /** @@ -574,11 +574,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF); - div = elementStart(1, 'div', null, [Child], ['foo', 'child']); + div = elementStart(1, 'div', ['child', ''], ['foo', 'child']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -587,7 +587,7 @@ describe('query', () => { }); it('should support reading a mix of ElementRef and directive instances', () => { - const Child = createDirective({exportAs: 'child'}); + const Child = createDirective('child', {exportAs: 'child'}); let childInstance, div; /** @@ -600,12 +600,12 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); - div = elementStart(1, 'div', null, [Child], ['foo', '', 'bar', 'child']); + div = elementStart(1, 'div', ['child', ''], ['foo', '', 'bar', 'child']); { childInstance = loadDirective(0); } elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -615,7 +615,7 @@ describe('query', () => { }); it('should not add results to query if a requested token cant be read', () => { - const Child = createDirective(); + const Child = createDirective('child'); /** *
    @@ -627,11 +627,11 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], false, Child); - elementStart(1, 'div', null, null, ['foo', '']); + elementStart(1, 'div', ['foo', '']); elementEnd(); } queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); - }); + }, [Child.ngDirectiveDef]); const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList); @@ -664,7 +664,7 @@ describe('query', () => { let cm1 = embeddedViewStart(1); { if (cm1) { - firstEl = elementStart(0, 'div', null, null, ['foo', '']); + firstEl = elementStart(0, 'div', null, ['foo', '']); elementEnd(); } } @@ -706,10 +706,10 @@ describe('query', () => { let tmp: any; if (cm) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); - firstEl = elementStart(1, 'b', null, null, ['foo', '']); + firstEl = elementStart(1, 'span', null, ['foo', '']); elementEnd(); container(2); - lastEl = elementStart(3, 'i', null, null, ['foo', '']); + lastEl = elementStart(3, 'span', null, ['foo', '']); elementEnd(); } containerRefreshStart(2); @@ -718,7 +718,7 @@ describe('query', () => { let cm1 = embeddedViewStart(1); { if (cm1) { - viewEl = elementStart(0, 'div', null, null, ['foo', '']); + viewEl = elementStart(0, 'div', null, ['foo', '']); elementEnd(); } } @@ -774,7 +774,7 @@ describe('query', () => { let cm1 = embeddedViewStart(0); { if (cm1) { - firstEl = elementStart(0, 'div', null, null, ['foo', '']); + firstEl = elementStart(0, 'div', null, ['foo', '']); elementEnd(); } } @@ -784,7 +784,7 @@ describe('query', () => { let cm1 = embeddedViewStart(1); { if (cm1) { - lastEl = elementStart(0, 'span', null, null, ['foo', '']); + lastEl = elementStart(0, 'span', null, ['foo', '']); elementEnd(); } } @@ -836,7 +836,7 @@ describe('query', () => { let cm1 = embeddedViewStart(0); { if (cm1) { - firstEl = elementStart(0, 'div', null, null, ['foo', '']); + firstEl = elementStart(0, 'div', null, ['foo', '']); elementEnd(); container(1); } @@ -846,7 +846,7 @@ describe('query', () => { let cm2 = embeddedViewStart(0); { if (cm2) { - lastEl = elementStart(0, 'span', null, null, ['foo', '']); + lastEl = elementStart(0, 'span', null, ['foo', '']); elementEnd(); } } @@ -894,7 +894,7 @@ describe('query', () => { query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(1, ['foo'], false, QUERY_READ_FROM_NODE); container(2); - elementStart(3, 'span', null, null, ['foo', '']); + elementStart(3, 'span', null, ['foo', '']); elementEnd(); } containerRefreshStart(2); @@ -903,7 +903,7 @@ describe('query', () => { let cm1 = embeddedViewStart(0); { if (cm1) { - elementStart(0, 'div', null, null, ['foo', '']); + elementStart(0, 'div', null, ['foo', '']); elementEnd(); } } diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 710b948b13..233dac0ac6 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -9,7 +9,7 @@ import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {CreateComponentOptions} from '../../src/render3/component'; -import {ComponentTemplate, ComponentType, DirectiveType, PublicFeature, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; +import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PublicFeature, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; import {NG_HOST_SYMBOL, renderTemplate} from '../../src/render3/instructions'; import {LElementNode} from '../../src/render3/interfaces/node'; import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; @@ -151,9 +151,10 @@ export function resetDOM() { * @deprecated use `TemplateFixture` or `ComponentFixture` */ export function renderToHtml( - template: ComponentTemplate, ctx: any, providedRendererFactory?: RendererFactory3) { + template: ComponentTemplate, ctx: any, defs?: any[], + providedRendererFactory?: RendererFactory3 | null) { host = renderTemplate( - containerEl, template, ctx, providedRendererFactory || testRendererFactory, host); + containerEl, template, ctx, providedRendererFactory || testRendererFactory, host, defs); return toHtml(containerEl); } @@ -188,23 +189,27 @@ export function toHtml(componentOrElement: T | RElement): string { } export function createComponent( - name: string, template: ComponentTemplate): ComponentType { + name: string, template: ComponentTemplate, + defs: (ComponentDef| DirectiveDef)[] = []): ComponentType { return class Component { value: any; static ngComponentDef = defineComponent({ type: Component, - tag: name, + selector: [[[name], null]], factory: () => new Component, template: template, - features: [PublicFeature] + features: [PublicFeature], + directiveDefs: () => defs }); }; } -export function createDirective({exportAs}: {exportAs?: string} = {}): DirectiveType { +export function createDirective( + name: string, {exportAs}: {exportAs?: string} = {}): DirectiveType { return class Directive { static ngDirectiveDef = defineDirective({ type: Directive, + selector: [[['', name, ''], null]], factory: () => new Directive(), features: [PublicFeature], exportAs: exportAs, diff --git a/packages/core/test/render3/renderer_factory_spec.ts b/packages/core/test/render3/renderer_factory_spec.ts index 29003b5a07..7fc21fc642 100644 --- a/packages/core/test/render3/renderer_factory_spec.ts +++ b/packages/core/test/render3/renderer_factory_spec.ts @@ -31,7 +31,7 @@ describe('renderer factory lifecycle', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, - tag: 'some-component', + selector: [[['some-component'], null]], template: function(ctx: SomeComponent, cm: boolean) { logs.push('component'); if (cm) { @@ -45,7 +45,7 @@ describe('renderer factory lifecycle', () => { class SomeComponentWhichThrows { static ngComponentDef = defineComponent({ type: SomeComponentWhichThrows, - tag: 'some-component-with-Error', + selector: [[['some-component-with-Error'], null]], template: function(ctx: SomeComponentWhichThrows, cm: boolean) { throw(new Error('SomeComponentWhichThrows threw')); }, @@ -60,11 +60,13 @@ describe('renderer factory lifecycle', () => { } } + const defs = [SomeComponent.ngComponentDef, SomeComponentWhichThrows.ngComponentDef]; + function TemplateWithComponent(ctx: any, cm: boolean) { logs.push('function_with_component'); if (cm) { text(0, 'bar'); - elementStart(1, SomeComponent); + elementStart(1, 'some-component'); elementEnd(); } } @@ -86,7 +88,7 @@ describe('renderer factory lifecycle', () => { }); it('should work with a template', () => { - renderToHtml(Template, {}, rendererFactory); + renderToHtml(Template, {}, [], rendererFactory); expect(logs).toEqual(['create', 'begin', 'function', 'end']); logs = []; @@ -95,12 +97,12 @@ describe('renderer factory lifecycle', () => { }); it('should work with a template which contains a component', () => { - renderToHtml(TemplateWithComponent, {}, rendererFactory); + renderToHtml(TemplateWithComponent, {}, defs, rendererFactory); expect(logs).toEqual( ['create', 'begin', 'function_with_component', 'create', 'component', 'end']); logs = []; - renderToHtml(TemplateWithComponent, {}); + renderToHtml(TemplateWithComponent, {}, defs); expect(logs).toEqual(['begin', 'function_with_component', 'component', 'end']); }); @@ -122,7 +124,7 @@ describe('animation renderer factory', () => { class SomeComponent { static ngComponentDef = defineComponent({ type: SomeComponent, - tag: 'some-component', + selector: [[['some-component'], null]], template: function(ctx: SomeComponent, cm: boolean) { if (cm) { text(0, 'foo'); @@ -139,7 +141,7 @@ describe('animation renderer factory', () => { } static ngComponentDef = defineComponent({ type: SomeComponentWithAnimation, - tag: 'some-component', + selector: [[['some-component'], null]], template: function(ctx: SomeComponentWithAnimation, cm: boolean) { if (cm) { elementStart(0, 'div'); diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 1a9385bd96..b539a25a90 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -18,6 +18,7 @@ describe('ViewContainerRef', () => { static ngDirectiveDef = defineDirective({ type: TestDirective, + selector: [[['', 'testDir', ''], null]], factory: () => new TestDirective(injectViewContainerRef(), injectTemplateRef(), ), }); } @@ -27,7 +28,7 @@ describe('ViewContainerRef', () => { static ngComponentDef = defineComponent({ type: TestComponent, - tag: 'test-cmp', + selector: [[['test-cmp'], null]], factory: () => new TestComponent(), template: (cmp: TestComponent, cm: boolean) => { if (cm) { @@ -37,12 +38,13 @@ describe('ViewContainerRef', () => { } textBinding(0, bind(ctx.$implicit)); }; - container(0, [TestDirective], subTemplate); + container(0, subTemplate, undefined, ['testDir', '']); } containerRefreshStart(0); cmp.testDir = loadDirective(0); containerRefreshEnd(); }, + directiveDefs: [TestDirective.ngDirectiveDef] }); }