refactor(ivy): remove directive references from template (#22986)

PR Close #22986
This commit is contained in:
Kara Erickson 2018-03-25 21:32:39 -07:00 committed by Matias Niemelä
parent 2aabbc51fa
commit 910a16a1ff
48 changed files with 1734 additions and 1278 deletions

View File

@ -17,7 +17,7 @@ export class LargeTableComponent {
/** @nocollapse */ /** @nocollapse */
static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({ static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({
type: LargeTableComponent, type: LargeTableComponent,
tag: 'largetable', selector: [[['largetable'], null]],
template: function(ctx: LargeTableComponent, cm: boolean) { template: function(ctx: LargeTableComponent, cm: boolean) {
if (cm) { if (cm) {
E(0, 'table'); E(0, 'table');

View File

@ -37,7 +37,7 @@ export class TreeComponent {
/** @nocollapse */ /** @nocollapse */
static ngComponentDef: ComponentDef<TreeComponent> = defineComponent({ static ngComponentDef: ComponentDef<TreeComponent> = defineComponent({
type: TreeComponent, type: TreeComponent,
tag: 'tree', selector: [[['tree'], null]],
template: function(ctx: TreeComponent, cm: boolean) { template: function(ctx: TreeComponent, cm: boolean) {
if (cm) { if (cm) {
E(0, 'span'); E(0, 'span');
@ -54,7 +54,7 @@ export class TreeComponent {
let cm0 = V(0); let cm0 = V(0);
{ {
if (cm0) { if (cm0) {
E(0, TreeComponent); E(0, 'tree');
e(); e();
} }
p(0, 'data', b(ctx.data.left)); p(0, 'data', b(ctx.data.left));
@ -69,7 +69,7 @@ export class TreeComponent {
let cm0 = V(0); let cm0 = V(0);
{ {
if (cm0) { if (cm0) {
E(0, TreeComponent); E(0, 'tree');
e(); e();
} }
p(0, 'data', b(ctx.data.right)); p(0, 'data', b(ctx.data.right));
@ -80,7 +80,8 @@ export class TreeComponent {
cr(); cr();
}, },
factory: () => new TreeComponent, factory: () => new TreeComponent,
inputs: {data: 'data'} inputs: {data: 'data'},
directiveDefs: () => [TreeComponent.ngComponentDef]
}); });
} }
@ -90,7 +91,7 @@ export class TreeFunction {
/** @nocollapse */ /** @nocollapse */
static ngComponentDef: ComponentDef<TreeFunction> = defineComponent({ static ngComponentDef: ComponentDef<TreeFunction> = defineComponent({
type: TreeFunction, type: TreeFunction,
tag: 'tree', selector: [[['tree'], null]],
template: function(ctx: TreeFunction, cm: boolean) { template: function(ctx: TreeFunction, cm: boolean) {
// bit of a hack // bit of a hack
TreeTpl(ctx.data, cm); TreeTpl(ctx.data, cm);

View File

@ -60,6 +60,9 @@ export function compileDirective(
// e.g. 'type: MyDirective` // e.g. 'type: MyDirective`
field('type', outputCtx.importExpr(directive.type.reference)); field('type', outputCtx.importExpr(directive.type.reference));
// e.g. `selector: [[[null, 'someDir', ''], null]]`
field('selector', createDirectiveSelector(directive.selector !));
// e.g. `factory: () => new MyApp(injectElementRef())` // e.g. `factory: () => new MyApp(injectElementRef())`
field('factory', createFactory(directive.type, outputCtx, reflector, directive.queries)); field('factory', createFactory(directive.type, outputCtx, reflector, directive.queries));
@ -118,13 +121,11 @@ export function compileComponent(
// e.g. `type: MyApp` // e.g. `type: MyApp`
field('type', outputCtx.importExpr(component.type.reference)); field('type', outputCtx.importExpr(component.type.reference));
// e.g. `tag: 'my-app'` // e.g. `selector: [[['my-app'], null]]`
// This is optional and only included if the first selector of a component has element. field('selector', createDirectiveSelector(component.selector !));
const selector = component.selector && CssSelector.parse(component.selector); const selector = component.selector && CssSelector.parse(component.selector);
const firstSelector = selector && selector[0]; const firstSelector = selector && selector[0];
if (firstSelector && firstSelector.hasElementSelector()) {
field('tag', o.literal(firstSelector.element));
}
// e.g. `attr: ["class", ".my.app"] // e.g. `attr: ["class", ".my.app"]
// This is optional an only included if the first selector of a component specifies attributes. // This is optional an only included if the first selector of a component specifies attributes.
@ -916,6 +917,11 @@ type HostBindings = {
[key: string]: string [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( function createHostAttributesArray(
directiveMetadata: CompileDirectiveMetadata, outputCtx: OutputContext): o.Expression|null { directiveMetadata: CompileDirectiveMetadata, outputCtx: OutputContext): o.Expression|null {
const values: o.Expression[] = []; const values: o.Expression[] = [];

View File

@ -92,11 +92,12 @@ describe('compiler compliance', () => {
const ChildComponentDefinition = ` const ChildComponentDefinition = `
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ChildComponent, type: ChildComponent,
tag: 'child', selector: [[['child'], null]],
factory: function ChildComponent_Factory() { return new ChildComponent(); }, factory: function ChildComponent_Factory() { return new ChildComponent(); },
template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) { template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
$r3$.ɵT(0, 'child-view'); $r3$.ɵT(0, 'child-view');
} }
} }
});`; });`;
@ -105,6 +106,7 @@ describe('compiler compliance', () => {
const SomeDirectiveDefinition = ` const SomeDirectiveDefinition = `
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective, type: SomeDirective,
selector: [[[null, 'some-directive', ''], null]],
factory: function SomeDirective_Factory() {return new SomeDirective(); } factory: function SomeDirective_Factory() {return new SomeDirective(); }
}); });
`; `;
@ -116,7 +118,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -161,6 +163,7 @@ describe('compiler compliance', () => {
const HostBindingDirDeclaration = ` const HostBindingDirDeclaration = `
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir, type: HostBindingDir,
selector: [[[null, 'hostBindingDir', ''], null]],
factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
hostBindings: function HostBindingDir_HostBindings( hostBindings: function HostBindingDir_HostBindings(
dirIndex: $number$, elIndex: $number$) { dirIndex: $number$, elIndex: $number$) {
@ -203,6 +206,7 @@ describe('compiler compliance', () => {
const IfDirectiveDefinition = ` const IfDirectiveDefinition = `
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: IfDirective, type: IfDirective,
selector: [[[null, 'if', ''], null]],
factory: function IfDirective_Factory() { return new IfDirective($r3$.ɵinjectTemplateRef()); } factory: function IfDirective_Factory() { return new IfDirective($r3$.ɵinjectTemplateRef()); }
});`; });`;
const MyComponentDefinition = ` const MyComponentDefinition = `
@ -211,7 +215,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -283,7 +287,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -361,7 +365,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -421,7 +425,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -485,7 +489,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -542,7 +546,7 @@ describe('compiler compliance', () => {
const SimpleComponentDefinition = ` const SimpleComponentDefinition = `
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleComponent, type: SimpleComponent,
tag: 'simple', selector: [[['simple'], null]],
factory: function SimpleComponent_Factory() { return new SimpleComponent(); }, factory: function SimpleComponent_Factory() { return new SimpleComponent(); },
template: function SimpleComponent_Template(ctx: IDENT, cm: IDENT) { template: function SimpleComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -561,7 +565,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ComplexComponent, type: ComplexComponent,
tag: 'complex', selector: [[['complex'], null]],
factory: function ComplexComponent_Factory() { return new ComplexComponent(); }, factory: function ComplexComponent_Factory() { return new ComplexComponent(); },
template: function ComplexComponent_Template(ctx: IDENT, cm: IDENT) { template: function ComplexComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -627,7 +631,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ViewQueryComponent, type: ViewQueryComponent,
tag: 'view-query-component', selector: [[['view-query-component'], null]],
factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); }, factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) { template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) {
var $tmp$: $any$; var $tmp$: $any$;
@ -685,7 +689,7 @@ describe('compiler compliance', () => {
const ContentQueryComponentDefinition = ` const ContentQueryComponentDefinition = `
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ContentQueryComponent, type: ContentQueryComponent,
tag: 'content-query-component', selector: [[['content-query-component'], null]],
factory: function ContentQueryComponent_Factory() { factory: function ContentQueryComponent_Factory() {
return [new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, true)]; return [new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, true)];
}, },
@ -768,7 +772,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: IDENT, cm: IDENT) { template: function MyApp_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -809,7 +813,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -877,7 +881,7 @@ describe('compiler compliance', () => {
const LifecycleCompDefinition = ` const LifecycleCompDefinition = `
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: LifecycleComp, type: LifecycleComp,
tag: 'lifecycle-comp', selector: [[['lifecycle-comp'], null]],
factory: function LifecycleComp_Factory() { return new LifecycleComp(); }, factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
template: function LifecycleComp_Template(ctx: IDENT, cm: IDENT) {}, template: function LifecycleComp_Template(ctx: IDENT, cm: IDENT) {},
inputs: {nameMin: 'name'}, inputs: {nameMin: 'name'},
@ -889,7 +893,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleLayout, type: SimpleLayout,
tag: 'simple-layout', selector: [[['simple-layout'], null]],
factory: function SimpleLayout_Factory() { return new SimpleLayout(); }, factory: function SimpleLayout_Factory() { return new SimpleLayout(); },
template: function SimpleLayout_Template(ctx: IDENT, cm: IDENT) { template: function SimpleLayout_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -997,6 +1001,7 @@ describe('compiler compliance', () => {
const ForDirectiveDefinition = ` const ForDirectiveDefinition = `
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: ForOfDirective, type: ForOfDirective,
selector: [[[null, 'forOf', ''], null]],
factory: function ForOfDirective_Factory() { factory: function ForOfDirective_Factory() {
return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef());
}, },
@ -1010,7 +1015,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -1088,7 +1093,7 @@ describe('compiler compliance', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
@ -1141,4 +1146,4 @@ describe('compiler compliance', () => {
}); });
}); });
}); });
}); });

View File

@ -14,7 +14,7 @@ import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_facto
import {assertComponentType, assertNotNull} from './assert'; import {assertComponentType, assertNotNull} from './assert';
import {queueInitHooks, queueLifecycleHooks} from './hooks'; 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 {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode, TNodeFlags} from './interfaces/node'; import {LElementNode, TNodeFlags} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
@ -123,7 +123,9 @@ export function renderComponent<T>(
const componentDef = (componentType as ComponentType<T>).ngComponentDef as ComponentDef<T>; const componentDef = (componentType as ComponentType<T>).ngComponentDef as ComponentDef<T>;
if (componentDef.type != componentType) componentDef.type = componentType; if (componentDef.type != componentType) componentDef.type = componentType;
let component: T; 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 = { const rootContext: RootContext = {
// Incomplete initialization due to circular reference. // Incomplete initialization due to circular reference.
component: null !, component: null !,
@ -131,28 +133,32 @@ export function renderComponent<T>(
clean: CLEAN_PROMISE, clean: CLEAN_PROMISE,
}; };
const rootView = createLView( const rootView = createLView(
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView(), null, -1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView(null),
rootContext, componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); null, rootContext, componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
const oldView = enterView(rootView, null !); const oldView = enterView(rootView, null !);
let elementNode: LElementNode; let elementNode: LElementNode;
try { try {
if (rendererFactory.begin) rendererFactory.begin();
// Create element node at index 0 in data array // 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 // Create directive instance with factory() and store at index 0 in directives array
component = rootContext.component = component = rootContext.component =
baseDirectiveCreate(0, componentDef.factory(), componentDef) as T; 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 { } finally {
// We must not use leaveView here because it will set creationMode to false too early, leaveView(oldView);
// causing init-only hooks not to run. The detectChanges call below will execute if (rendererFactory.end) rendererFactory.end();
// leaveView at the appropriate time in the lifecycle.
enterView(oldView, null);
} }
opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef));
renderComponentOrTemplate(elementNode, rootView, component);
return component; return component;
} }

View File

@ -16,7 +16,8 @@ import {Type} from '../type';
import {resolveRendererType2} from '../view/util'; import {resolveRendererType2} from '../view/util';
import {diPublic} from './di'; 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<T>(componentDefinition: {
*/ */
type: Type<T>; type: Type<T>;
/** The selector that will be used to match nodes to this component. */
selector: CssSelector;
/** /**
* Factory method used to create an instance of directive. * Factory method used to create an instance of directive.
*/ */
@ -90,11 +94,6 @@ export function defineComponent<T>(componentDefinition: {
*/ */
exportAs?: string; exportAs?: string;
/**
* HTML tag name to use in place where this component should be instantiated.
*/
tag: string;
/** /**
* Template function use for rendering DOM. * Template function use for rendering DOM.
* *
@ -147,13 +146,20 @@ export function defineComponent<T>(componentDefinition: {
* Defines the set of injectable objects that are visible to its view DOM children. * Defines the set of injectable objects that are visible to its view DOM children.
*/ */
viewProviders?: Provider[]; 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<T> { }): ComponentDef<T> {
const type = componentDefinition.type; const type = componentDefinition.type;
const def = <ComponentDef<any>>{ const def = <ComponentDef<any>>{
type: type, type: type,
diPublic: null, diPublic: null,
factory: componentDefinition.factory, factory: componentDefinition.factory,
tag: componentDefinition.tag || null !,
template: componentDefinition.template || null !, template: componentDefinition.template || null !,
hostBindings: componentDefinition.hostBindings || null, hostBindings: componentDefinition.hostBindings || null,
attributes: componentDefinition.attributes || null, attributes: componentDefinition.attributes || null,
@ -168,7 +174,9 @@ export function defineComponent<T>(componentDefinition: {
afterViewInit: type.prototype.ngAfterViewInit || null, afterViewInit: type.prototype.ngAfterViewInit || null,
afterViewChecked: type.prototype.ngAfterViewChecked || null, afterViewChecked: type.prototype.ngAfterViewChecked || null,
onDestroy: type.prototype.ngOnDestroy || 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; const feature = componentDefinition.features;
feature && feature.forEach((fn) => fn(def)); feature && feature.forEach((fn) => fn(def));
@ -300,6 +308,9 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
*/ */
type: Type<T>; type: Type<T>;
/** The selector that will be used to match nodes to this directive. */
selector: CssSelector;
/** /**
* Factory method used to create an instance of directive. * Factory method used to create an instance of directive.
*/ */

View File

@ -19,7 +19,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
import {Type} from '../type'; import {Type} from '../type';
import {assertLessThan, assertNotNull} from './assert'; 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 {ComponentTemplate, DirectiveDef} from './interfaces/definition';
import {LInjector} from './interfaces/injector'; import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node';
@ -299,12 +299,10 @@ export function getOrCreateChangeDetectorRef(
if (di.changeDetectorRef) return di.changeDetectorRef; if (di.changeDetectorRef) return di.changeDetectorRef;
const currentNode = di.node; const currentNode = di.node;
if (currentNode.data === null) { if (isComponent(currentNode.tNode !)) {
// 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
return di.changeDetectorRef = createViewRef(currentNode.data as LView, context); return di.changeDetectorRef = createViewRef(currentNode.data as LView, context);
} else if (currentNode.type === LNodeType.Element) {
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
} }
return null !; return null !;
} }
@ -388,7 +386,7 @@ export function getOrCreateInjectable<T>(
// The size of the node's directive's list is stored in certain bits of the node's flags, // 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. // so exact it with a mask and shift it back such that the bits reflect the real value.
const flags = node.tNode !.flags; const flags = node.tNode !.flags;
const size = flags & TNodeFlags.SIZE_MASK; const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
if (size !== 0) { if (size !== 0) {
// The start index of the directives list is also part of the node's flags, but there is // 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<C>( createEmbeddedView<C>(
templateRef: viewEngine_TemplateRef<C>, context?: C|undefined, templateRef: viewEngine_TemplateRef<C>, context?: C|undefined,
index?: number|undefined): viewEngine_EmbeddedViewRef<C> { index?: number|undefined): viewEngine_EmbeddedViewRef<C> {
// set current view to container node's view
enterView(this._node.view, null);
const viewRef = templateRef.createEmbeddedView(context !); const viewRef = templateRef.createEmbeddedView(context !);
this.insert(viewRef, index); this.insert(viewRef, index);
return viewRef; return viewRef;

View File

@ -45,7 +45,8 @@ export function queueLifecycleHooks(flags: number, currentView: LView): void {
const tView = currentView.tView; const tView = currentView.tView;
if (tView.firstTemplatePass === true) { if (tView.firstTemplatePass === true) {
const start = flags >> TNodeFlags.INDX_SHIFT; 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 // 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 // directiveCreate) so we can preserve the current hook order. Content, view, and destroy

View File

@ -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 {LContainerNode, LElementNode, LNode, LNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './interfaces/node';
import {assertNodeType} from './node_assert'; import {assertNodeType} from './node_assert';
import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode} from './node_manipulation'; import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode} from './node_manipulation';
import {matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelector, matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition'; import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType} from './interfaces/definition';
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {isDifferent, stringify} from './util'; import {isDifferent, stringify} from './util';
import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks'; 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. * 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. */ /** Sets the host bindings for the current view. */
function setHostBindings(bindings: number[] | null): void { export function setHostBindings(bindings: number[] | null): void {
if (bindings != null) { if (bindings != null) {
const defs = currentView.tView.directives !; const defs = currentView.tView.directives !;
for (let i = 0; i < bindings.length; i += 2) { 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) { if (!checkNoChangesMode) {
const tView = currentView.tView; const tView = currentView.tView;
executeInitHooks(currentView, tView, creationMode); 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 template Template function with the instructions.
* @param context to pass into the template. * @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<T>( export function renderTemplate<T>(
hostNode: RElement, template: ComponentTemplate<T>, context: T, hostNode: RElement, template: ComponentTemplate<T>, context: T,
providedRendererFactory: RendererFactory3, host: LElementNode | null): LElementNode { providedRendererFactory: RendererFactory3, host: LElementNode | null,
directiveRegistry: DirectiveDefListOrFactory | null = null): LElementNode {
if (host == null) { if (host == null) {
resetApplicationState(); resetApplicationState();
rendererFactory = providedRendererFactory; rendererFactory = providedRendererFactory;
host = createLNode( host = createLNode(
null, LNodeType.Element, hostNode, null, LNodeType.Element, hostNode,
createLView( createLView(
-1, providedRendererFactory.createRenderer(null, null), getOrCreateTView(template), -1, providedRendererFactory.createRenderer(null, null),
null, {}, LViewFlags.CheckAlways)); getOrCreateTView(template, directiveRegistry), null, {}, LViewFlags.CheckAlways));
} }
const hostView = host.data !; const hostView = host.data !;
ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.'); ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.');
@ -427,8 +431,9 @@ export function renderEmbeddedTemplate<T>(
previousOrParentNode = null !; previousOrParentNode = null !;
let cm: boolean = false; let cm: boolean = false;
if (viewNode == null) { if (viewNode == null) {
const view = const view = createLView(
createLView(-1, renderer, createTView(), template, context, LViewFlags.CheckAlways); -1, renderer, createTView(currentView.tView.directiveRegistry), template, context,
LViewFlags.CheckAlways);
viewNode = createLNode(null, LNodeType.View, null, view); viewNode = createLNode(null, LNodeType.View, null, view);
cm = true; cm = true;
} }
@ -460,7 +465,7 @@ export function renderComponentOrTemplate<T>(
// Element was stored at 0 in data and directive was stored at 0 in directives // Element was stored at 0 in data and directive was stored at 0 in directives
// in renderComponent() // in renderComponent()
setHostBindings(rootDirectiveIndices); setHostBindings(_ROOT_DIRECTIVE_INDICES);
componentRefresh(0, 0); componentRefresh(0, 0);
} }
} finally { } finally {
@ -479,9 +484,8 @@ export function renderComponentOrTemplate<T>(
* Create DOM element. The instruction must later be followed by `elementEnd()` call. * Create DOM element. The instruction must later be followed by `elementEnd()` call.
* *
* @param index Index of the element in the data array * @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 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. * @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 * Attributes and localRefs are passed as an array of strings where elements with an even index
@ -489,77 +493,71 @@ export function renderComponentOrTemplate<T>(
* ['id', 'warning5', 'class', 'alert'] * ['id', 'warning5', 'class', 'alert']
*/ */
export function elementStart( export function elementStart(
index: number, nameOrComponentType?: string | ComponentType<any>, attrs?: string[] | null, index: number, name?: string, attrs?: string[] | null, localRefs?: string[] | null): RElement {
directiveTypes?: DirectiveType<any>[] | null, localRefs?: string[] | null): RElement {
let node: LElementNode; let node: LElementNode;
let native: RElement; let native: RElement;
if (nameOrComponentType == null) { if (name == null) {
// native node retrieval - used for exporting elements as tpl local variables (<div #foo>) // native node retrieval - used for exporting elements as tpl local variables (<div #foo>)
const node = data[index] !; const node = data[index] !;
native = node && (node as LElementNode).native; native = node && (node as LElementNode).native;
} else { } else {
ngDevMode && ngDevMode &&
assertNull(currentView.bindingStartIndex, 'elements should be created before any bindings'); assertNull(currentView.bindingStartIndex, 'elements should be created before any bindings');
const isHostElement = typeof nameOrComponentType !== 'string';
let hostComponentDef: ComponentDef<any>|null = null; native = renderer.createElement(name);
let name = nameOrComponentType as string; node = createLNode(index, LNodeType.Element, native !, null);
let directiveIndex = getNextDirectiveIndex(index); if (attrs) setUpAttributes(native, attrs);
appendChild(node.parent !, native, currentView);
if (isHostElement) { if (firstTemplatePass) {
hostComponentDef = firstTemplatePass ? const tNode = createTNode(name, attrs || null, null, null);
(nameOrComponentType as ComponentType<any>).ngComponentDef : cacheMatchingDirectivesForNode(tNode);
currentView.tView.directives ![directiveIndex] as ComponentDef<any>;
name = hostComponentDef !.tag; ngDevMode && assertDataInRange(index - 1);
} node.tNode = tData[index] = tNode;
if (name === null) { if (!isComponent(tNode)) {
// TODO: future support for nameless components. tNode.localNames = findMatchingLocalNames(null, localRefs, -1, '');
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);
} }
hack_declareDirectives(index, localRefs);
} }
return native; 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<any>).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. */ /** Stores index of component's host element so it will be queued for view refresh during CD. */
function queueComponentIndexForCheck(dirIndex: number, elIndex: number): void { function queueComponentIndexForCheck(dirIndex: number, elIndex: number): void {
if (firstTemplatePass) { if (firstTemplatePass) {
@ -576,30 +574,35 @@ function queueHostBindingForCheck(dirIndex: number, elIndex: number): void {
} }
/** Sets the context for a ChangeDetectorRef to the given instance. */ /** 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) { if (injector && injector.changeDetectorRef != null) {
(injector.changeDetectorRef as ViewRef<any>)._setComponentContext(instance); (injector.changeDetectorRef as ViewRef<any>)._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 * This function instantiates the given directives. It is a hack since it assumes the directives
* come in the correct order for DI. * come in the correct order for DI.
*/ */
function hack_declareDirectives( function hack_declareDirectives(elementIndex: number, localRefs: string[] | null | undefined) {
index: number, elementIndex: number, directiveTypes: DirectiveType<any>[] | null | undefined, const size = (previousOrParentNode.tNode !.flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
localRefs: string[] | null | undefined, ) {
if (directiveTypes) { if (size > 0) {
const defs = currentView.tView.directives !; 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 // TODO(mhevery): This assumes that the directives come in correct order, which
// is not guaranteed. Must be refactored to take it into account. // is not guaranteed. Must be refactored to take it into account.
for (let i = 0; i < directiveTypes.length; i++) { for (let i = startIndex; i < endIndex; i++) {
const directiveType = directiveTypes[i]; const def = tDirectives[i] as DirectiveDef<any>;
const directiveDef = directiveCreate(elementIndex, def.factory(), def, localRefs);
firstTemplatePass ? directiveType.ngDirectiveDef : defs[index] as DirectiveDef<any>; startIndex++;
directiveCreate(index, elementIndex, directiveDef.factory(), directiveDef, localRefs);
index++;
} }
} }
} }
@ -633,12 +636,13 @@ function findMatchingLocalNames(
* @param template The template from which to get static data * @param template The template from which to get static data
* @returns TView * @returns TView
*/ */
function getOrCreateTView(template: ComponentTemplate<any>): TView { function getOrCreateTView(
return template.ngPrivateData || (template.ngPrivateData = createTView() as never); template: ComponentTemplate<any>, defs: DirectiveDefListOrFactory | null): TView {
return template.ngPrivateData || (template.ngPrivateData = createTView(defs) as never);
} }
/** Creates a TView instance */ /** Creates a TView instance */
export function createTView(): TView { export function createTView(defs: DirectiveDefListOrFactory | null): TView {
return { return {
data: [], data: [],
directives: null, directives: null,
@ -652,7 +656,8 @@ export function createTView(): TView {
destroyHooks: null, destroyHooks: null,
pipeDestroyHooks: null, pipeDestroyHooks: null,
hostBindings: null, hostBindings: null,
components: null components: null,
directiveRegistry: typeof defs === 'function' ? defs() : defs
}; };
} }
@ -708,13 +713,22 @@ export function locateHostElement(
* *
* @returns LElementNode created * @returns LElementNode created
*/ */
export function hostElement(rNode: RElement | null, def: ComponentDef<any>): LElementNode { export function hostElement(
tag: string, rNode: RElement | null, def: ComponentDef<any>): LElementNode {
resetApplicationState(); resetApplicationState();
const node = createLNode( const node = createLNode(
0, LNodeType.Element, rNode, createLView( 0, LNodeType.Element, rNode,
-1, renderer, getOrCreateTView(def.template), null, null, createLView(
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways)); -1, renderer, getOrCreateTView(def.template, def.directiveDefs), null, null,
if (firstTemplatePass) node.tNode = createTNode(def.tag, null, 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; return node;
} }
@ -898,7 +912,7 @@ function setInputsForProperty(inputs: PropertyAliasValue, value: any): void {
*/ */
function generatePropertyAliases( function generatePropertyAliases(
tNodeFlags: TNodeFlags, direction: BindingDirection): PropertyAliases|null { 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; let propStore: PropertyAliases|null = null;
if (size > 0) { if (size > 0) {
@ -1101,20 +1115,25 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
* NOTE: directives can be created in order other than the index order. They can also * 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. * 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 elementIndex Index of the host element in the data array
* @param directive The directive instance. * @param directive The directive instance.
* @param directiveDef DirectiveDef object which contains information about the template. * @param directiveDef DirectiveDef object which contains information about the template.
* @param localRefs Names under which a query can retrieve the directive instance * @param localRefs Names under which a query can retrieve the directive instance
*/ */
export function directiveCreate<T>( export function directiveCreate<T>(
index: number, elementIndex: number, directive: T, directiveDef: DirectiveDef<T>, elementIndex: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>,
localRefs?: string[] | null): T { localRefs?: string[] | null): T {
const index = directives ? directives.length : 0;
const instance = baseDirectiveCreate(index, directive, directiveDef); const instance = baseDirectiveCreate(index, directive, directiveDef);
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode'); ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
const tNode: TNode|null = previousOrParentNode.tNode !; const tNode: TNode|null = previousOrParentNode.tNode !;
const isComponent = (directiveDef as ComponentDef<any>).template;
if (isComponent) {
addComponentLogic(index, elementIndex, directive, directiveDef as ComponentDef<any>);
}
if (firstTemplatePass) { if (firstTemplatePass) {
// Init hooks are queued now so ngOnInit is called in host components before // Init hooks are queued now so ngOnInit is called in host components before
// any projected components. // any projected components.
@ -1123,7 +1142,8 @@ export function directiveCreate<T>(
if (directiveDef.hostBindings) queueHostBindingForCheck(index, elementIndex); if (directiveDef.hostBindings) queueHostBindingForCheck(index, elementIndex);
if (localRefs) { if (localRefs) {
const localNames = findMatchingLocalNames(directiveDef, localRefs, index); const localNames =
findMatchingLocalNames(directiveDef, localRefs, index, isComponent ? '' : undefined);
tNode.localNames = tNode.localNames =
localNames && tNode.localNames ? tNode.localNames.concat(localNames) : localNames; localNames && tNode.localNames ? tNode.localNames.concat(localNames) : localNames;
} }
@ -1136,6 +1156,26 @@ export function directiveCreate<T>(
return instance; return instance;
} }
function addComponentLogic<T>(
index: number, elementIndex: number, instance: T, def: ComponentDef<any>): 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 * A lighter version of directiveCreate() that is used for the root component
* *
@ -1143,7 +1183,7 @@ export function directiveCreate<T>(
* current Angular. Example: local refs and inputs on root component. * current Angular. Example: local refs and inputs on root component.
*/ */
export function baseDirectiveCreate<T>( export function baseDirectiveCreate<T>(
index: number, directive: T, directiveDef: DirectiveDef<T>): T { index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<any>): T {
ngDevMode && ngDevMode &&
assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings'); assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings');
ngDevMode && assertPreviousIsParent(); ngDevMode && assertPreviousIsParent();
@ -1156,17 +1196,6 @@ export function baseDirectiveCreate<T>(
ngDevMode && assertDataNext(index, directives); ngDevMode && assertDataNext(index, directives);
directives[index] = directive; 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; const diPublic = directiveDef !.diPublic;
if (diPublic) { if (diPublic) {
diPublic(directiveDef !); diPublic(directiveDef !);
@ -1253,8 +1282,8 @@ function generateInitialInputs(
* @param localRefs A set of local reference bindings on the element. * @param localRefs A set of local reference bindings on the element.
*/ */
export function container( export function container(
index: number, directiveTypes?: DirectiveType<any>[], template?: ComponentTemplate<any>, index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[],
tagName?: string, attrs?: string[], localRefs?: string[] | null): void { localRefs?: string[] | null): void {
ngDevMode && ngDevMode &&
assertNull( assertNull(
currentView.bindingStartIndex, 'container nodes should be created before any bindings'); 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 // Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted. // because views can be removed and re-inserted.
addToViewTree(node.data); addToViewTree(node.data);
hack_declareDirectives(getNextDirectiveIndex(index), index, directiveTypes, localRefs);
if (firstTemplatePass) cacheMatchingDirectivesForNode(node.tNode);
hack_declareDirectives(index, localRefs);
isParent = false; isParent = false;
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container); 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. * 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)); enterView(newView, createLNode(null, LNodeType.View, null, newView));
} }
return !existingViewNode; return !existingViewNode;
} }
@ -1441,7 +1465,7 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV
ngDevMode && assertNodeType(parent, LNodeType.Container); ngDevMode && assertNodeType(parent, LNodeType.Container);
const tContainer = (parent !.tNode as TContainerNode).data; const tContainer = (parent !.tNode as TContainerNode).data;
if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) { if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) {
tContainer[viewIndex] = createTView(); tContainer[viewIndex] = createTView(currentView.tView.directiveRegistry);
} }
return tContainer[viewIndex]; return tContainer[viewIndex];
} }
@ -1487,9 +1511,10 @@ export function componentRefresh<T>(directiveIndex: number, elementIndex: number
// Only attached CheckAlways components or attached, dirty OnPush components should be checked // Only attached CheckAlways components or attached, dirty OnPush components should be checked
if (viewAttached(hostView) && hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) { if (viewAttached(hostView) && hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
ngDevMode && assertDataInRange(directiveIndex, directives !); ngDevMode && assertDataInRange(directiveIndex, directives !);
const template = (currentView.tView.directives ![directiveIndex] as ComponentDef<any>).template; const def = currentView.tView.directives ![directiveIndex] as ComponentDef<any>;
detectChangesInternal( detectChangesInternal(
hostView, element, template, getDirectiveInstance<T>(directives ![directiveIndex])); hostView, element, def, getDirectiveInstance<T>(directives ![directiveIndex]));
} }
} }
@ -1795,8 +1820,8 @@ export function detectChanges<T>(component: T): void {
const hostNode = _getComponentHostLElementNode(component); const hostNode = _getComponentHostLElementNode(component);
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView'); ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
const componentIndex = hostNode.tNode !.flags >> TNodeFlags.INDX_SHIFT; const componentIndex = hostNode.tNode !.flags >> TNodeFlags.INDX_SHIFT;
const template = (hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>).template; const def = hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>;
detectChangesInternal(hostNode.data as LView, hostNode, template, component); 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. */ /** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
export function detectChangesInternal<T>( export function detectChangesInternal<T>(
hostView: LView, hostNode: LElementNode, template: ComponentTemplate<any>, component: T) { hostView: LView, hostNode: LElementNode, def: ComponentDef<any>, component: T) {
const oldView = enterView(hostView, hostNode); const oldView = enterView(hostView, hostNode);
const template = def.template;
try { try {
template(component, creationMode); template(component, creationMode);
refreshDynamicChildren(); refreshDynamicChildren();
@ -2150,3 +2177,4 @@ export function _getComponentHostLElementNode<T>(component: T): LElementNode {
} }
export const CLEAN_PROMISE = _CLEAN_PROMISE; export const CLEAN_PROMISE = _CLEAN_PROMISE;
export const ROOT_DIRECTIVE_INDICES = _ROOT_DIRECTIVE_INDICES;

View File

@ -12,7 +12,7 @@ import {Provider} from '../../core';
import {RendererType2} from '../../render/api'; import {RendererType2} from '../../render/api';
import {Type} from '../../type'; import {Type} from '../../type';
import {resolveRendererType2} from '../../view/util'; import {resolveRendererType2} from '../../view/util';
import {CssSelector} from './projection';
/** /**
@ -61,6 +61,9 @@ export interface DirectiveDef<T> {
/** Function that makes a directive public to the DI system. */ /** Function that makes a directive public to the DI system. */
diPublic: ((def: DirectiveDef<any>) => void)|null; diPublic: ((def: DirectiveDef<any>) => 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 * 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 * are their aliases if any, or their original unminified property names
@ -122,13 +125,6 @@ export interface DirectiveDef<T> {
* See: {@link defineComponent} * See: {@link defineComponent}
*/ */
export interface ComponentDef<T> extends DirectiveDef<T> { export interface ComponentDef<T> extends DirectiveDef<T> {
/**
* 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. * The View template of the component.
* *
@ -157,6 +153,14 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
* children only. * children only.
*/ */
readonly viewProviders?: Provider[]; 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<T> {
export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<T>) => void; export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<T>) => void;
export type ComponentDefFeature = <T>(componentDef: ComponentDef<T>) => void; export type ComponentDefFeature = <T>(componentDef: ComponentDef<T>) => 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<any>| ComponentDef<any>)[];
// Note: This hack is necessary so we don't erroneously get a circular dependency // Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types. // failure based on types.
export const unusedValueExportToPlacateAjd = 1; export const unusedValueExportToPlacateAjd = 1;

View File

@ -32,7 +32,19 @@ export const enum LNodeType {
* on how to map a particular set of bits to the node's first directive index * 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) * (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. * LNode is an internal data structure which is used for the incremental DOM algorithm.

View File

@ -7,7 +7,7 @@
*/ */
import {LContainer} from './container'; 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 {LElementNode, LViewNode, TNode} from './node';
import {LQueries} from './query'; import {LQueries} from './query';
import {Renderer3} from './renderer'; import {Renderer3} from './renderer';
@ -219,19 +219,31 @@ export interface LViewOrLContainer {
* Stored on the template function as ngPrivateData. * Stored on the template function as ngPrivateData.
*/ */
export interface TView { export interface TView {
/** Whether or not this template has been processed. */
firstTemplatePass: boolean;
/** Static data equivalent of LView.data[]. Contains TNodes. */ /** Static data equivalent of LView.data[]. Contains TNodes. */
data: TData; 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 * Defs are stored at the same index in TView.directives[] as their instances
* are stored in LView.directives[]. This simplifies lookup in DI. * are stored in LView.directives[]. This simplifies lookup in DI.
*/ */
directives: (ComponentDef<any>|DirectiveDef<any>)[]|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 * Array of ngOnInit and ngDoCheck hooks that should be executed for this view in

View File

@ -196,8 +196,8 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
function geIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null { function geIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
const defs = node.view.tView.directives !; const defs = node.view.tView.directives !;
const flags = node.tNode !.flags; const flags = node.tNode !.flags;
for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + (flags & TNodeFlags.SIZE_MASK); i < ii; const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
i++) { for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + size; i < ii; i++) {
const def = defs[i] as DirectiveDef<any>; const def = defs[i] as DirectiveDef<any>;
if (def.diPublic && def.type === type) { if (def.diPublic && def.type === type) {
return i; return i;

View File

@ -21,7 +21,10 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
constructor(private _view: LView, context: T|null, ) { this.context = context !; } constructor(private _view: LView, context: T|null, ) { this.context = context !; }
/** @internal */ /** @internal */
_setComponentContext(context: T) { this.context = context; } _setComponentContext(view: LView, context: T) {
this._view = view;
this.context = context;
}
destroy(): void { notImplemented(); } destroy(): void { notImplemented(); }
destroyed: boolean; destroyed: boolean;
@ -223,9 +226,9 @@ export class EmbeddedViewRef<T> extends ViewRef<T> {
* @param context The context for this view * @param context The context for this view
* @returns The ViewRef * @returns The ViewRef
*/ */
export function createViewRef<T>(view: LView, context: T): ViewRef<T> { export function createViewRef<T>(view: LView | null, context: T): ViewRef<T> {
// TODO: add detectChanges back in when implementing ChangeDetectorRef.detectChanges // 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. */ /** Interface for destroy logic. Implemented by addDestroyable. */

View File

@ -44,6 +44,9 @@
{ {
"name": "QueueAction" "name": "QueueAction"
}, },
{
"name": "ROOT_DIRECTIVE_INDICES"
},
{ {
"name": "SafeSubscriber" "name": "SafeSubscriber"
}, },
@ -107,6 +110,9 @@
{ {
"name": "baseDirectiveCreate" "name": "baseDirectiveCreate"
}, },
{
"name": "buildTNodeFlags"
},
{ {
"name": "callHooks" "name": "callHooks"
}, },
@ -239,9 +245,6 @@
{ {
"name": "renderComponent" "name": "renderComponent"
}, },
{
"name": "renderComponentOrTemplate"
},
{ {
"name": "renderEmbeddedTemplate" "name": "renderEmbeddedTemplate"
}, },
@ -251,9 +254,6 @@
{ {
"name": "resolveRendererType2" "name": "resolveRendererType2"
}, },
{
"name": "rootDirectiveIndices"
},
{ {
"name": "runIfPresent" "name": "runIfPresent"
}, },

View File

@ -34,7 +34,7 @@ describe('iv perf test', () => {
class Component { class Component {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: Component, type: Component,
tag: 'div', selector: [[['div'], null]],
template: function Template(ctx: any, cm: any) { template: function Template(ctx: any, cm: any) {
if (cm) { if (cm) {
container(0); container(0);

View File

@ -13,7 +13,7 @@ import {getRenderedText, whenRendered} from '../../src/render3/component';
import {LifecycleHooksFeature, defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index'; 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 {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', () => { describe('change detection', () => {
@ -25,7 +25,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: () => new MyComponent(), factory: () => new MyComponent(),
template: (ctx: MyComponent, cm: boolean) => { template: (ctx: MyComponent, cm: boolean) => {
if (cm) { if (cm) {
@ -96,7 +96,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: () => comp = new MyComponent(), factory: () => comp = new MyComponent(),
/** /**
* {{ doCheckCount }} - {{ name }} * {{ doCheckCount }} - {{ name }}
@ -123,16 +123,17 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(), factory: () => new MyApp(),
/** <my-comp [name]="name"></my-comp> */ /** <my-comp [name]="name"></my-comp> */
template: (ctx: MyApp, cm: boolean) => { template: (ctx: MyApp, cm: boolean) => {
if (cm) { if (cm) {
elementStart(0, MyComponent); elementStart(0, 'my-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'name', bind(ctx.name)); 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', () => { it('should not check OnPush components in update mode when parent events occur', () => {
class ButtonParent { function noop() {}
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(),
/**
* <my-comp></my-comp>
* <button id="parent" (click)="noop()"></button>
*/
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); const buttonParent = renderComponent(ButtonParent);
expect(getRenderedText(buttonParent)).toEqual('1 - Nancy'); expect(getRenderedText(buttonParent)).toEqual('1 - Nancy');
@ -232,35 +223,28 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ButtonParent, type: ButtonParent,
tag: 'button-parent', selector: [[['button-parent'], null]],
factory: () => parent = new ButtonParent(), factory: () => parent = new ButtonParent(),
/** {{ doCheckCount }} - <my-comp></my-comp> */ /** {{ doCheckCount }} - <my-comp></my-comp> */
template: (ctx: ButtonParent, cm: boolean) => { template: (ctx: ButtonParent, cm: boolean) => {
if (cm) { if (cm) {
text(0); text(0);
elementStart(1, MyComponent); elementStart(1, 'my-comp');
elementEnd(); elementEnd();
} }
textBinding(0, interpolation1('', ctx.doCheckCount, ' - ')); textBinding(0, interpolation1('', ctx.doCheckCount, ' - '));
}, },
directiveDefs: () => [MyComponent.ngComponentDef],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}); });
} }
class MyButtonApp { const MyButtonApp = createComponent('my-button-app', function(ctx: any, cm: boolean) {
static ngComponentDef = defineComponent({ if (cm) {
type: MyButtonApp, elementStart(0, 'button-parent');
tag: 'my-button-app', elementEnd();
factory: () => new MyButtonApp(), }
/** <button-parent></button-parent> */ }, [ButtonParent.ngComponentDef]);
template: (ctx: MyButtonApp, cm: boolean) => {
if (cm) {
elementStart(0, ButtonParent);
elementEnd();
}
}
});
}
const myButtonApp = renderComponent(MyButtonApp); const myButtonApp = renderComponent(MyButtonApp);
expect(parent !.doCheckCount).toEqual(1); expect(parent !.doCheckCount).toEqual(1);
@ -298,7 +282,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: () => myComp = new MyComp(injectChangeDetectorRef()), factory: () => myComp = new MyComp(injectChangeDetectorRef()),
/** {{ name }} */ /** {{ name }} */
template: (ctx: MyComp, cm: boolean) => { template: (ctx: MyComp, cm: boolean) => {
@ -320,7 +304,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ParentComp, type: ParentComp,
tag: 'parent-comp', selector: [[['parent-comp'], null]],
factory: () => new ParentComp(injectChangeDetectorRef()), factory: () => new ParentComp(injectChangeDetectorRef()),
/** /**
* {{ doCheckCount}} - * {{ doCheckCount}} -
@ -329,19 +313,23 @@ describe('change detection', () => {
template: (ctx: ParentComp, cm: boolean) => { template: (ctx: ParentComp, cm: boolean) => {
if (cm) { if (cm) {
text(0); text(0);
elementStart(1, MyComp); elementStart(1, 'my-comp');
elementEnd(); elementEnd();
} }
textBinding(0, interpolation1('', ctx.doCheckCount, ' - ')); textBinding(0, interpolation1('', ctx.doCheckCount, ' - '));
} },
directiveDefs: () => [MyComp.ngComponentDef]
}); });
} }
class Dir { class Dir {
constructor(public cdr: ChangeDetectorRef) {} constructor(public cdr: ChangeDetectorRef) {}
static ngDirectiveDef = static ngDirectiveDef = defineDirective({
defineDirective({type: Dir, factory: () => dir = new Dir(injectChangeDetectorRef())}); 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', () => { it('should check component view when called by directive on component node', () => {
class MyApp { /** <my-comp dir></my-comp> */
static ngComponentDef = defineComponent({ const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) {
type: MyApp, if (cm) {
tag: 'my-app', elementStart(0, 'my-comp', ['dir', '']);
factory: () => new MyApp(), elementEnd();
/** <my-comp dir></my-comp> */ }
template: (ctx: MyApp, cm: boolean) => { }, [MyComp.ngComponentDef, Dir.ngDirectiveDef]);
if (cm) {
elementStart(0, MyComp, ['dir', ''], [Dir]);
elementEnd();
}
}
});
}
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(getRenderedText(app)).toEqual('Nancy'); expect(getRenderedText(app)).toEqual('Nancy');
@ -423,32 +404,25 @@ describe('change detection', () => {
}); });
it('should check host component when called by directive on element node', () => { it('should check host component when called by directive on element node', () => {
class MyApp { /**
name = 'Frank'; * {{ name }}
* <div dir></div>
static ngComponentDef = defineComponent({ */
type: MyApp, const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) {
tag: 'my-app', if (cm) {
factory: () => new MyApp(), text(0);
/** elementStart(1, 'div', ['dir', '']);
* {{ name }} elementEnd();
* <div dir></div> }
*/ textBinding(1, bind(ctx.value));
template: (ctx: MyApp, cm: boolean) => { }, [Dir.ngDirectiveDef]);
if (cm) {
text(0);
elementStart(1, 'div', ['dir', ''], [Dir]);
elementEnd();
}
textBinding(1, bind(ctx.name));
}
});
}
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
app.value = 'Frank';
tick(app);
expect(getRenderedText(app)).toEqual('Frank'); expect(getRenderedText(app)).toEqual('Frank');
app.name = 'Joe'; app.value = 'Joe';
dir !.cdr.detectChanges(); dir !.cdr.detectChanges();
expect(getRenderedText(app)).toEqual('Joe'); expect(getRenderedText(app)).toEqual('Joe');
}); });
@ -462,7 +436,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** /**
* {{ name}} * {{ name}}
@ -480,14 +454,15 @@ describe('change detection', () => {
{ {
if (ctx.showing) { if (ctx.showing) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'div', ['dir', ''], [Dir]); elementStart(0, 'div', ['dir', '']);
elementEnd(); elementEnd();
} }
} }
embeddedViewEnd(); embeddedViewEnd();
} }
containerRefreshEnd(); containerRefreshEnd();
} },
directiveDefs: [Dir.ngDirectiveDef]
}); });
} }
@ -512,7 +487,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: DetectChangesComp, type: DetectChangesComp,
tag: 'detect-changes-comp', selector: [[['detect-changes-comp'], null]],
factory: () => new DetectChangesComp(injectChangeDetectorRef()), factory: () => new DetectChangesComp(injectChangeDetectorRef()),
/** {{ value }} */ /** {{ value }} */
template: (ctx: DetectChangesComp, cm: boolean) => { template: (ctx: DetectChangesComp, cm: boolean) => {
@ -541,7 +516,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: DetectChangesComp, type: DetectChangesComp,
tag: 'detect-changes-comp', selector: [[['detect-changes-comp'], null]],
factory: () => new DetectChangesComp(injectChangeDetectorRef()), factory: () => new DetectChangesComp(injectChangeDetectorRef()),
/** {{ doCheckCount }} */ /** {{ doCheckCount }} */
template: (ctx: DetectChangesComp, cm: boolean) => { template: (ctx: DetectChangesComp, cm: boolean) => {
@ -567,15 +542,16 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** <detached-comp></detached-comp> */ /** <detached-comp></detached-comp> */
template: (ctx: MyApp, cm: boolean) => { template: (ctx: MyApp, cm: boolean) => {
if (cm) { if (cm) {
elementStart(0, DetachedComp); elementStart(0, 'detached-comp');
elementEnd(); elementEnd();
} }
} },
directiveDefs: () => [DetachedComp.ngComponentDef]
}); });
} }
@ -589,7 +565,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: DetachedComp, type: DetachedComp,
tag: 'detached-comp', selector: [[['detached-comp'], null]],
factory: () => comp = new DetachedComp(injectChangeDetectorRef()), factory: () => comp = new DetachedComp(injectChangeDetectorRef()),
/** {{ value }} */ /** {{ value }} */
template: (ctx: DetachedComp, cm: boolean) => { template: (ctx: DetachedComp, cm: boolean) => {
@ -685,7 +661,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: OnPushComp, type: OnPushComp,
tag: 'on-push-comp', selector: [[['on-push-comp'], null]],
factory: () => onPushComp = new OnPushComp(injectChangeDetectorRef()), factory: () => onPushComp = new OnPushComp(injectChangeDetectorRef()),
/** {{ value }} */ /** {{ value }} */
template: (ctx: OnPushComp, cm: boolean) => { template: (ctx: OnPushComp, cm: boolean) => {
@ -699,25 +675,18 @@ describe('change detection', () => {
}); });
} }
class OnPushApp { /** <on-push-comp [value]="value"></on-push-comp> */
value = 'one'; const OnPushApp = createComponent('on-push-app', function(ctx: any, cm: boolean) {
if (cm) {
static ngComponentDef = defineComponent({ elementStart(0, 'on-push-comp');
type: OnPushApp, elementEnd();
tag: 'on-push-app', }
factory: () => new OnPushApp(), elementProperty(0, 'value', bind(ctx.value));
/** <on-push-comp [value]="value"></on-push-comp> */ }, [OnPushComp.ngComponentDef]);
template: (ctx: OnPushApp, cm: boolean) => {
if (cm) {
elementStart(0, OnPushComp);
elementEnd();
}
elementProperty(0, 'value', bind(ctx.value));
}
});
}
const app = renderComponent(OnPushApp); const app = renderComponent(OnPushApp);
app.value = 'one';
tick(app);
expect(getRenderedText(app)).toEqual('one'); expect(getRenderedText(app)).toEqual('one');
onPushComp !.cdr.detach(); onPushComp !.cdr.detach();
@ -748,7 +717,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: OnPushComp, type: OnPushComp,
tag: 'on-push-comp', selector: [[['on-push-comp'], null]],
factory: () => comp = new OnPushComp(injectChangeDetectorRef()), factory: () => comp = new OnPushComp(injectChangeDetectorRef()),
/** {{ value }} */ /** {{ value }} */
template: (ctx: OnPushComp, cm: boolean) => { template: (ctx: OnPushComp, cm: boolean) => {
@ -766,7 +735,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: OnPushParent, type: OnPushParent,
tag: 'on-push-parent', selector: [[['on-push-parent'], null]],
factory: () => new OnPushParent(), factory: () => new OnPushParent(),
/** /**
* {{ value }} - * {{ value }} -
@ -775,11 +744,12 @@ describe('change detection', () => {
template: (ctx: OnPushParent, cm: boolean) => { template: (ctx: OnPushParent, cm: boolean) => {
if (cm) { if (cm) {
text(0); text(0);
elementStart(1, OnPushComp); elementStart(1, 'on-push-comp');
elementEnd(); elementEnd();
} }
textBinding(0, interpolation1('', ctx.value, ' - ')); textBinding(0, interpolation1('', ctx.value, ' - '));
}, },
directiveDefs: () => [OnPushComp.ngComponentDef],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}); });
} }
@ -832,7 +802,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: EmbeddedViewParent, type: EmbeddedViewParent,
tag: 'embedded-view-parent', selector: [[['embedded-view-parent'], null]],
factory: () => new EmbeddedViewParent(), factory: () => new EmbeddedViewParent(),
/** /**
* {{ value }} - * {{ value }} -
@ -850,7 +820,7 @@ describe('change detection', () => {
{ {
if (ctx.showing) { if (ctx.showing) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, OnPushComp); elementStart(0, 'on-push-comp');
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();
@ -858,6 +828,7 @@ describe('change detection', () => {
} }
containerRefreshEnd(); containerRefreshEnd();
}, },
directiveDefs: () => [OnPushComp.ngComponentDef],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}); });
} }
@ -904,7 +875,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: NoChangesComp, type: NoChangesComp,
tag: 'no-changes-comp', selector: [[['no-changes-comp'], null]],
factory: () => comp = new NoChangesComp(injectChangeDetectorRef()), factory: () => comp = new NoChangesComp(injectChangeDetectorRef()),
template: (ctx: NoChangesComp, cm: boolean) => { template: (ctx: NoChangesComp, cm: boolean) => {
if (cm) { if (cm) {
@ -922,7 +893,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: AppComp, type: AppComp,
tag: 'app-comp', selector: [[['app-comp'], null]],
factory: () => new AppComp(injectChangeDetectorRef()), factory: () => new AppComp(injectChangeDetectorRef()),
/** /**
* {{ value }} - * {{ value }} -
@ -931,11 +902,12 @@ describe('change detection', () => {
template: (ctx: AppComp, cm: boolean) => { template: (ctx: AppComp, cm: boolean) => {
if (cm) { if (cm) {
text(0); text(0);
elementStart(1, NoChangesComp); elementStart(1, 'no-changes-comp');
elementEnd(); elementEnd();
} }
textBinding(0, interpolation1('', ctx.value, ' - ')); textBinding(0, interpolation1('', ctx.value, ' - '));
} },
directiveDefs: () => [NoChangesComp.ngComponentDef]
}); });
} }
@ -981,7 +953,7 @@ describe('change detection', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: EmbeddedViewApp, type: EmbeddedViewApp,
tag: 'embedded-view-app', selector: [[['embedded-view-app'], null]],
factory: () => new EmbeddedViewApp(injectChangeDetectorRef()), factory: () => new EmbeddedViewApp(injectChangeDetectorRef()),
/** /**
* % if (showing) { * % if (showing) {

View File

@ -23,14 +23,14 @@ describe('@angular/common integration', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
factory: () => new MyApp(), factory: () => new MyApp(),
tag: 'my-app', selector: [[['my-app'], null]],
// <ul> // <ul>
// <li *ngFor="let item of items">{{item}}</li> // <li *ngFor="let item of items">{{item}}</li>
// </ul> // </ul>
template: (myApp: MyApp, cm: boolean) => { template: (myApp: MyApp, cm: boolean) => {
if (cm) { if (cm) {
elementStart(0, 'ul'); elementStart(0, 'ul');
{ container(1, [NgForOf], liTemplate); } { container(1, liTemplate, undefined, ['ngForOf', '']); }
elementEnd(); elementEnd();
} }
elementProperty(1, 'ngForOf', bind(myApp.items)); elementProperty(1, 'ngForOf', bind(myApp.items));
@ -45,7 +45,8 @@ describe('@angular/common integration', () => {
} }
textBinding(1, bind(row.$implicit)); textBinding(1, bind(row.$implicit));
} }
} },
directiveDefs: () => [NgForOf.ngDirectiveDef]
}); });
} }
@ -54,4 +55,4 @@ describe('@angular/common integration', () => {
}); });
// TODO: Test inheritance // TODO: Test inheritance
}); });
}); });

View File

@ -16,6 +16,7 @@ export const NgForOf: DirectiveType<NgForOfDef<any>> = NgForOfDef as any;
NgForOf.ngDirectiveDef = defineDirective({ NgForOf.ngDirectiveDef = defineDirective({
type: NgForOfDef, type: NgForOfDef,
selector: [[['', 'ngForOf', ''], null]],
factory: () => new NgForOfDef( factory: () => new NgForOfDef(
injectViewContainerRef(), injectTemplateRef(), injectViewContainerRef(), injectTemplateRef(),
directiveInject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)), directiveInject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)),

View File

@ -28,7 +28,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ChildComponent, type: ChildComponent,
tag: `child`, selector: [[['child'], null]],
factory: () => new ChildComponent(), factory: () => new ChildComponent(),
template: function(ctx: $ChildComponent$, cm: $boolean$) { template: function(ctx: $ChildComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -47,6 +47,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective, type: SomeDirective,
selector: [[['', 'some-directive', ''], null]],
factory: () => new SomeDirective(), factory: () => new SomeDirective(),
}); });
// /NORMATIVE // /NORMATIVE
@ -55,7 +56,6 @@ describe('components & directives', () => {
// Important: keep arrays outside of function to not create new instances. // Important: keep arrays outside of function to not create new instances.
// NORMATIVE // NORMATIVE
const $e0_attrs$ = ['some-directive', '']; const $e0_attrs$ = ['some-directive', ''];
const $e0_dirs$ = [SomeDirective];
// /NORMATIVE // /NORMATIVE
@Component({selector: 'my-component', template: `<child some-directive></child>!`}) @Component({selector: 'my-component', template: `<child some-directive></child>!`})
@ -63,11 +63,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: () => new MyComponent(), factory: () => new MyComponent(),
template: function(ctx: $MyComponent$, cm: $boolean$) { template: function(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, ChildComponent, $e0_attrs$, $e0_dirs$); $r3$.ɵE(0, 'child', $e0_attrs$);
$r3$.ɵe(); $r3$.ɵe();
$r3$.ɵT(1, '!'); $r3$.ɵT(1, '!');
} }
@ -76,6 +76,11 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyComponent.ngComponentDef.directiveDefs =
[ChildComponent.ngComponentDef, SomeDirective.ngDirectiveDef];
// /NON-NORMATIVE
expect(renderComp(MyComponent)).toEqual('<child some-directive="">child-view</child>!'); expect(renderComp(MyComponent)).toEqual('<child some-directive="">child-view</child>!');
expect(log).toEqual(['ChildComponent', 'SomeDirective']); expect(log).toEqual(['ChildComponent', 'SomeDirective']);
}); });
@ -90,6 +95,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir, type: HostBindingDir,
selector: [[['', 'hostBindingDir', ''], null]],
factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵp(elIndex, 'id', $r3$.ɵb($r3$.ɵd<HostBindingDir>(dirIndex).dirId)); $r3$.ɵp(elIndex, 'id', $r3$.ɵb($r3$.ɵd<HostBindingDir>(dirIndex).dirId));
@ -99,7 +105,6 @@ describe('components & directives', () => {
} }
const $e0_attrs$ = ['hostBindingDir', '']; const $e0_attrs$ = ['hostBindingDir', ''];
const $e0_dirs$ = [HostBindingDir];
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
@ -110,17 +115,21 @@ describe('components & directives', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); $r3$.ɵE(0, 'div', $e0_attrs$);
$r3$.ɵe(); $r3$.ɵe();
} }
} }
}); });
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`); expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`);
}); });
@ -134,6 +143,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
selector: [[['', 'hostListenerDir', ''], null]],
type: HostListenerDir, type: HostListenerDir,
factory: function HostListenerDir_Factory() { factory: function HostListenerDir_Factory() {
const $dir$ = new HostListenerDir(); const $dir$ = new HostListenerDir();
@ -145,7 +155,6 @@ describe('components & directives', () => {
} }
const $e0_attrs$ = ['hostListenerDir', '']; const $e0_attrs$ = ['hostListenerDir', ''];
const $e0_dirs$ = [HostListenerDir];
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
@ -156,11 +165,11 @@ describe('components & directives', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'button', $e0_attrs$, $e0_dirs$); $r3$.ɵE(0, 'button', $e0_attrs$);
$r3$.ɵT(1, 'Click'); $r3$.ɵT(1, 'Click');
$r3$.ɵe(); $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(`<button hostlistenerdir="">Click</button>`); expect(renderComp(MyApp)).toEqual(`<button hostlistenerdir="">Click</button>`);
}); });
@ -179,6 +192,7 @@ describe('components & directives', () => {
class HostAttributeDir { class HostAttributeDir {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
selector: [[['', 'hostAttributeDir', ''], null]],
type: HostAttributeDir, type: HostAttributeDir,
factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); }, factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); },
attributes: ['role', 'listbox'] attributes: ['role', 'listbox']
@ -187,7 +201,6 @@ describe('components & directives', () => {
} }
const $e0_attrs$ = ['hostAttributeDir', '']; const $e0_attrs$ = ['hostAttributeDir', ''];
const $e0_dirs$ = [HostAttributeDir];
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
@ -198,17 +211,21 @@ describe('components & directives', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); $r3$.ɵE(0, 'div', $e0_attrs$);
$r3$.ɵe(); $r3$.ɵe();
} }
} }
}); });
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [HostAttributeDir.ngDirectiveDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<div hostattributedir="" role="listbox"></div>`); expect(renderComp(MyApp)).toEqual(`<div hostattributedir="" role="listbox"></div>`);
}); });
@ -222,6 +239,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir, type: HostBindingDir,
selector: [[['', 'hostBindingDir', ''], null]],
factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵd<HostBindingDir>(dirIndex).label)); $r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵd<HostBindingDir>(dirIndex).label));
@ -231,7 +249,6 @@ describe('components & directives', () => {
} }
const $e0_attrs$ = ['hostBindingDir', '']; const $e0_attrs$ = ['hostBindingDir', ''];
const $e0_dirs$ = [HostBindingDir];
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
@ -242,17 +259,21 @@ describe('components & directives', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$); $r3$.ɵE(0, 'div', $e0_attrs$);
$r3$.ɵe(); $r3$.ɵe();
} }
} }
}); });
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [HostBindingDir.ngDirectiveDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<div aria-label="some label" hostbindingdir=""></div>`); expect(renderComp(MyApp)).toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
}); });
@ -273,7 +294,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return new MyComp(); }, factory: function MyComp_Factory() { return new MyComp(); },
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -298,11 +319,11 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp); $r3$.ɵE(0, 'my-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name)); $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(`<my-comp>some name</my-comp>`); expect(renderComp(MyApp)).toEqual(`<my-comp>some name</my-comp>`);
}); });
@ -325,6 +350,7 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: IfDirective, type: IfDirective,
selector: [[['', 'if', ''], null]],
factory: () => new IfDirective($r3$.ɵinjectTemplateRef()), factory: () => new IfDirective($r3$.ɵinjectTemplateRef()),
}); });
// /NORMATIVE // /NORMATIVE
@ -333,7 +359,6 @@ describe('components & directives', () => {
// Important: keep arrays outside of function to not create new instances. // Important: keep arrays outside of function to not create new instances.
// NORMATIVE // NORMATIVE
const $e0_locals$ = ['foo', '']; const $e0_locals$ = ['foo', ''];
const $c1_dirs$ = [IfDirective];
// /NORMATIVE // /NORMATIVE
@Component( @Component(
@ -343,12 +368,12 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: () => new MyComponent(), factory: () => new MyComponent(),
template: function(ctx: $MyComponent$, cm: $boolean$) { template: function(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'ul', null, null, $e0_locals$); $r3$.ɵE(0, 'ul', null, $e0_locals$);
$r3$.ɵC(2, $c1_dirs$, C1); $r3$.ɵC(2, C1, '', ['if', '']);
$r3$.ɵe(); $r3$.ɵe();
} }
let $foo$ = $r3$.ɵld<any>(1); let $foo$ = $r3$.ɵld<any>(1);
@ -386,7 +411,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyArrayComp, type: MyArrayComp,
tag: 'my-array-comp', selector: [[['my-array-comp'], null]],
factory: function MyArrayComp_Factory() { return new MyArrayComp(); }, factory: function MyArrayComp_Factory() { return new MyArrayComp(); },
template: function MyArrayComp_Template(ctx: $MyArrayComp$, cm: $boolean$) { template: function MyArrayComp_Template(ctx: $MyArrayComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -415,11 +440,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyArrayComp); $r3$.ɵE(0, 'my-array-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'names', cm ? $e0_arr$ : $r3$.ɵNC); $r3$.ɵp(0, 'names', cm ? $e0_arr$ : $r3$.ɵNC);
@ -428,6 +453,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`); expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
}); });
@ -453,11 +482,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyArrayComp); $r3$.ɵE(0, 'my-array-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$)))); $r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$))));
@ -466,6 +495,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<my-array-comp>NANCY Bess</my-array-comp>`); expect(renderComp(MyApp)).toEqual(`<my-array-comp>NANCY Bess</my-array-comp>`);
}); });
@ -479,7 +512,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return new MyComp(); }, factory: function MyComp_Factory() { return new MyComp(); },
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -505,11 +538,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp); $r3$.ɵE(0, 'my-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1)); $r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1));
@ -518,6 +551,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<my-comp>3</my-comp>`); expect(renderComp(MyApp)).toEqual(`<my-comp>3</my-comp>`);
}); });
@ -541,11 +578,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyArrayComp); $r3$.ɵE(0, 'my-array-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName))); $r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
@ -554,6 +591,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [MyArrayComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`); expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
}); });
@ -581,7 +622,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return new MyComp(); }, factory: function MyComp_Factory() { return new MyComp(); },
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -642,11 +683,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(c: MyApp, cm: boolean) { template: function MyApp_Template(c: MyApp, cm: boolean) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp); $r3$.ɵE(0, 'my-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp( $r3$.ɵp(
@ -657,6 +698,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [MyComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<my-comp>start-abcde-middle-fghi-end</my-comp>`); expect(renderComp(MyApp)).toEqual(`<my-comp>start-abcde-middle-fghi-end</my-comp>`);
}); });
@ -676,7 +721,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ObjectComp, type: ObjectComp,
tag: 'object-comp', selector: [[['object-comp'], null]],
factory: function ObjectComp_Factory() { return new ObjectComp(); }, factory: function ObjectComp_Factory() { return new ObjectComp(); },
template: function ObjectComp_Template(ctx: $ObjectComp$, cm: $boolean$) { template: function ObjectComp_Template(ctx: $ObjectComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -710,11 +755,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, ObjectComp); $r3$.ɵE(0, 'object-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name))); $r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
@ -723,6 +768,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [ObjectComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)).toEqual(`<object-comp><p>500</p><p>slide</p></object-comp>`); expect(renderComp(MyApp)).toEqual(`<object-comp><p>500</p><p>slide</p></object-comp>`);
}); });
@ -743,7 +792,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: NestedComp, type: NestedComp,
tag: 'nested-comp', selector: [[['nested-comp'], null]],
factory: function NestedComp_Factory() { return new NestedComp(); }, factory: function NestedComp_Factory() { return new NestedComp(); },
template: function NestedComp_Template(ctx: $NestedComp$, cm: $boolean$) { template: function NestedComp_Template(ctx: $NestedComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -786,11 +835,11 @@ describe('components & directives', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, NestedComp); $r3$.ɵE(0, 'nested-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp( $r3$.ɵp(
@ -802,6 +851,10 @@ describe('components & directives', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE (done by defineNgModule)
MyApp.ngComponentDef.directiveDefs = [NestedComp.ngComponentDef];
// /NON-NORMATIVE
expect(renderComp(MyApp)) expect(renderComp(MyApp))
.toEqual(`<nested-comp><p>slide</p><p>0</p><p>100</p></nested-comp>`); .toEqual(`<nested-comp><p>slide</p><p>0</p><p>100</p></nested-comp>`);
}); });

View File

@ -24,7 +24,7 @@ describe('content projection', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleComponent, type: SimpleComponent,
tag: 'simple', selector: [[['simple'], null]],
factory: () => new SimpleComponent(), factory: () => new SimpleComponent(),
template: function(ctx: $SimpleComponent$, cm: $boolean$) { template: function(ctx: $SimpleComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -54,7 +54,7 @@ describe('content projection', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ComplexComponent, type: ComplexComponent,
tag: 'complex', selector: [[['complex'], null]],
factory: () => new ComplexComponent(), factory: () => new ComplexComponent(),
template: function(ctx: $ComplexComponent$, cm: $boolean$) { template: function(ctx: $ComplexComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -79,15 +79,16 @@ describe('content projection', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(), factory: () => new MyApp(),
template: function(ctx: $MyApp$, cm: $boolean$) { template: function(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, SimpleComponent); $r3$.ɵE(0, 'simple');
$r3$.ɵT(1, 'content'); $r3$.ɵT(1, 'content');
$r3$.ɵe(); $r3$.ɵe();
} }
} },
directiveDefs: () => [SimpleComponent.ngComponentDef]
}); });
} }
}); });

View File

@ -32,7 +32,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: () => new MyComponent(), factory: () => new MyComponent(),
template: function(ctx: $MyComponent$, cm: $boolean$) { template: function(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -69,7 +69,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ListenerComp, type: ListenerComp,
tag: 'listener-comp', selector: [[['listener-comp'], null]],
factory: function ListenerComp_Factory() { return new ListenerComp(); }, factory: function ListenerComp_Factory() { return new ListenerComp(); },
template: function ListenerComp_Template(ctx: $ListenerComp$, cm: $boolean$) { template: function ListenerComp_Template(ctx: $ListenerComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -101,7 +101,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -131,7 +131,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -161,7 +161,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -195,7 +195,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -243,7 +243,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
@ -277,7 +277,7 @@ describe('elements', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: StyleComponent, type: StyleComponent,
tag: 'style-comp', selector: [[['style-comp'], null]],
factory: function StyleComponent_Factory() { return new StyleComponent(); }, factory: function StyleComponent_Factory() { return new StyleComponent(); },
template: function StyleComponent_Template(ctx: $StyleComponent$, cm: $boolean$) { template: function StyleComponent_Template(ctx: $StyleComponent$, cm: $boolean$) {
if (cm) { if (cm) {

View File

@ -30,7 +30,7 @@ describe('injection', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { factory: function MyComp_Factory() {
return new MyComp($r3$.ɵinjectChangeDetectorRef()); return new MyComp($r3$.ɵinjectChangeDetectorRef());
}, },
@ -47,18 +47,20 @@ describe('injection', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
/** <my-comp></my-comp> */ /** <my-comp></my-comp> */
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp); $r3$.ɵE(0, 'my-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
} },
directiveDefs: () => [MyComp.ngComponentDef]
}); });
} }
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef is historically the constructor // ChangeDetectorRef is the token, ViewRef is historically the constructor
expect(toHtml(app)).toEqual('<my-comp>ViewRef</my-comp>'); expect(toHtml(app)).toEqual('<my-comp>ViewRef</my-comp>');
@ -75,7 +77,7 @@ describe('injection', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); }, factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); },
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -90,15 +92,16 @@ describe('injection', () => {
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
/** <my-comp></my-comp> */ /** <my-comp></my-comp> */
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp, e0_attrs); $r3$.ɵE(0, 'my-comp', e0_attrs);
$r3$.ɵe(); $r3$.ɵe();
} }
} },
directiveDefs: () => [MyComp.ngComponentDef]
}); });
} }
const e0_attrs = ['title', 'WORKS']; const e0_attrs = ['title', 'WORKS'];
@ -139,7 +142,7 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { factory: function MyApp_Factory() {
return new MyApp( return new MyApp(
$r3$.ɵdirectiveInject(ServiceA), $r3$.ɵdirectiveInject(ServiceB), inject(INJECTOR)); $r3$.ɵdirectiveInject(ServiceA), $r3$.ɵdirectiveInject(ServiceB), inject(INJECTOR));

View File

@ -41,7 +41,7 @@ describe('lifecycle hooks', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: LifecycleComp, type: LifecycleComp,
tag: 'lifecycle-comp', selector: [[['lifecycle-comp'], null]],
factory: function LifecycleComp_Factory() { return new LifecycleComp(); }, factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
template: function LifecycleComp_Template(ctx: $LifecycleComp$, cm: $boolean$) {}, template: function LifecycleComp_Template(ctx: $LifecycleComp$, cm: $boolean$) {},
inputs: {nameMin: 'name'}, inputs: {nameMin: 'name'},
@ -64,13 +64,13 @@ describe('lifecycle hooks', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleLayout, type: SimpleLayout,
tag: 'simple-layout', selector: [[['simple-layout'], null]],
factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); }, factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); },
template: function SimpleLayout_Template(ctx: $SimpleLayout$, cm: $boolean$) { template: function SimpleLayout_Template(ctx: $SimpleLayout$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, LifecycleComp); $r3$.ɵE(0, 'lifecycle-comp');
$r3$.ɵe(); $r3$.ɵe();
$r3$.ɵE(1, LifecycleComp); $r3$.ɵE(1, 'lifecycle-comp');
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1)); $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1));
@ -80,6 +80,10 @@ describe('lifecycle hooks', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE
SimpleLayout.ngComponentDef.directiveDefs = [LifecycleComp.ngComponentDef];
// /NON-NORMATIVE
it('should gen hooks with a few simple components', () => { it('should gen hooks with a few simple components', () => {
expect(toHtml(renderComponent(SimpleLayout))) expect(toHtml(renderComponent(SimpleLayout)))
.toEqual(`<lifecycle-comp></lifecycle-comp><lifecycle-comp></lifecycle-comp>`); .toEqual(`<lifecycle-comp></lifecycle-comp><lifecycle-comp></lifecycle-comp>`);

View File

@ -23,11 +23,11 @@ describe('local references', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: () => new MyComponent, factory: () => new MyComponent,
template: function(ctx: $MyComponent$, cm: $boolean$) { template: function(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'input', null, null, ['user', '']); $r3$.ɵE(0, 'input', null, ['user', '']);
$r3$.ɵe(); $r3$.ɵe();
$r3$.ɵT(2); $r3$.ɵT(2);
} }

View File

@ -82,7 +82,7 @@ describe('pipes', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
@ -133,17 +133,13 @@ describe('pipes', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: OneTimeIf, type: OneTimeIf,
selector: [[['', 'oneTimeIf', ''], null]],
factory: () => new OneTimeIf($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()), factory: () => new OneTimeIf($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()),
inputs: {oneTimeIf: 'oneTimeIf'} inputs: {oneTimeIf: 'oneTimeIf'}
}); });
// /NORMATIVE // /NORMATIVE
} }
// Important: keep arrays outside of function to not create new instances.
// NORMATIVE
const $c1_dirs$ = [OneTimeIf];
// /NORMATIVE
@Component({ @Component({
template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}} template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}}
<div *oneTimeIf="more">{{name | myPurePipe:size}}</div>` <div *oneTimeIf="more">{{name | myPurePipe:size}}</div>`
@ -156,7 +152,7 @@ describe('pipes', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
let $pi$: $MyPurePipe$; let $pi$: $MyPurePipe$;
@ -165,7 +161,7 @@ describe('pipes', () => {
$pi$ = $r3$.ɵPp(1, $MyPurePipe_ngPipeDef$); $pi$ = $r3$.ɵPp(1, $MyPurePipe_ngPipeDef$);
$r3$.ɵT(2); $r3$.ɵT(2);
$r3$.ɵPp(3, $MyPurePipe_ngPipeDef$, $pi$); $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(0, $r3$.ɵi1('', $r3$.ɵpb2(1, ctx.name, ctx.size), ''));
$r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(3, ctx.name, ctx.size), '')); $r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(3, ctx.name, ctx.size), ''));
@ -187,6 +183,10 @@ describe('pipes', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE
MyApp.ngComponentDef.directiveDefs = [OneTimeIf.ngDirectiveDef];
// /NON-NORMATIVE
let myApp: MyApp = renderComponent(MyApp); let myApp: MyApp = renderComponent(MyApp);
expect(toHtml(containerEl)).toEqual('WorldWorld<div>World</div>'); expect(toHtml(containerEl)).toEqual('WorldWorld<div>World</div>');
expect(myPurePipeTransformCalls).toEqual(3); expect(myPurePipeTransformCalls).toEqual(3);

View File

@ -22,6 +22,7 @@ describe('queries', () => {
class SomeDirective { class SomeDirective {
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective, type: SomeDirective,
selector: [[['', 'someDir', ''], null]],
factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); }, factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); },
features: [$r3$.ɵPublicFeature] features: [$r3$.ɵPublicFeature]
}); });
@ -32,7 +33,6 @@ describe('queries', () => {
// NORMATIVE // NORMATIVE
const $e1_attrs$ = ['someDir', '']; const $e1_attrs$ = ['someDir', ''];
const $e1_dirs$ = [SomeDirective];
// /NORMATIVE // /NORMATIVE
@Component({ @Component({
@ -48,14 +48,14 @@ describe('queries', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ViewQueryComponent, type: ViewQueryComponent,
tag: 'view-query-component', selector: [[['view-query-component'], null]],
factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); }, factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) { template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) {
let $tmp$: any; let $tmp$: any;
if (cm) { if (cm) {
$r3$.ɵQ(0, SomeDirective, false); $r3$.ɵQ(0, SomeDirective, false);
$r3$.ɵQ(1, SomeDirective, false); $r3$.ɵQ(1, SomeDirective, false);
$r3$.ɵE(2, 'div', $e1_attrs$, $e1_dirs$); $r3$.ɵE(2, 'div', $e1_attrs$);
$r3$.ɵe(); $r3$.ɵe();
} }
@ -67,6 +67,9 @@ describe('queries', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE
ViewQueryComponent.ngComponentDef.directiveDefs = [SomeDirective.ngDirectiveDef];
// /NON-NORMATIVE
const viewQueryComp = renderComponent(ViewQueryComponent); const viewQueryComp = renderComponent(ViewQueryComponent);
expect(viewQueryComp.someDir).toEqual(someDir); expect(viewQueryComp.someDir).toEqual(someDir);
@ -92,7 +95,7 @@ describe('queries', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: ContentQueryComponent, type: ContentQueryComponent,
tag: 'content-query-component', selector: [[['content-query-component'], null]],
factory: function ContentQueryComponent_Factory() { factory: function ContentQueryComponent_Factory() {
return [ return [
new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false), new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false),
@ -120,7 +123,6 @@ describe('queries', () => {
} }
const $e2_attrs$ = ['someDir', '']; const $e2_attrs$ = ['someDir', ''];
const $e2_dirs$ = [SomeDirective];
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
@ -134,13 +136,13 @@ describe('queries', () => {
// NON-NORMATIVE // NON-NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, ContentQueryComponent); $r3$.ɵE(0, 'content-query-component');
contentQueryComp = $r3$.ɵd<any[]>(0)[0]; contentQueryComp = $r3$.ɵd<any[]>(0)[0];
$r3$.ɵE(1, 'div', $e2_attrs$, $e2_dirs$); $r3$.ɵE(1, 'div', $e2_attrs$);
$r3$.ɵe(); $r3$.ɵe();
$r3$.ɵe(); $r3$.ɵe();
} }
@ -149,6 +151,10 @@ describe('queries', () => {
// /NON-NORMATIVE // /NON-NORMATIVE
} }
// NON-NORMATIVE
MyApp.ngComponentDef.directiveDefs =
[ContentQueryComponent.ngComponentDef, SomeDirective.ngDirectiveDef];
// /NON-NORMATIVE
expect(toHtml(renderComponent(MyApp))) expect(toHtml(renderComponent(MyApp)))
.toEqual( .toEqual(

View File

@ -38,7 +38,7 @@ describe('compiler sanitization', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {

View File

@ -58,7 +58,7 @@ class ToDoAppComponent {
// NORMATIVE // NORMATIVE
static ngComponentDef = r3.defineComponent({ static ngComponentDef = r3.defineComponent({
type: ToDoAppComponent, type: ToDoAppComponent,
tag: 'todo-app', selector: [[['todo-app'], null]],
factory: function ToDoAppComponent_Factory() { factory: function ToDoAppComponent_Factory() {
return new ToDoAppComponent(r3.directiveInject(AppState)); return new ToDoAppComponent(r3.directiveInject(AppState));
}, },
@ -67,7 +67,7 @@ class ToDoAppComponent {
const ToDoAppComponent_NgForOf_Template = function ToDoAppComponent_NgForOf_Template( const ToDoAppComponent_NgForOf_Template = function ToDoAppComponent_NgForOf_Template(
ctx1: NgForOfContext<ToDo>, cm: boolean) { ctx1: NgForOfContext<ToDo>, cm: boolean) {
if (cm) { if (cm) {
r3.E(0, ToDoItemComponent); r3.E(0, 'todo');
r3.L('archive', ctx.onArchive.bind(ctx)); r3.L('archive', ctx.onArchive.bind(ctx));
r3.e(); r3.e();
} }
@ -77,7 +77,7 @@ class ToDoAppComponent {
r3.T(1, 'ToDo Application'); r3.T(1, 'ToDo Application');
r3.e(); r3.e();
r3.E(2, 'div'); r3.E(2, 'div');
r3.C(3, c3_directives, ToDoAppComponent_NgForOf_Template); r3.C(3, ToDoAppComponent_NgForOf_Template, '', ['ngForOf', '']);
r3.e(); r3.e();
r3.E(4, 'span'); r3.E(4, 'span');
r3.T(5); r3.T(5);
@ -89,9 +89,10 @@ class ToDoAppComponent {
// /NORMATIVE // /NORMATIVE
} }
// NORMATIVE // NON-NORMATIVE
const c3_directives = [NgForOf as r3.DirectiveType<NgForOf<ToDo>>]; ToDoAppComponent.ngComponentDef.directiveDefs = () =>
// /NORMATIVE [ToDoItemComponent.ngComponentDef, (NgForOf as r3.DirectiveType<NgForOf<any>>).ngDirectiveDef];
// /NON-NORMATIVE
@Component({ @Component({
selector: 'todo', selector: 'todo',
@ -122,7 +123,7 @@ class ToDoItemComponent {
// NORMATIVE // NORMATIVE
static ngComponentDef = r3.defineComponent({ static ngComponentDef = r3.defineComponent({
type: ToDoItemComponent, type: ToDoItemComponent,
tag: 'todo', selector: [[['todo'], null]],
factory: function ToDoItemComponent_Factory() { return new ToDoItemComponent(); }, factory: function ToDoItemComponent_Factory() { return new ToDoItemComponent(); },
template: function ToDoItemComponent_Template(ctx: ToDoItemComponent, cm: boolean) { template: function ToDoItemComponent_Template(ctx: ToDoItemComponent, cm: boolean) {
if (cm) { if (cm) {

View File

@ -63,6 +63,7 @@ describe('template variables', () => {
// NORMATIVE // NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({ static ngDirectiveDef = $r3$.ɵdefineDirective({
type: ForOfDirective, type: ForOfDirective,
selector: [[['', 'forOf', ''], null]],
factory: function ForOfDirective_Factory() { factory: function ForOfDirective_Factory() {
return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef());
}, },
@ -80,10 +81,6 @@ describe('template variables', () => {
name: string; name: string;
} }
// NORMATIVE
const $c1_dirs$ = [ForOfDirective];
// /NORMATIVE
@Component({ @Component({
selector: 'my-component', selector: 'my-component',
template: `<ul><li *for="let item of items">{{item.name}}</li></ul>` template: `<ul><li *for="let item of items">{{item.name}}</li></ul>`
@ -94,12 +91,12 @@ describe('template variables', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'ul'); $r3$.ɵE(0, 'ul');
$r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1); $r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, '', ['forOf', '']);
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items)); $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
@ -120,6 +117,10 @@ describe('template variables', () => {
// /NORMATIVE // /NORMATIVE
} }
// NON-NORMATIVE
MyComponent.ngComponentDef.directiveDefs = [ForOfDirective.ngDirectiveDef];
// /NON-NORMATIVE
// TODO(chuckj): update when the changes to enable ngForOf lands. // TODO(chuckj): update when the changes to enable ngForOf lands.
expect(toHtml(renderComponent(MyComponent))).toEqual('<ul></ul>'); expect(toHtml(renderComponent(MyComponent))).toEqual('<ul></ul>');
}); });
@ -135,10 +136,6 @@ describe('template variables', () => {
infos: Info[]; infos: Info[];
} }
// NORMATIVE
const $c1_dirs$ = [ForOfDirective];
// /NORMATIVE
@Component({ @Component({
selector: 'my-component', selector: 'my-component',
template: ` template: `
@ -162,12 +159,12 @@ describe('template variables', () => {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent, type: MyComponent,
tag: 'my-component', selector: [[['my-component'], null]],
factory: function MyComponent_Factory() { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) { template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, 'ul'); $r3$.ɵE(0, 'ul');
$r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1); $r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, '', ['forOf', '']);
$r3$.ɵe(); $r3$.ɵe();
} }
$r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items)); $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
@ -181,7 +178,7 @@ describe('template variables', () => {
$r3$.ɵT(2); $r3$.ɵT(2);
$r3$.ɵe(); $r3$.ɵe();
$r3$.ɵE(3, 'ul'); $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();
$r3$.ɵe(); $r3$.ɵe();
} }

View File

@ -24,7 +24,7 @@ describe('component', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: CounterComponent, type: CounterComponent,
tag: 'counter', selector: [[['counter'], null]],
template: function(ctx: CounterComponent, cm: boolean) { template: function(ctx: CounterComponent, cm: boolean) {
if (cm) { if (cm) {
text(0); text(0);
@ -87,7 +87,7 @@ describe('component with a container', () => {
items: string[]; items: string[];
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: WrapperComponent, type: WrapperComponent,
tag: 'wrapper', selector: [[['wrapper'], null]],
template: function ChildComponentTemplate(ctx: {items: string[]}, cm: boolean) { template: function ChildComponentTemplate(ctx: {items: string[]}, cm: boolean) {
if (cm) { if (cm) {
container(0); container(0);
@ -107,18 +107,20 @@ describe('component with a container', () => {
function template(ctx: {items: string[]}, cm: boolean) { function template(ctx: {items: string[]}, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, WrapperComponent); elementStart(0, 'wrapper');
elementEnd(); elementEnd();
} }
elementProperty(0, 'items', bind(ctx.items)); elementProperty(0, 'items', bind(ctx.items));
} }
const defs = [WrapperComponent.ngComponentDef];
it('should re-render on input change', () => { it('should re-render on input change', () => {
const ctx: {items: string[]} = {items: ['a']}; const ctx: {items: string[]} = {items: ['a']};
expect(renderToHtml(template, ctx)).toEqual('<wrapper>a</wrapper>'); expect(renderToHtml(template, ctx, defs)).toEqual('<wrapper>a</wrapper>');
ctx.items = [...ctx.items, 'b']; ctx.items = [...ctx.items, 'b'];
expect(renderToHtml(template, ctx)).toEqual('<wrapper>ab</wrapper>'); expect(renderToHtml(template, ctx, defs)).toEqual('<wrapper>ab</wrapper>');
}); });
}); });
@ -129,38 +131,40 @@ describe('encapsulation', () => {
class WrapperComponent { class WrapperComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: WrapperComponent, type: WrapperComponent,
tag: 'wrapper', selector: [[['wrapper'], null]],
template: function(ctx: WrapperComponent, cm: boolean) { template: function(ctx: WrapperComponent, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, EncapsulatedComponent); elementStart(0, 'encapsulated');
elementEnd(); elementEnd();
} }
}, },
factory: () => new WrapperComponent, factory: () => new WrapperComponent,
directiveDefs: () => [EncapsulatedComponent.ngComponentDef]
}); });
} }
class EncapsulatedComponent { class EncapsulatedComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: EncapsulatedComponent, type: EncapsulatedComponent,
tag: 'encapsulated', selector: [[['encapsulated'], null]],
template: function(ctx: EncapsulatedComponent, cm: boolean) { template: function(ctx: EncapsulatedComponent, cm: boolean) {
if (cm) { if (cm) {
text(0, 'foo'); text(0, 'foo');
elementStart(1, LeafComponent); elementStart(1, 'leaf');
elementEnd(); elementEnd();
} }
}, },
factory: () => new EncapsulatedComponent, factory: () => new EncapsulatedComponent,
rendererType: rendererType:
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}), createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
directiveDefs: () => [LeafComponent.ngComponentDef]
}); });
} }
class LeafComponent { class LeafComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: LeafComponent, type: LeafComponent,
tag: 'leaf', selector: [[['leaf'], null]],
template: function(ctx: LeafComponent, cm: boolean) { template: function(ctx: LeafComponent, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'span'); elementStart(0, 'span');
@ -190,23 +194,24 @@ describe('encapsulation', () => {
class WrapperComponentWith { class WrapperComponentWith {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: WrapperComponentWith, type: WrapperComponentWith,
tag: 'wrapper', selector: [[['wrapper'], null]],
template: function(ctx: WrapperComponentWith, cm: boolean) { template: function(ctx: WrapperComponentWith, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, LeafComponentwith); elementStart(0, 'leaf');
elementEnd(); elementEnd();
} }
}, },
factory: () => new WrapperComponentWith, factory: () => new WrapperComponentWith,
rendererType: rendererType:
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}), createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
directiveDefs: () => [LeafComponentwith.ngComponentDef]
}); });
} }
class LeafComponentwith { class LeafComponentwith {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: LeafComponentwith, type: LeafComponentwith,
tag: 'leaf', selector: [[['leaf'], null]],
template: function(ctx: LeafComponentwith, cm: boolean) { template: function(ctx: LeafComponentwith, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'span'); elementStart(0, 'span');
@ -245,7 +250,7 @@ describe('recursive components', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: TreeComponent, type: TreeComponent,
tag: 'tree-comp', selector: [[['tree-comp'], null]],
factory: () => new TreeComponent(), factory: () => new TreeComponent(),
template: (ctx: TreeComponent, cm: boolean) => { template: (ctx: TreeComponent, cm: boolean) => {
if (cm) { if (cm) {
@ -258,7 +263,7 @@ describe('recursive components', () => {
{ {
if (ctx.data.left != null) { if (ctx.data.left != null) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, TreeComponent); elementStart(0, 'tree-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'data', bind(ctx.data.left)); elementProperty(0, 'data', bind(ctx.data.left));
@ -270,7 +275,7 @@ describe('recursive components', () => {
{ {
if (ctx.data.right != null) { if (ctx.data.right != null) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, TreeComponent); elementStart(0, 'tree-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'data', bind(ctx.data.right)); elementProperty(0, 'data', bind(ctx.data.right));
@ -283,6 +288,7 @@ describe('recursive components', () => {
}); });
} }
TreeComponent.ngComponentDef.directiveDefs = () => [TreeComponent.ngComponentDef];
function _buildTree(currDepth: number): TreeNode { function _buildTree(currDepth: number): TreeNode {
const children = currDepth < 2 ? _buildTree(currDepth + 1) : null; const children = currDepth < 2 ? _buildTree(currDepth + 1) : null;

View File

@ -31,11 +31,12 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ text(1, 'content'); } { text(1, 'content'); }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>content</div></child>'); expect(toHtml(parent)).toEqual('<child><div>content</div></child>');
}); });
@ -49,11 +50,12 @@ describe('content projection', () => {
}); });
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ text(1, 'content'); } { text(1, 'content'); }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child>content</child>'); expect(toHtml(parent)).toEqual('<child>content</child>');
}); });
@ -70,14 +72,14 @@ describe('content projection', () => {
const Child = createComponent('child', function(ctx: any, cm: boolean) { const Child = createComponent('child', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
projectionDef(0); projectionDef(0);
elementStart(1, GrandChild); elementStart(1, 'grand-child');
{ projection(2, 0); } { projection(2, 0); }
elementEnd(); elementEnd();
} }
}); }, [GrandChild.ngComponentDef]);
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'b'); elementStart(1, 'b');
text(2, 'Hello'); text(2, 'Hello');
@ -86,7 +88,8 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
.toEqual('<child><grand-child><div><b>Hello</b>World!</div></grand-child></child>'); .toEqual('<child><grand-child><div><b>Hello</b>World!</div></grand-child></child>');
@ -117,14 +120,15 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', (ctx: any, cm: boolean) => { const Parent = createComponent('parent', (ctx: any, cm: boolean) => {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, ProjectedComp); elementStart(1, 'projected-comp');
elementEnd(); elementEnd();
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef, ProjectedComp.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
.toEqual('<child><div><projected-comp>content</projected-comp></div></child>'); .toEqual('<child><div><projected-comp>content</projected-comp></div></child>');
@ -141,7 +145,7 @@ describe('content projection', () => {
}); });
const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
text(1, '('); text(1, '(');
container(2); container(2);
@ -159,7 +163,8 @@ describe('content projection', () => {
} }
} }
containerRefreshEnd(); containerRefreshEnd();
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>()</div></child>'); expect(toHtml(parent)).toEqual('<child><div>()</div></child>');
parent.value = true; parent.value = true;
@ -179,7 +184,7 @@ describe('content projection', () => {
}); });
const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ container(1); } { container(1); }
elementEnd(); elementEnd();
} }
@ -193,7 +198,8 @@ describe('content projection', () => {
} }
} }
containerRefreshEnd(); containerRefreshEnd();
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child></child>'); expect(toHtml(parent)).toEqual('<child></child>');
@ -217,7 +223,7 @@ describe('content projection', () => {
}); });
const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
text(1, '('); text(1, '(');
container(2); container(2);
@ -240,7 +246,8 @@ describe('content projection', () => {
} }
} }
containerRefreshEnd(); containerRefreshEnd();
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>(else)</div></child>'); expect(toHtml(parent)).toEqual('<child><div>(else)</div></child>');
parent.value = true; parent.value = true;
@ -289,14 +296,15 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
childCmptInstance = loadDirective(0); childCmptInstance = loadDirective(0);
text(1, 'content'); text(1, 'content');
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div><span>content</span></div></child>'); expect(toHtml(parent)).toEqual('<child><div><span>content</span></div></child>');
@ -340,14 +348,15 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
childCmptInstance = loadDirective(0); childCmptInstance = loadDirective(0);
text(1, 'content'); text(1, 'content');
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>content</div></child>'); expect(toHtml(parent)).toEqual('<child><div>content</div></child>');
@ -393,14 +402,15 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
childCmptInstance = loadDirective(0); childCmptInstance = loadDirective(0);
text(1, 'content'); text(1, 'content');
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>before-content-after</div></child>'); expect(toHtml(parent)).toEqual('<child><div>before-content-after</div></child>');
@ -431,11 +441,12 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ text(1, 'content'); } { text(1, 'content'); }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div></div><span>content</span></child>'); expect(toHtml(parent)).toEqual('<child><div></div><span>content</span></child>');
}); });
@ -484,14 +495,15 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
childCmptInstance = loadDirective(0); childCmptInstance = loadDirective(0);
text(1, 'content'); text(1, 'content');
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child>content<div></div></child>'); expect(toHtml(parent)).toEqual('<child>content<div></div></child>');
@ -529,7 +541,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span', ['title', 'toFirst']); elementStart(1, 'span', ['title', 'toFirst']);
{ text(2, '1'); } { text(2, '1'); }
@ -540,7 +552,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -575,7 +587,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span', ['class', 'toFirst']); elementStart(1, 'span', ['class', 'toFirst']);
{ text(2, '1'); } { text(2, '1'); }
@ -586,7 +598,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -621,7 +633,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span', ['class', 'other toFirst']); elementStart(1, 'span', ['class', 'other toFirst']);
{ text(2, '1'); } { text(2, '1'); }
@ -632,7 +644,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -667,7 +679,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span', ['class', 'toFirst']); elementStart(1, 'span', ['class', 'toFirst']);
{ text(2, '1'); } { text(2, '1'); }
@ -678,7 +690,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -711,7 +723,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span', ['class', 'toFirst']); elementStart(1, 'span', ['class', 'toFirst']);
{ text(2, '1'); } { text(2, '1'); }
@ -723,7 +735,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -757,7 +769,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span'); elementStart(1, 'span');
{ text(2, '1'); } { text(2, '1'); }
@ -769,7 +781,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -807,7 +819,7 @@ describe('content projection', () => {
const Child = createComponent('child', function(ctx: any, cm: boolean) { const Child = createComponent('child', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
projectionDef(0); projectionDef(0);
elementStart(1, GrandChild); elementStart(1, 'grand-child');
{ {
projection(2, 0); projection(2, 0);
elementStart(3, 'span'); elementStart(3, 'span');
@ -816,7 +828,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [GrandChild.ngComponentDef]);
/** /**
* <child> * <child>
@ -827,7 +839,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'span'); elementStart(1, 'span');
{ text(2, 'parent content'); } { text(2, 'parent content'); }
@ -835,7 +847,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)) expect(toHtml(parent))
@ -871,7 +883,7 @@ describe('content projection', () => {
const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) { const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
projectionDef(0); projectionDef(0);
elementStart(1, Card); elementStart(1, 'card');
{ {
elementStart(2, 'h1', ['card-title', '']); elementStart(2, 'h1', ['card-title', '']);
{ text(3, 'Title'); } { text(3, 'Title'); }
@ -880,7 +892,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Card.ngComponentDef]);
/** /**
* <card-with-title> * <card-with-title>
@ -889,11 +901,11 @@ describe('content projection', () => {
*/ */
const App = createComponent('app', function(ctx: any, cm: boolean) { const App = createComponent('app', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, CardWithTitle); elementStart(0, 'card-with-title');
{ text(1, 'content'); } { text(1, 'content'); }
elementEnd(); elementEnd();
} }
}); }, [CardWithTitle.ngComponentDef]);
const app = renderComponent(App); const app = renderComponent(App);
expect(toHtml(app)) expect(toHtml(app))
@ -930,7 +942,7 @@ describe('content projection', () => {
const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) { const CardWithTitle = createComponent('card-with-title', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
projectionDef(0); projectionDef(0);
elementStart(1, Card); elementStart(1, 'card');
{ {
elementStart(2, 'h1', ['ngProjectAs', '[card-title]']); elementStart(2, 'h1', ['ngProjectAs', '[card-title]']);
{ text(3, 'Title'); } { text(3, 'Title'); }
@ -939,7 +951,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Card.ngComponentDef]);
/** /**
* <card-with-title> * <card-with-title>
@ -948,11 +960,11 @@ describe('content projection', () => {
*/ */
const App = createComponent('app', function(ctx: any, cm: boolean) { const App = createComponent('app', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, CardWithTitle); elementStart(0, 'card-with-title');
{ text(1, 'content'); } { text(1, 'content'); }
elementEnd(); elementEnd();
} }
}); }, [CardWithTitle.ngComponentDef]);
const app = renderComponent(App); const app = renderComponent(App);
expect(toHtml(app)) expect(toHtml(app))
@ -980,7 +992,7 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: any, cm: boolean) { const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ {
elementStart(1, 'div', ['ngProjectAs', 'span']); elementStart(1, 'div', ['ngProjectAs', 'span']);
{ text(2, 'should not project'); } { text(2, 'should not project'); }
@ -991,7 +1003,7 @@ describe('content projection', () => {
} }
elementEnd(); elementEnd();
} }
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><div>should project</div></child>'); expect(toHtml(parent)).toEqual('<child><div>should project</div></child>');
@ -1020,8 +1032,8 @@ describe('content projection', () => {
*/ */
const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) { const Parent = createComponent('parent', function(ctx: {value: any}, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Child); elementStart(0, 'child');
{ container(1, undefined, undefined, 'div'); } { container(1, undefined, 'div'); }
elementEnd(); elementEnd();
} }
containerRefreshStart(1); containerRefreshStart(1);
@ -1036,7 +1048,7 @@ describe('content projection', () => {
} }
} }
containerRefreshEnd(); containerRefreshEnd();
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Parent); const parent = renderComponent(Parent);
expect(toHtml(parent)).toEqual('<child><span><div>content</div></span></child>'); expect(toHtml(parent)).toEqual('<child><span><div>content</div></span></child>');
}); });

View File

@ -6,9 +6,10 @@
* found in the LICENSE file at https://angular.io/license * 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 {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', () => { describe('JS control flow', () => {
it('should work with if block', () => { it('should work with if block', () => {
@ -531,6 +532,136 @@ describe('JS control flow', () => {
ctx.condition = true; ctx.condition = true;
expect(renderToHtml(Template, ctx)).toEqual('<div><span>Hello</span></div>'); expect(renderToHtml(Template, ctx)).toEqual('<div><span>Hello</span></div>');
}); });
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', () => { describe('JS for loop', () => {

View File

@ -29,6 +29,7 @@ describe('define', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: MyDirective, type: MyDirective,
selector: [[['', 'myDir', ''], null]],
factory: () => new MyDirective(), factory: () => new MyDirective(),
features: [NgOnChangesFeature()], features: [NgOnChangesFeature()],
inputs: {valA: 'valA', valB: 'valB'} inputs: {valA: 'valA', valB: 'valB'}

View File

@ -17,19 +17,20 @@ import {LNodeType} from '../../src/render3/interfaces/node';
import {LViewFlags} from '../../src/render3/interfaces/view'; import {LViewFlags} from '../../src/render3/interfaces/view';
import {ViewRef} from '../../src/render3/view_ref'; 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('di', () => {
describe('no dependencies', () => { describe('no dependencies', () => {
it('should create directive with no deps', () => { it('should create directive with no deps', () => {
class Directive { class Directive {
value: string = 'Created'; 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) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [Directive]); elementStart(0, 'div', ['dir', '']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
@ -37,7 +38,8 @@ describe('di', () => {
textBinding(1, bind(loadDirective<Directive>(0).value)); textBinding(1, bind(loadDirective<Directive>(0).value));
} }
expect(renderToHtml(Template, {})).toEqual('<div>Created</div>'); expect(renderToHtml(Template, {}, [Directive.ngDirectiveDef]))
.toEqual('<div dir="">Created</div>');
}); });
}); });
@ -45,14 +47,22 @@ describe('di', () => {
it('should create directive with inter view dependencies', () => { it('should create directive with inter view dependencies', () => {
class DirectiveA { class DirectiveA {
value: string = 'A'; value: string = 'A';
static ngDirectiveDef = defineDirective( static ngDirectiveDef = defineDirective({
{type: DirectiveA, factory: () => new DirectiveA, features: [PublicFeature]}); type: DirectiveA,
selector: [[['', 'dirA', ''], null]],
factory: () => new DirectiveA,
features: [PublicFeature]
});
} }
class DirectiveB { class DirectiveB {
value: string = 'B'; value: string = 'B';
static ngDirectiveDef = defineDirective( static ngDirectiveDef = defineDirective({
{type: DirectiveB, factory: () => new DirectiveB, features: [PublicFeature]}); type: DirectiveB,
selector: [[['', 'dirB', ''], null]],
factory: () => new DirectiveB,
features: [PublicFeature]
});
} }
class DirectiveC { class DirectiveC {
@ -60,15 +70,16 @@ describe('di', () => {
constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; } constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveC, type: DirectiveC,
selector: [[['', 'dirC', ''], null]],
factory: () => new DirectiveC(directiveInject(DirectiveA), directiveInject(DirectiveB)) factory: () => new DirectiveC(directiveInject(DirectiveA), directiveInject(DirectiveB))
}); });
} }
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [DirectiveA]); elementStart(0, 'div', ['dirA', '']);
{ {
elementStart(1, 'span', null, [DirectiveB, DirectiveC]); elementStart(1, 'span', ['dirB', '', 'dirC', '']);
{ text(2); } { text(2); }
elementEnd(); elementEnd();
} }
@ -78,7 +89,10 @@ describe('di', () => {
textBinding(2, bind(loadDirective<DirectiveC>(2).value)); textBinding(2, bind(loadDirective<DirectiveC>(2).value));
} }
expect(renderToHtml(Template, {})).toEqual('<div><span>AB</span></div>'); const defs =
[DirectiveA.ngDirectiveDef, DirectiveB.ngDirectiveDef, DirectiveC.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs))
.toEqual('<div dira=""><span dirb="" dirc="">AB</span></div>');
}); });
}); });
@ -91,6 +105,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', 'dir', ''], null]],
factory: () => new Directive(injectElementRef()), factory: () => new Directive(injectElementRef()),
features: [PublicFeature] features: [PublicFeature]
}); });
@ -103,13 +118,14 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
selector: [[['', 'dirSame', ''], null]],
factory: () => new DirectiveSameInstance(injectElementRef(), directiveInject(Directive)) factory: () => new DirectiveSameInstance(injectElementRef(), directiveInject(Directive))
}); });
} }
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [Directive, DirectiveSameInstance]); elementStart(0, 'div', ['dir', '', 'dirSame', '']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
@ -120,7 +136,9 @@ describe('di', () => {
loadDirective<DirectiveSameInstance>(1).value, '')); loadDirective<DirectiveSameInstance>(1).value, ''));
} }
expect(renderToHtml(Template, {})).toEqual('<div>ElementRef-true</div>'); const defs = [Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs))
.toEqual('<div dir="" dirsame="">ElementRef-true</div>');
}); });
}); });
@ -133,6 +151,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', 'dir', ''], null]],
factory: () => new Directive(injectTemplateRef()), factory: () => new Directive(injectTemplateRef()),
features: [PublicFeature] features: [PublicFeature]
}); });
@ -145,6 +164,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
selector: [[['', 'dirSame', ''], null]],
factory: () => new DirectiveSameInstance(injectTemplateRef(), directiveInject(Directive)) factory: () => new DirectiveSameInstance(injectTemplateRef(), directiveInject(Directive))
}); });
} }
@ -152,7 +172,7 @@ describe('di', () => {
function Template(ctx: any, cm: any) { function Template(ctx: any, cm: any) {
if (cm) { if (cm) {
container(0, [Directive, DirectiveSameInstance], function() {}); container(0, function() {}, undefined, ['dir', '', 'dirSame', '']);
text(1); text(1);
} }
// TODO: remove loadDirective when removing directive references // TODO: remove loadDirective when removing directive references
@ -162,7 +182,8 @@ describe('di', () => {
loadDirective<DirectiveSameInstance>(1).value, '')); loadDirective<DirectiveSameInstance>(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({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', 'dir', ''], null]],
factory: () => new Directive(injectViewContainerRef()), factory: () => new Directive(injectViewContainerRef()),
features: [PublicFeature] features: [PublicFeature]
}); });
@ -187,6 +209,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
selector: [[['', 'dirSame', ''], null]],
factory: factory:
() => new DirectiveSameInstance(injectViewContainerRef(), directiveInject(Directive)) () => new DirectiveSameInstance(injectViewContainerRef(), directiveInject(Directive))
}); });
@ -194,7 +217,7 @@ describe('di', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [Directive, DirectiveSameInstance]); elementStart(0, 'div', ['dir', '', 'dirSame', '']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
@ -205,7 +228,9 @@ describe('di', () => {
loadDirective<DirectiveSameInstance>(1).value, '')); loadDirective<DirectiveSameInstance>(1).value, ''));
} }
expect(renderToHtml(Template, {})).toEqual('<div>ViewContainerRef-true</div>'); const defs = [Directive.ngDirectiveDef, DirectiveSameInstance.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs))
.toEqual('<div dir="" dirsame="">ViewContainerRef-true</div>');
}); });
}); });
@ -219,7 +244,7 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: () => comp = new MyComp(injectChangeDetectorRef()), factory: () => comp = new MyComp(injectChangeDetectorRef()),
template: function(ctx: MyComp, cm: boolean) { template: function(ctx: MyComp, cm: boolean) {
if (cm) { if (cm) {
@ -235,6 +260,7 @@ describe('di', () => {
constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', 'dir', ''], null]],
factory: () => dir = new Directive(injectChangeDetectorRef()), factory: () => dir = new Directive(injectChangeDetectorRef()),
features: [PublicFeature], features: [PublicFeature],
exportAs: 'dir' exportAs: 'dir'
@ -246,34 +272,53 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
selector: [[['', 'dirSame', ''], null]],
factory: () => dirSameInstance = new DirectiveSameInstance(injectChangeDetectorRef()) factory: () => dirSameInstance = new DirectiveSameInstance(injectChangeDetectorRef())
}); });
} }
const $e0_attrs$ = ['dir', '', 'dirSameInstance', '']; class IfDirective {
/* @Input */
myIf = true;
constructor(public template: TemplateRef<any>, 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', () => { it('should inject current component ChangeDetectorRef into directives on components', () => {
class MyApp { /** <my-comp dir dirSameInstance #dir="dir"></my-comp> {{ dir.value }} */
static ngComponentDef = defineComponent({ const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) {
type: MyApp, if (cm) {
tag: 'my-app', elementStart(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
factory: () => new MyApp(), elementEnd();
/** <my-comp dir dirSameInstance #dir="dir"></my-comp> {{ dir.value }} */ text(1);
template: function(ctx: any, cm: boolean) { }
if (cm) { // TODO: remove loadDirective when removing directive references
elementStart(0, MyComp, $e0_attrs$, [Directive, DirectiveSameInstance]); textBinding(1, bind(loadDirective<Directive>(1).value));
elementEnd(); }, defs);
text(1);
}
// TODO: remove loadDirective when removing directive references
textBinding(1, bind(loadDirective<Directive>(1).value));
}
});
}
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef has historically been the constructor // ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(toHtml(app)).toEqual('<my-comp dir="" dirsameinstance=""></my-comp>ViewRef'); expect(toHtml(app)).toEqual('<my-comp dir="" dirsame=""></my-comp>ViewRef');
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect(dir !.cdr).toBe(comp !.cdr); expect(dir !.cdr).toBe(comp !.cdr);
@ -287,23 +332,24 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** <div dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */ /** <div dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */
template: function(ctx: any, cm: boolean) { template: function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
// TODO: remove loadDirective when removing directive references // TODO: remove loadDirective when removing directive references
textBinding(1, bind(loadDirective<Directive>(0).value)); textBinding(1, bind(loadDirective<Directive>(0).value));
} },
directiveDefs: defs
}); });
} }
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(toHtml(app)).toEqual('<div dir="" dirsameinstance="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); expect(dir !.cdr).toBe(app.cdr);
@ -316,7 +362,7 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** /**
* <my-comp> * <my-comp>
@ -326,9 +372,9 @@ describe('di', () => {
*/ */
template: function(ctx: any, cm: boolean) { template: function(ctx: any, cm: boolean) {
if (cm) { 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();
} }
elementEnd(); elementEnd();
@ -336,13 +382,13 @@ describe('di', () => {
} }
// TODO: remove loadDirective when removing directive references // TODO: remove loadDirective when removing directive references
textBinding(2, bind(loadDirective<Directive>(1).value)); textBinding(2, bind(loadDirective<Directive>(1).value));
} },
directiveDefs: defs
}); });
} }
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(toHtml(app)) expect(toHtml(app)).toEqual('<my-comp><div dir="" dirsame=""></div></my-comp>ViewRef');
.toEqual('<my-comp><div dir="" dirsameinstance=""></div></my-comp>ViewRef');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app !.cdr); expect(dir !.cdr).toBe(app !.cdr);
@ -358,7 +404,7 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** /**
* % if (showing) { * % if (showing) {
@ -373,7 +419,7 @@ describe('di', () => {
{ {
if (ctx.showing) { if (ctx.showing) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
@ -383,12 +429,13 @@ describe('di', () => {
embeddedViewEnd(); embeddedViewEnd();
} }
containerRefreshEnd(); containerRefreshEnd();
} },
directiveDefs: defs
}); });
} }
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(toHtml(app)).toEqual('<div dir="" dirsameinstance="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); expect(dir !.cdr).toBe(app.cdr);
@ -396,26 +443,6 @@ describe('di', () => {
}); });
it('should inject host component ChangeDetectorRef into directives on containers', () => { it('should inject host component ChangeDetectorRef into directives on containers', () => {
class IfDirective {
/* @Input */
myIf = true;
constructor(public template: TemplateRef<any>, 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 { class MyApp {
showing = true; showing = true;
@ -423,31 +450,32 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
/** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */ /** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */
template: function(ctx: MyApp, cm: boolean) { template: function(ctx: MyApp, cm: boolean) {
if (cm) { if (cm) {
container(0, [IfDirective], C1); container(0, C1, undefined, ['myIf', 'showing']);
} }
containerRefreshStart(0); containerRefreshStart(0);
containerRefreshEnd(); containerRefreshEnd();
function C1(ctx1: any, cm1: boolean) { function C1(ctx1: any, cm1: boolean) {
if (cm1) { if (cm1) {
elementStart(0, 'div', $e0_attrs$, [Directive, DirectiveSameInstance]); elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
// TODO: remove loadDirective when removing directive references // TODO: remove loadDirective when removing directive references
textBinding(1, bind(loadDirective<Directive>(0).value)); textBinding(1, bind(loadDirective<Directive>(0).value));
} }
} },
directiveDefs: defs
}); });
} }
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(toHtml(app)).toEqual('<div dir="" dirsameinstance="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); expect(dir !.cdr).toBe(app.cdr);
@ -457,20 +485,14 @@ describe('di', () => {
it('should injectAttribute', () => { it('should injectAttribute', () => {
let exist: string|undefined = 'wrong'; let exist: string|undefined = 'wrong';
let nonExist: string|undefined = 'wrong'; let nonExist: string|undefined = 'wrong';
class MyApp {
static ngComponentDef = defineComponent({ const MyApp = createComponent('my-app', function(ctx: any, cm: boolean) {
type: MyApp, if (cm) {
tag: 'my-app', elementStart(0, 'div', ['exist', 'existValue', 'other', 'ignore']);
factory: () => new MyApp(), exist = injectAttribute('exist');
template: function(ctx: MyApp, cm: boolean) { nonExist = injectAttribute('nonExist');
if (cm) { }
elementStart(0, 'div', ['exist', 'existValue', 'other', 'ignore']); });
exist = injectAttribute('exist');
nonExist = injectAttribute('nonExist');
}
}
});
}
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
expect(exist).toEqual('existValue'); expect(exist).toEqual('existValue');
@ -557,7 +579,7 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', selector: [[['my-app'], null]],
factory: () => new MyApp( factory: () => new MyApp(
directiveInject(String as any, InjectFlags.Default, 'DefaultValue')), directiveInject(String as any, InjectFlags.Default, 'DefaultValue')),
template: () => null template: () => null
@ -569,21 +591,14 @@ describe('di', () => {
}); });
it('should inject from parent view', () => { it('should inject from parent view', () => {
class ParentDirective { const ParentDirective = createDirective('parentDir');
static ngDirectiveDef = defineDirective({
type: ParentDirective,
factory: () => new ParentDirective(),
features: [PublicFeature]
});
}
class ChildDirective { class ChildDirective {
value: string; value: string;
constructor(public parent: ParentDirective) { constructor(public parent: any) { this.value = (parent.constructor as any).name; }
this.value = (parent.constructor as any).name;
}
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: ChildDirective, type: ChildDirective,
selector: [[['', 'childDir', ''], null]],
factory: () => new ChildDirective(directiveInject(ParentDirective)), factory: () => new ChildDirective(directiveInject(ParentDirective)),
features: [PublicFeature] features: [PublicFeature]
}); });
@ -591,10 +606,9 @@ describe('di', () => {
class Child2Directive { class Child2Directive {
value: boolean; value: boolean;
constructor(parent: ParentDirective, child: ChildDirective) { constructor(parent: any, child: ChildDirective) { this.value = parent === child.parent; }
this.value = parent === child.parent;
}
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
selector: [[['', 'child2Dir', ''], null]],
type: Child2Directive, type: Child2Directive,
factory: () => new Child2Directive( factory: () => new Child2Directive(
directiveInject(ParentDirective), directiveInject(ChildDirective)) directiveInject(ParentDirective), directiveInject(ChildDirective))
@ -603,14 +617,14 @@ describe('di', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [ParentDirective]); elementStart(0, 'div', ['parentDir', '']);
{ container(1); } { container(1); }
elementEnd(); elementEnd();
} }
containerRefreshStart(1); containerRefreshStart(1);
{ {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'span', null, [ChildDirective, Child2Directive]); elementStart(0, 'span', ['childDir', '', 'child2Dir', '']);
{ text(1); } { text(1); }
elementEnd(); elementEnd();
} }
@ -624,7 +638,12 @@ describe('di', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
expect(renderToHtml(Template, {})).toEqual('<div><span>ParentDirective-true</span></div>'); const defs = [
ChildDirective.ngDirectiveDef, Child2Directive.ngDirectiveDef,
ParentDirective.ngDirectiveDef
];
expect(renderToHtml(Template, {}, defs))
.toEqual('<div parentdir=""><span child2dir="" childdir="">Directive-true</span></div>');
}); });
it('should inject from module Injector', () => { it('should inject from module Injector', () => {
@ -635,7 +654,7 @@ describe('di', () => {
describe('getOrCreateNodeInjector', () => { describe('getOrCreateNodeInjector', () => {
it('should handle initial undefined state', () => { it('should handle initial undefined state', () => {
const contentView = const contentView =
createLView(-1, null !, createTView(), null, null, LViewFlags.CheckAlways); createLView(-1, null !, createTView(null), null, null, LViewFlags.CheckAlways);
const oldView = enterView(contentView, null !); const oldView = enterView(contentView, null !);
try { try {
const parent = createLNode(0, LNodeType.Element, null, null); const parent = createLNode(0, LNodeType.Element, null, null);

View File

@ -22,6 +22,7 @@ describe('directive', () => {
klass = 'foo'; klass = 'foo';
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', 'dir', ''], null]],
factory: () => directiveInstance = new Directive, factory: () => directiveInstance = new Directive,
hostBindings: (directiveIndex: number, elementIndex: number) => { hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty( elementProperty(
@ -32,14 +33,15 @@ describe('directive', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'span', null, [Directive]); elementStart(0, 'span', ['dir', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})).toEqual('<span class="foo"></span>'); const defs = [Directive.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs)).toEqual('<span class="foo" dir=""></span>');
directiveInstance !.klass = 'bar'; directiveInstance !.klass = 'bar';
expect(renderToHtml(Template, {})).toEqual('<span class="bar"></span>'); expect(renderToHtml(Template, {}, defs)).toEqual('<span class="bar" dir=""></span>');
}); });
}); });

View File

@ -17,7 +17,7 @@ describe('exports', () => {
/** <input value="one" #myInput> {{ myInput.value }} */ /** <input value="one" #myInput> {{ myInput.value }} */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(0, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
text(1); text(1);
} }
@ -33,7 +33,7 @@ describe('exports', () => {
/** <comp #myComp></comp> {{ myComp.name }} */ /** <comp #myComp></comp> {{ myComp.name }} */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComponent, null, null, ['myComp', '']); elementStart(0, 'comp', null, ['myComp', '']);
elementEnd(); elementEnd();
text(1); text(1);
} }
@ -46,13 +46,13 @@ describe('exports', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'comp', selector: [[['comp'], null]],
template: function() {}, template: function() {},
factory: () => new MyComponent factory: () => new MyComponent
}); });
} }
expect(renderToHtml(Template, {})).toEqual('<comp></comp>Nancy'); expect(renderToHtml(Template, {}, [MyComponent.ngComponentDef])).toEqual('<comp></comp>Nancy');
}); });
it('should support component instance fed into directive', () => { it('should support component instance fed into directive', () => {
@ -63,7 +63,7 @@ describe('exports', () => {
constructor() { myComponent = this; } constructor() { myComponent = this; }
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'comp', selector: [[['comp'], null]],
template: function() {}, template: function() {},
factory: () => new MyComponent factory: () => new MyComponent
}); });
@ -72,23 +72,29 @@ describe('exports', () => {
class MyDir { class MyDir {
myDir: MyComponent; myDir: MyComponent;
constructor() { myDir = this; } constructor() { myDir = this; }
static ngDirectiveDef = static ngDirectiveDef = defineDirective({
defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}}); type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => new MyDir,
inputs: {myDir: 'myDir'}
});
} }
const defs = [MyComponent.ngComponentDef, MyDir.ngDirectiveDef];
/** <comp #myComp></comp> <div [myDir]="myComp"></div> */ /** <comp #myComp></comp> <div [myDir]="myComp"></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComponent, null, null, ['myComp', '']); elementStart(0, 'comp', null, ['myComp', '']);
elementEnd(); elementEnd();
elementStart(1, 'div', null, [MyDir]); elementStart(1, 'div', ['myDir', '']);
elementEnd(); elementEnd();
} }
// TODO: replace loadDirective when removing directive refs // TODO: replace loadDirective when removing directive refs
elementProperty(1, 'myDir', bind(loadDirective<MyComponent>(0))); elementProperty(1, 'myDir', bind(loadDirective<MyComponent>(0)));
} }
renderToHtml(Template, {}); renderToHtml(Template, {}, defs);
expect(myDir !.myDir).toEqual(myComponent !); expect(myDir !.myDir).toEqual(myComponent !);
}); });
@ -97,7 +103,7 @@ describe('exports', () => {
/** <div someDir #myDir="someDir"></div> {{ myDir.name }} */ /** <div someDir #myDir="someDir"></div> {{ myDir.name }} */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [SomeDir], ['myDir', 'someDir']); elementStart(0, 'div', ['someDir', ''], ['myDir', 'someDir']);
elementEnd(); elementEnd();
text(1); text(1);
} }
@ -107,11 +113,16 @@ describe('exports', () => {
class SomeDir { class SomeDir {
name = 'Drew'; name = 'Drew';
static ngDirectiveDef = static ngDirectiveDef = defineDirective({
defineDirective({type: SomeDir, factory: () => new SomeDir, exportAs: 'someDir'}); type: SomeDir,
selector: [[['', 'someDir', ''], null]],
factory: () => new SomeDir,
exportAs: 'someDir'
});
} }
expect(renderToHtml(Template, {})).toEqual('<div></div>Drew'); expect(renderToHtml(Template, {}, [SomeDir.ngDirectiveDef]))
.toEqual('<div somedir=""></div>Drew');
}); });
describe('forward refs', () => { describe('forward refs', () => {
@ -120,7 +131,7 @@ describe('exports', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
text(0); text(0);
elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(1, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(1); let myInput = elementStart(1);
@ -137,7 +148,7 @@ describe('exports', () => {
if (cm) { if (cm) {
elementStart(0, 'div'); elementStart(0, 'div');
elementEnd(); elementEnd();
elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(1, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(1); let myInput = elementStart(1);
@ -153,7 +164,7 @@ describe('exports', () => {
if (cm) { if (cm) {
elementStart(0, 'div'); elementStart(0, 'div');
elementEnd(); elementEnd();
elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(1, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(1); let myInput = elementStart(1);
@ -169,7 +180,7 @@ describe('exports', () => {
if (cm) { if (cm) {
elementStart(0, 'div'); elementStart(0, 'div');
elementEnd(); elementEnd();
elementStart(1, 'input', ['type', 'checkbox', 'checked', 'true'], null, ['myInput', '']); elementStart(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(1); let myInput = elementStart(1);
@ -190,7 +201,7 @@ describe('exports', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'comp', selector: [[['comp'], null]],
template: function(ctx: MyComponent, cm: boolean) {}, template: function(ctx: MyComponent, cm: boolean) {},
factory: () => new MyComponent factory: () => new MyComponent
}); });
@ -201,23 +212,27 @@ describe('exports', () => {
constructor() { myDir = this; } constructor() { myDir = this; }
static ngDirectiveDef = static ngDirectiveDef = defineDirective({
defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}}); type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => new MyDir,
inputs: {myDir: 'myDir'}
});
} }
/** <div [myDir]="myComp"></div><comp #myComp></comp> */ /** <div [myDir]="myComp"></div><comp #myComp></comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [MyDir]); elementStart(0, 'div', ['myDir', '']);
elementEnd(); elementEnd();
elementStart(1, MyComponent, null, null, ['myComp', '']); elementStart(1, 'comp', null, ['myComp', '']);
elementEnd(); elementEnd();
} }
// TODO: replace loadDirective when removing directive refs // TODO: replace loadDirective when removing directive refs
elementProperty(0, 'myDir', bind(loadDirective<MyComponent>(1))); elementProperty(0, 'myDir', bind(loadDirective<MyComponent>(1)));
} }
renderToHtml(Template, {}); renderToHtml(Template, {}, [MyComponent.ngComponentDef, MyDir.ngDirectiveDef]);
expect(myDir !.myDir).toEqual(myComponent !); expect(myDir !.myDir).toEqual(myComponent !);
}); });
@ -228,9 +243,9 @@ describe('exports', () => {
if (cm) { if (cm) {
text(0); text(0);
text(1); text(1);
elementStart(2, MyComponent, null, null, ['myComp', '']); elementStart(2, 'comp', null, ['myComp', '']);
elementEnd(); elementEnd();
elementStart(3, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(3, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(3); let myInput = elementStart(3);
@ -249,12 +264,13 @@ describe('exports', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComponent, type: MyComponent,
tag: 'comp', selector: [[['comp'], null]],
template: function() {}, template: function() {},
factory: () => new MyComponent factory: () => new MyComponent
}); });
} }
expect(renderToHtml(Template, {})).toEqual('oneNancy<comp></comp><input value="one">'); expect(renderToHtml(Template, {}, [MyComponent.ngComponentDef]))
.toEqual('oneNancy<comp></comp><input value="one">');
}); });
it('should work inside a view container', () => { it('should work inside a view container', () => {
@ -271,7 +287,7 @@ describe('exports', () => {
{ {
if (cm1) { if (cm1) {
text(0); text(0);
elementStart(1, 'input', ['value', 'one'], null, ['myInput', '']); elementStart(1, 'input', ['value', 'one'], ['myInput', '']);
elementEnd(); elementEnd();
} }
let myInput = elementStart(1); let myInput = elementStart(1);

View File

@ -178,7 +178,7 @@ describe('render3 integration test', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: TodoComponent, type: TodoComponent,
tag: 'todo', selector: [[['todo'], null]],
template: function TodoTemplate(ctx: any, cm: boolean) { template: function TodoTemplate(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'p'); elementStart(0, 'p');
@ -194,26 +194,28 @@ describe('render3 integration test', () => {
}); });
} }
const defs = [TodoComponent.ngComponentDef];
it('should support a basic component template', () => { it('should support a basic component template', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, TodoComponent); elementStart(0, 'todo');
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, null)).toEqual('<todo><p>Todo one</p></todo>'); expect(renderToHtml(Template, null, defs)).toEqual('<todo><p>Todo one</p></todo>');
}); });
it('should support a component template with sibling', () => { it('should support a component template with sibling', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, TodoComponent); elementStart(0, 'todo');
elementEnd(); elementEnd();
text(1, 'two'); text(1, 'two');
} }
} }
expect(renderToHtml(Template, null)).toEqual('<todo><p>Todo one</p></todo>two'); expect(renderToHtml(Template, null, defs)).toEqual('<todo><p>Todo one</p></todo>two');
}); });
it('should support a component template with component sibling', () => { it('should support a component template with component sibling', () => {
@ -223,13 +225,13 @@ describe('render3 integration test', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, TodoComponent); elementStart(0, 'todo');
elementEnd(); elementEnd();
elementStart(1, TodoComponent); elementStart(1, 'todo');
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, null)) expect(renderToHtml(Template, null, defs))
.toEqual('<todo><p>Todo one</p></todo><todo><p>Todo one</p></todo>'); .toEqual('<todo><p>Todo one</p></todo><todo><p>Todo one</p></todo>');
}); });
@ -240,7 +242,7 @@ describe('render3 integration test', () => {
title = 'one'; title = 'one';
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: TodoComponentHostBinding, type: TodoComponentHostBinding,
tag: 'todo', selector: [[['todo'], null]],
template: function TodoComponentHostBindingTemplate( template: function TodoComponentHostBindingTemplate(
ctx: TodoComponentHostBinding, cm: boolean) { ctx: TodoComponentHostBinding, cm: boolean) {
if (cm) { if (cm) {
@ -260,21 +262,22 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, TodoComponentHostBinding); elementStart(0, 'todo');
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})).toEqual('<todo title="one">one</todo>'); const defs = [TodoComponentHostBinding.ngComponentDef];
expect(renderToHtml(Template, {}, defs)).toEqual('<todo title="one">one</todo>');
cmptInstance !.title = 'two'; cmptInstance !.title = 'two';
expect(renderToHtml(Template, {})).toEqual('<todo title="two">two</todo>'); expect(renderToHtml(Template, {}, defs)).toEqual('<todo title="two">two</todo>');
}); });
it('should support root component with host attribute', () => { it('should support root component with host attribute', () => {
class HostAttributeComp { class HostAttributeComp {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: HostAttributeComp, type: HostAttributeComp,
tag: 'host-attr-comp', selector: [[['host-attr-comp'], null]],
factory: () => new HostAttributeComp(), factory: () => new HostAttributeComp(),
template: (ctx: HostAttributeComp, cm: boolean) => {}, template: (ctx: HostAttributeComp, cm: boolean) => {},
attributes: ['role', 'button'] attributes: ['role', 'button']
@ -291,7 +294,7 @@ describe('render3 integration test', () => {
name = 'Bess'; name = 'Bess';
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'comp', selector: [[['comp'], null]],
template: function MyCompTemplate(ctx: any, cm: boolean) { template: function MyCompTemplate(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'p'); elementStart(0, 'p');
@ -306,12 +309,13 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'comp');
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, null)).toEqual('<comp><p>Bess</p></comp>'); expect(renderToHtml(Template, null, [MyComp.ngComponentDef]))
.toEqual('<comp><p>Bess</p></comp>');
}); });
it('should support a component with sub-views', () => { it('should support a component with sub-views', () => {
@ -324,7 +328,7 @@ describe('render3 integration test', () => {
condition: boolean; condition: boolean;
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'comp', selector: [[['comp'], null]],
template: function MyCompTemplate(ctx: any, cm: boolean) { template: function MyCompTemplate(ctx: any, cm: boolean) {
if (cm) { if (cm) {
container(0); container(0);
@ -350,14 +354,16 @@ describe('render3 integration test', () => {
/** <comp [condition]="condition"></comp> */ /** <comp [condition]="condition"></comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'condition', bind(ctx.condition)); elementProperty(0, 'condition', bind(ctx.condition));
} }
expect(renderToHtml(Template, {condition: true})).toEqual('<comp><div>text</div></comp>'); const defs = [MyComp.ngComponentDef];
expect(renderToHtml(Template, {condition: false})).toEqual('<comp></comp>'); expect(renderToHtml(Template, {condition: true}, defs))
.toEqual('<comp><div>text</div></comp>');
expect(renderToHtml(Template, {condition: false}, defs)).toEqual('<comp></comp>');
}); });
@ -428,7 +434,7 @@ describe('render3 integration test', () => {
beforeTree: Tree; beforeTree: Tree;
afterTree: Tree; afterTree: Tree;
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
tag: 'child', selector: [[['child'], null]],
type: ChildComponent, type: ChildComponent,
template: function ChildComponentTemplate( template: function ChildComponentTemplate(
ctx: {beforeTree: Tree, afterTree: Tree}, cm: boolean) { ctx: {beforeTree: Tree, afterTree: Tree}, cm: boolean) {
@ -460,7 +466,7 @@ describe('render3 integration test', () => {
function parentTemplate(ctx: ParentCtx, cm: boolean) { function parentTemplate(ctx: ParentCtx, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ChildComponent); elementStart(0, 'child');
{ container(1); } { container(1); }
elementEnd(); elementEnd();
} }
@ -482,14 +488,15 @@ describe('render3 integration test', () => {
projectedTree: {beforeLabel: 'p'}, projectedTree: {beforeLabel: 'p'},
afterTree: {afterLabel: 'z'} afterTree: {afterLabel: 'z'}
}; };
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>apz</child>'); const defs = [ChildComponent.ngComponentDef];
expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>apz</child>');
ctx.projectedTree = {subTrees: [{}, {}, {subTrees: [{}, {}]}, {}]}; ctx.projectedTree = {subTrees: [{}, {}, {subTrees: [{}, {}]}, {}]};
ctx.beforeTree.subTrees !.push({afterLabel: 'b'}); ctx.beforeTree.subTrees !.push({afterLabel: 'b'});
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>abz</child>'); expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>abz</child>');
ctx.projectedTree.subTrees ![1].afterLabel = 'h'; ctx.projectedTree.subTrees ![1].afterLabel = 'h';
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>abhz</child>'); expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>abhz</child>');
ctx.beforeTree.subTrees !.push({beforeLabel: 'c'}); ctx.beforeTree.subTrees !.push({beforeLabel: 'c'});
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>abchz</child>'); expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>abchz</child>');
// To check the context easily: // To check the context easily:
// console.log(JSON.stringify(ctx)); // console.log(JSON.stringify(ctx));
@ -633,6 +640,7 @@ describe('render3 integration test', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: HostBindingDir, type: HostBindingDir,
selector: [[['', 'hostBindingDir', ''], null]],
factory: function HostBindingDir_Factory() { factory: function HostBindingDir_Factory() {
return hostBindingDir = new HostBindingDir(); return hostBindingDir = new HostBindingDir();
}, },
@ -645,16 +653,17 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['hostBindingDir', ''], [HostBindingDir]); elementStart(0, 'div', ['hostBindingDir', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})) const defs = [HostBindingDir.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs))
.toEqual(`<div aria-label="some label" hostbindingdir=""></div>`); .toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
hostBindingDir !.label = 'other label'; hostBindingDir !.label = 'other label';
expect(renderToHtml(Template, {})) expect(renderToHtml(Template, {}, defs))
.toEqual(`<div aria-label="other label" hostbindingdir=""></div>`); .toEqual(`<div aria-label="other label" hostbindingdir=""></div>`);
}); });
}); });

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ describe('event listeners', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'comp', selector: [[['comp'], null]],
/** <button (click)="onClick()"> Click me </button> */ /** <button (click)="onClick()"> Click me </button> */
template: function CompTemplate(ctx: any, cm: boolean) { template: function CompTemplate(ctx: any, cm: boolean) {
if (cm) { if (cm) {
@ -61,7 +61,7 @@ describe('event listeners', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: PreventDefaultComp, type: PreventDefaultComp,
tag: 'prevent-default-comp', selector: [[['prevent-default-comp'], null]],
factory: () => new PreventDefaultComp(), factory: () => new PreventDefaultComp(),
/** <button (click)="onClick($event)">Click</button> */ /** <button (click)="onClick($event)">Click</button> */
template: (ctx: PreventDefaultComp, cm: boolean) => { template: (ctx: PreventDefaultComp, cm: boolean) => {
@ -235,6 +235,7 @@ describe('event listeners', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: HostListenerDir, type: HostListenerDir,
selector: [[['', 'hostListenerDir', ''], null]],
factory: function HostListenerDir_Factory() { factory: function HostListenerDir_Factory() {
const $dir$ = new HostListenerDir(); const $dir$ = new HostListenerDir();
listener('click', function() { return $dir$.onClick(); }); listener('click', function() { return $dir$.onClick(); });
@ -245,13 +246,13 @@ describe('event listeners', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', ['hostListenerDir', ''], [HostListenerDir]); elementStart(0, 'button', ['hostListenerDir', '']);
text(1, 'Click'); text(1, 'Click');
elementEnd(); elementEnd();
} }
} }
renderToHtml(Template, {}); renderToHtml(Template, {}, [HostListenerDir.ngDirectiveDef]);
const button = containerEl.querySelector('button') !; const button = containerEl.querySelector('button') !;
button.click(); button.click();
expect(events).toEqual(['click!']); expect(events).toEqual(['click!']);
@ -337,9 +338,9 @@ describe('event listeners', () => {
if (ctx.showing) { if (ctx.showing) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
text(0, 'Hello'); text(0, 'Hello');
elementStart(1, MyComp); elementStart(1, 'comp');
elementEnd(); elementEnd();
elementStart(2, MyComp); elementStart(2, 'comp');
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();
@ -349,7 +350,7 @@ describe('event listeners', () => {
} }
const ctx = {showing: true}; const ctx = {showing: true};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, [MyComp.ngComponentDef]);
const buttons = containerEl.querySelectorAll('button') !; const buttons = containerEl.querySelectorAll('button') !;
buttons[0].click(); buttons[0].click();
@ -360,7 +361,7 @@ describe('event listeners', () => {
// the child view listener should be removed when the parent view is removed // the child view listener should be removed when the parent view is removed
ctx.showing = false; ctx.showing = false;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, [MyComp.ngComponentDef]);
buttons[0].click(); buttons[0].click();
buttons[1].click(); buttons[1].click();
expect(comps[0] !.counter).toEqual(1); expect(comps[0] !.counter).toEqual(1);

View File

@ -15,6 +15,8 @@ import {containerEl, renderToHtml} from './render_util';
describe('outputs', () => { describe('outputs', () => {
let buttonToggle: ButtonToggle; let buttonToggle: ButtonToggle;
let destroyComp: DestroyComp;
let buttonDir: MyButton;
class ButtonToggle { class ButtonToggle {
change = new EventEmitter(); change = new EventEmitter();
@ -22,7 +24,7 @@ describe('outputs', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ButtonToggle, type: ButtonToggle,
tag: 'button-toggle', selector: [[['button-toggle'], null]],
template: function(ctx: any, cm: boolean) {}, template: function(ctx: any, cm: boolean) {},
factory: () => buttonToggle = new ButtonToggle(), factory: () => buttonToggle = new ButtonToggle(),
outputs: {change: 'change', resetStream: 'reset'} outputs: {change: 'change', resetStream: 'reset'}
@ -36,16 +38,47 @@ describe('outputs', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: OtherDir, type: OtherDir,
selector: [[['', 'otherDir', ''], null]],
factory: () => otherDir = new OtherDir, factory: () => otherDir = new OtherDir,
outputs: {changeStream: 'change'} 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()
});
}
/** <button myButton (click)="onClick()">Click me</button> */
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', () => { it('should call component output function when event is emitted', () => {
/** <button-toggle (change)="onChange()"></button-toggle> */ /** <button-toggle (change)="onChange()"></button-toggle> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -55,7 +88,7 @@ describe('outputs', () => {
let counter = 0; let counter = 0;
const ctx = {onChange: () => counter++}; const ctx = {onChange: () => counter++};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
@ -68,7 +101,7 @@ describe('outputs', () => {
/** <button-toggle (change)="onChange()" (reset)="onReset()"></button-toggle> */ /** <button-toggle (change)="onChange()" (reset)="onReset()"></button-toggle> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
listener('reset', function() { return ctx.onReset(); }); listener('reset', function() { return ctx.onReset(); });
@ -80,7 +113,7 @@ describe('outputs', () => {
let counter = 0; let counter = 0;
let resetCounter = 0; let resetCounter = 0;
const ctx = {onChange: () => counter++, onReset: () => resetCounter++}; const ctx = {onChange: () => counter++, onReset: () => resetCounter++};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
@ -93,7 +126,7 @@ describe('outputs', () => {
/** <button-toggle (change)="counter++"></button-toggle> */ /** <button-toggle (change)="counter++"></button-toggle> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.counter++; }); listener('change', function() { return ctx.counter++; });
} }
@ -102,7 +135,7 @@ describe('outputs', () => {
} }
const ctx = {counter: 0}; const ctx = {counter: 0};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(ctx.counter).toEqual(1); expect(ctx.counter).toEqual(1);
@ -127,7 +160,7 @@ describe('outputs', () => {
{ {
if (ctx.condition) { if (ctx.condition) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -141,13 +174,13 @@ describe('outputs', () => {
let counter = 0; let counter = 0;
const ctx = {onChange: () => counter++, condition: true}; const ctx = {onChange: () => counter++, condition: true};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
ctx.condition = false; ctx.condition = false;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
@ -177,7 +210,7 @@ describe('outputs', () => {
{ {
if (ctx.condition2) { if (ctx.condition2) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -195,33 +228,19 @@ describe('outputs', () => {
let counter = 0; let counter = 0;
const ctx = {onChange: () => counter++, condition: true, condition2: true}; const ctx = {onChange: () => counter++, condition: true, condition2: true};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
ctx.condition = false; ctx.condition = false;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
}); });
it('should work properly when view also has listeners and destroys', () => { 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) { * % if (condition) {
* <button (click)="onClick()">Click me</button> * <button (click)="onClick()">Click me</button>
@ -243,12 +262,12 @@ describe('outputs', () => {
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
elementStart(2, ButtonToggle); elementStart(2, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
elementEnd(); elementEnd();
elementStart(3, DestroyComp); elementStart(3, 'destroy-comp');
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();
@ -260,7 +279,7 @@ describe('outputs', () => {
let clickCounter = 0; let clickCounter = 0;
let changeCounter = 0; let changeCounter = 0;
const ctx = {condition: true, onChange: () => changeCounter++, onClick: () => clickCounter++}; const ctx = {condition: true, onChange: () => changeCounter++, onClick: () => clickCounter++};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(changeCounter).toEqual(1); expect(changeCounter).toEqual(1);
@ -272,7 +291,7 @@ describe('outputs', () => {
expect(clickCounter).toEqual(1); expect(clickCounter).toEqual(1);
ctx.condition = false; ctx.condition = false;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
expect(destroyComp !.events).toEqual(['destroy']); expect(destroyComp !.events).toEqual(['destroy']);
@ -283,19 +302,9 @@ describe('outputs', () => {
}); });
it('should fire event listeners along with outputs if they match', () => { it('should fire event listeners along with outputs if they match', () => {
let buttonDir: MyButton;
/** <button myButton (click)="onClick()">Click me</button> */
class MyButton {
click = new EventEmitter();
static ngDirectiveDef = defineDirective(
{type: MyButton, factory: () => buttonDir = new MyButton, outputs: {click: 'click'}});
}
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [MyButton]); elementStart(0, 'button', ['myButton', '']);
{ {
listener('click', function() { return ctx.onClick(); }); listener('click', function() { return ctx.onClick(); });
} }
@ -304,7 +313,7 @@ describe('outputs', () => {
} }
let counter = 0; let counter = 0;
renderToHtml(Template, {counter, onClick: () => counter++}); renderToHtml(Template, {counter, onClick: () => counter++}, deps);
// To match current Angular behavior, the click listener is still // To match current Angular behavior, the click listener is still
// set up in addition to any matching outputs. // set up in addition to any matching outputs.
@ -320,7 +329,7 @@ describe('outputs', () => {
/** <button-toggle (change)="onChange()" otherDir></button-toggle> */ /** <button-toggle (change)="onChange()" otherDir></button-toggle> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle, null, [OtherDir]); elementStart(0, 'button-toggle', ['otherDir', '']);
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -329,7 +338,7 @@ describe('outputs', () => {
} }
let counter = 0; let counter = 0;
renderToHtml(Template, {counter, onChange: () => counter++}); renderToHtml(Template, {counter, onChange: () => counter++}, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
@ -339,19 +348,23 @@ describe('outputs', () => {
}); });
it('should work with an input and output of the same name', () => { it('should work with an input and output of the same name', () => {
let otherDir: OtherDir; let otherDir: OtherChangeDir;
class OtherDir { class OtherChangeDir {
change: boolean; change: boolean;
static ngDirectiveDef = defineDirective( static ngDirectiveDef = defineDirective({
{type: OtherDir, factory: () => otherDir = new OtherDir, inputs: {change: 'change'}}); type: OtherChangeDir,
selector: [[['', 'otherChangeDir', ''], null]],
factory: () => otherDir = new OtherChangeDir,
inputs: {change: 'change'}
});
} }
/** <button-toggle (change)="onChange()" otherDir [change]="change"></button-toggle> */ /** <button-toggle (change)="onChange()" otherChangeDir [change]="change"></button-toggle> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle, null, [OtherDir]); elementStart(0, 'button-toggle', ['otherChangeDir', '']);
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -361,10 +374,11 @@ describe('outputs', () => {
} }
let counter = 0; 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); 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); expect(otherDir !.change).toEqual(false);
buttonToggle !.change.next(); buttonToggle !.change.next();
@ -395,7 +409,7 @@ describe('outputs', () => {
{ {
if (ctx.condition) { if (ctx.condition) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, 'button-toggle');
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -404,7 +418,7 @@ describe('outputs', () => {
embeddedViewEnd(); embeddedViewEnd();
} else { } else {
if (embeddedViewStart(1)) { if (embeddedViewStart(1)) {
elementStart(0, 'div', null, [OtherDir]); elementStart(0, 'div', ['otherDir', '']);
{ {
listener('change', function() { return ctx.onChange(); }); listener('change', function() { return ctx.onChange(); });
} }
@ -418,13 +432,13 @@ describe('outputs', () => {
let counter = 0; let counter = 0;
const ctx = {condition: true, onChange: () => counter++, onClick: () => {}}; const ctx = {condition: true, onChange: () => counter++, onClick: () => {}};
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
buttonToggle !.change.next(); buttonToggle !.change.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
ctx.condition = false; ctx.condition = false;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
expect(counter).toEqual(1); expect(counter).toEqual(1);
otherDir !.changeStream.next(); otherDir !.changeStream.next();

View File

@ -6,15 +6,15 @@
* found in the LICENSE file at https://angular.io/license * 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 {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 {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 {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2'; import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
import {renderComponent, renderToHtml} from './render_util'; import {renderToHtml} from './render_util';
let log: string[] = []; let log: string[] = [];
@ -51,8 +51,12 @@ describe('pipe', () => {
constructor() { this.dirProp = ''; } constructor() { this.dirProp = ''; }
static ngDirectiveDef = static ngDirectiveDef = defineDirective({
defineDirective({type: MyDir, factory: () => new MyDir(), inputs: {dirProp: 'elprop'}}); type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => new MyDir(),
inputs: {dirProp: 'elprop'}
});
} }
@Pipe({name: 'double'}) @Pipe({name: 'double'})
@ -67,14 +71,14 @@ describe('pipe', () => {
function Template(ctx: string, cm: boolean) { function Template(ctx: string, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', null, [MyDir]); elementStart(0, 'div', ['myDir', '']);
pipe(1, DoublePipe.ngPipeDef); pipe(1, DoublePipe.ngPipeDef);
elementEnd(); elementEnd();
} }
elementProperty(0, 'elprop', bind(pipeBind1(1, ctx))); elementProperty(0, 'elprop', bind(pipeBind1(1, ctx)));
directive = loadDirective(0); directive = loadDirective(0);
} }
renderToHtml(Template, 'a'); renderToHtml(Template, 'a', [MyDir.ngDirectiveDef]);
expect(directive !.dirProp).toEqual('aa'); expect(directive !.dirProp).toEqual('aa');
}); });
@ -124,11 +128,11 @@ describe('pipe', () => {
} }
elementProperty(0, 'someProp', bind(pipeBind1(1, 'Megatron'))); elementProperty(0, 'someProp', bind(pipeBind1(1, 'Megatron')));
} }
renderToHtml(Template, person, rendererFactory2); renderToHtml(Template, person, [], rendererFactory2);
expect(renderLog.log).toEqual(['someProp=Megatron']); expect(renderLog.log).toEqual(['someProp=Megatron']);
renderLog.clear(); renderLog.clear();
renderToHtml(Template, person, rendererFactory2); renderToHtml(Template, person, [], rendererFactory2);
expect(renderLog.log).toEqual([]); expect(renderLog.log).toEqual([]);
}); });
@ -192,7 +196,7 @@ describe('pipe', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
const pipeInstances: CountingPipe[] = []; const pipeInstances: CountingPipe[] = [];
renderToHtml(Template, {}, rendererFactory2); renderToHtml(Template, {}, [], rendererFactory2);
expect(pipeInstances.length).toEqual(4); expect(pipeInstances.length).toEqual(4);
expect(pipeInstances[0]).toBeAnInstanceOf(CountingPipe); expect(pipeInstances[0]).toBeAnInstanceOf(CountingPipe);
expect(pipeInstances[1]).toBe(pipeInstances[0]); expect(pipeInstances[1]).toBe(pipeInstances[0]);
@ -249,7 +253,7 @@ describe('pipe', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
const pipeInstances: CountingImpurePipe[] = []; const pipeInstances: CountingImpurePipe[] = [];
renderToHtml(Template, {}, rendererFactory2); renderToHtml(Template, {}, [], rendererFactory2);
expect(pipeInstances.length).toEqual(4); expect(pipeInstances.length).toEqual(4);
expect(pipeInstances[0]).toBeAnInstanceOf(CountingImpurePipe); expect(pipeInstances[0]).toBeAnInstanceOf(CountingImpurePipe);
expect(pipeInstances[1]).toBeAnInstanceOf(CountingImpurePipe); expect(pipeInstances[1]).toBeAnInstanceOf(CountingImpurePipe);

View File

@ -11,7 +11,7 @@ import {EventEmitter} from '@angular/core';
import {defineComponent, defineDirective, tick} from '../../src/render3/index'; 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 {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', () => { describe('elementProperty', () => {
@ -68,7 +68,7 @@ describe('elementProperty', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: HostBindingComp, type: HostBindingComp,
tag: 'host-binding-comp', selector: [[['host-binding-comp'], null]],
factory: () => new HostBindingComp(), factory: () => new HostBindingComp(),
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
const instance = loadDirective(dirIndex) as HostBindingComp; const instance = loadDirective(dirIndex) as HostBindingComp;
@ -89,12 +89,18 @@ describe('elementProperty', () => {
describe('input properties', () => { describe('input properties', () => {
let button: MyButton; let button: MyButton;
let otherDir: OtherDir; let otherDir: OtherDir;
let otherDisabledDir: OtherDisabledDir;
let idDir: IdDir;
class MyButton { class MyButton {
disabled: boolean; disabled: boolean;
static ngDirectiveDef = defineDirective( static ngDirectiveDef = defineDirective({
{type: MyButton, factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}}); type: MyButton,
selector: [[['', 'myButton', ''], null]],
factory: () => button = new MyButton(),
inputs: {disabled: 'disabled'}
});
} }
class OtherDir { class OtherDir {
@ -103,18 +109,47 @@ describe('elementProperty', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: OtherDir, type: OtherDir,
selector: [[['', 'otherDir', ''], null]],
factory: () => otherDir = new OtherDir(), factory: () => otherDir = new OtherDir(),
inputs: {id: 'id'}, inputs: {id: 'id'},
outputs: {clickStream: 'click'} 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)', () => { it('should check input properties before setting (directives)', () => {
/** <button myButton [id]="id" [disabled]="isDisabled">Click me</button> */ /** <button myButton otherDir [id]="id" [disabled]="isDisabled">Click me</button> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [MyButton, OtherDir]); elementStart(0, 'button', ['otherDir', '', 'myButton', '']);
{ text(1, 'Click me'); } { text(1, 'Click me'); }
elementEnd(); elementEnd();
} }
@ -124,13 +159,15 @@ describe('elementProperty', () => {
} }
const ctx: any = {isDisabled: true, id: 0}; const ctx: any = {isDisabled: true, id: 0};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`); expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdir="">Click me</button>`);
expect(button !.disabled).toEqual(true); expect(button !.disabled).toEqual(true);
expect(otherDir !.id).toEqual(0); expect(otherDir !.id).toEqual(0);
ctx.isDisabled = false; ctx.isDisabled = false;
ctx.id = 1; ctx.id = 1;
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`); expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdir="">Click me</button>`);
expect(button !.disabled).toEqual(false); expect(button !.disabled).toEqual(false);
expect(otherDir !.id).toEqual(1); expect(otherDir !.id).toEqual(1);
}); });
@ -140,7 +177,7 @@ describe('elementProperty', () => {
/** <button myButton [id]="id" [disabled]="isDisabled">Click me</button> */ /** <button myButton [id]="id" [disabled]="isDisabled">Click me</button> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [MyButton]); elementStart(0, 'button', ['myButton', '']);
{ text(1, 'Click me'); } { text(1, 'Click me'); }
elementEnd(); elementEnd();
} }
@ -150,23 +187,27 @@ describe('elementProperty', () => {
} }
const ctx: any = {isDisabled: true, id: 0}; const ctx: any = {isDisabled: true, id: 0};
expect(renderToHtml(Template, ctx)).toEqual(`<button id="0">Click me</button>`);
expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button id="0" mybutton="">Click me</button>`);
expect(button !.disabled).toEqual(true); expect(button !.disabled).toEqual(true);
ctx.isDisabled = false; ctx.isDisabled = false;
ctx.id = 1; ctx.id = 1;
expect(renderToHtml(Template, ctx)).toEqual(`<button id="1">Click me</button>`); expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button id="1" mybutton="">Click me</button>`);
expect(button !.disabled).toEqual(false); expect(button !.disabled).toEqual(false);
}); });
it('should check that property is not an input property before setting (component)', () => { it('should check that property is not an input property before setting (component)', () => {
let comp: Comp; let comp: Comp;
class Comp { class Comp {
id: number; id: number;
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: Comp, type: Comp,
tag: 'comp', selector: [[['comp'], null]],
template: function(ctx: any, cm: boolean) {}, template: function(ctx: any, cm: boolean) {},
factory: () => comp = new Comp(), factory: () => comp = new Comp(),
inputs: {id: 'id'} inputs: {id: 'id'}
@ -176,36 +217,26 @@ describe('elementProperty', () => {
/** <comp [id]="id"></comp> */ /** <comp [id]="id"></comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, Comp); elementStart(0, 'comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'id', bind(ctx.id)); elementProperty(0, 'id', bind(ctx.id));
} }
expect(renderToHtml(Template, {id: 1})).toEqual(`<comp></comp>`); const deps = [Comp.ngComponentDef];
expect(renderToHtml(Template, {id: 1}, deps)).toEqual(`<comp></comp>`);
expect(comp !.id).toEqual(1); expect(comp !.id).toEqual(1);
expect(renderToHtml(Template, {id: 2})).toEqual(`<comp></comp>`); expect(renderToHtml(Template, {id: 2}, deps)).toEqual(`<comp></comp>`);
expect(comp !.id).toEqual(2); expect(comp !.id).toEqual(2);
}); });
it('should support two input properties with the same name', () => { 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'}
});
}
/** <button myButton otherDisabledDir [disabled]="isDisabled">Click me</button> */ /** <button myButton otherDisabledDir [disabled]="isDisabled">Click me</button> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [MyButton, OtherDisabledDir]); elementStart(0, 'button', ['myButton', '', 'otherDisabledDir', '']);
{ text(1, 'Click me'); } { text(1, 'Click me'); }
elementEnd(); elementEnd();
} }
@ -213,12 +244,14 @@ describe('elementProperty', () => {
} }
const ctx: any = {isDisabled: true}; const ctx: any = {isDisabled: true};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`); expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdisableddir="">Click me</button>`);
expect(button !.disabled).toEqual(true); expect(button !.disabled).toEqual(true);
expect(otherDisabledDir !.disabled).toEqual(true); expect(otherDisabledDir !.disabled).toEqual(true);
ctx.isDisabled = false; ctx.isDisabled = false;
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`); expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdisableddir="">Click me</button>`);
expect(button !.disabled).toEqual(false); expect(button !.disabled).toEqual(false);
expect(otherDisabledDir !.disabled).toEqual(false); expect(otherDisabledDir !.disabled).toEqual(false);
}); });
@ -227,7 +260,7 @@ describe('elementProperty', () => {
/** <button otherDir [id]="id" (click)="onClick()">Click me</button> */ /** <button otherDir [id]="id" (click)="onClick()">Click me</button> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [OtherDir]); elementStart(0, 'button', ['otherDir', '']);
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', ctx.onClick.bind(ctx));
text(1, 'Click me'); text(1, 'Click me');
@ -239,27 +272,18 @@ describe('elementProperty', () => {
let counter = 0; let counter = 0;
const ctx: any = {id: 1, onClick: () => counter++}; const ctx: any = {id: 1, onClick: () => counter++};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`); expect(renderToHtml(Template, ctx, deps)).toEqual(`<button otherdir="">Click me</button>`);
expect(otherDir !.id).toEqual(1); expect(otherDir !.id).toEqual(1);
otherDir !.clickStream.next(); otherDir !.clickStream.next();
expect(counter).toEqual(1); expect(counter).toEqual(1);
ctx.id = 2; ctx.id = 2;
renderToHtml(Template, ctx); renderToHtml(Template, ctx, deps);
expect(otherDir !.id).toEqual(2); expect(otherDir !.id).toEqual(2);
}); });
it('should support unrelated element properties at same index in if-else block', () => { 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'}});
}
/** /**
* <button idDir [id]="id1">Click me</button> // inputs: {'id': [0, 'idNumber']} * <button idDir [id]="id1">Click me</button> // inputs: {'id': [0, 'idNumber']}
* % if (condition) { * % if (condition) {
@ -270,7 +294,7 @@ describe('elementProperty', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [IdDir]); elementStart(0, 'button', ['idDir', '']);
{ text(1, 'Click me'); } { text(1, 'Click me'); }
elementEnd(); elementEnd();
container(2); container(2);
@ -288,7 +312,7 @@ describe('elementProperty', () => {
embeddedViewEnd(); embeddedViewEnd();
} else { } else {
if (embeddedViewStart(1)) { if (embeddedViewStart(1)) {
elementStart(0, 'button', null, [OtherDir]); elementStart(0, 'button', ['otherDir', '']);
{ text(1, 'Click me too'); } { text(1, 'Click me too'); }
elementEnd(); elementEnd();
} }
@ -299,12 +323,13 @@ describe('elementProperty', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'})) expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'}, deps))
.toEqual(`<button>Click me</button><button id="two">Click me too</button>`); .toEqual(`<button iddir="">Click me</button><button id="two">Click me too</button>`);
expect(idDir !.idNumber).toEqual('one'); expect(idDir !.idNumber).toEqual('one');
expect(renderToHtml(Template, {condition: false, id1: 'four', id2: 'two', id3: 'three'})) expect(
.toEqual(`<button>Click me</button><button>Click me too</button>`); renderToHtml(Template, {condition: false, id1: 'four', id2: 'two', id3: 'three'}, deps))
.toEqual(`<button iddir="">Click me</button><button otherdir="">Click me too</button>`);
expect(idDir !.idNumber).toEqual('four'); expect(idDir !.idNumber).toEqual('four');
expect(otherDir !.id).toEqual('three'); expect(otherDir !.id).toEqual('three');
}); });
@ -320,6 +345,7 @@ describe('elementProperty', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: MyDir, type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => myDir = new MyDir(), factory: () => myDir = new MyDir(),
inputs: {role: 'role', direction: 'dir'}, inputs: {role: 'role', direction: 'dir'},
outputs: {changeStream: 'change'}, outputs: {changeStream: 'change'},
@ -331,21 +357,27 @@ describe('elementProperty', () => {
class MyDirB { class MyDirB {
roleB: string; roleB: string;
static ngDirectiveDef = defineDirective( static ngDirectiveDef = defineDirective({
{type: MyDirB, factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}}); 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', () => { it('should set input property based on attribute if existing', () => {
/** <div role="button" myDir></div> */ /** <div role="button" myDir></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]); elementStart(0, 'div', ['role', 'button', 'myDir', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})).toEqual(`<div role="button"></div>`); expect(renderToHtml(Template, {}, deps)).toEqual(`<div mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
}); });
@ -354,16 +386,17 @@ describe('elementProperty', () => {
/** <div role="button" [role]="role" myDir></div> */ /** <div role="button" [role]="role" myDir></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]); elementStart(0, 'div', ['role', 'button', 'myDir', '']);
elementEnd(); elementEnd();
} }
elementProperty(0, 'role', bind(ctx.role)); elementProperty(0, 'role', bind(ctx.role));
} }
expect(renderToHtml(Template, {role: 'listbox'})).toEqual(`<div role="button"></div>`); expect(renderToHtml(Template, {role: 'listbox'}, deps))
.toEqual(`<div mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('listbox'); expect(myDir !.role).toEqual('listbox');
renderToHtml(Template, {role: 'button'}); renderToHtml(Template, {role: 'button'}, deps);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
}); });
@ -372,12 +405,13 @@ describe('elementProperty', () => {
/** <div role="button" myDir myDirB></div> */ /** <div role="button" myDir myDirB></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir, MyDirB]); elementStart(0, 'div', ['role', 'button', 'myDir', '', 'myDirB', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})).toEqual(`<div role="button"></div>`); expect(renderToHtml(Template, {}, deps))
.toEqual(`<div mydir="" mydirb="" role="button"></div>`);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
expect(dirB !.roleB).toEqual('button'); expect(dirB !.roleB).toEqual('button');
}); });
@ -387,12 +421,13 @@ describe('elementProperty', () => {
/** <div role="button" dir="rtl" myDir></div> */ /** <div role="button" dir="rtl" myDir></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]); elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})).toEqual(`<div dir="rtl" role="button"></div>`); expect(renderToHtml(Template, {}, deps))
.toEqual(`<div dir="rtl" mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
expect(myDir !.direction).toEqual('rtl'); expect(myDir !.direction).toEqual('rtl');
}); });
@ -402,16 +437,15 @@ describe('elementProperty', () => {
/** <div role="button" (change)="onChange()" myDir></div> */ /** <div role="button" (change)="onChange()" myDir></div> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]); elementStart(0, 'div', ['role', 'button', 'myDir', '']);
{ listener('change', ctx.onChange.bind(ctx)); } { listener('change', ctx.onChange.bind(ctx)); }
elementEnd(); elementEnd();
} }
} }
let counter = 0; let counter = 0;
expect(renderToHtml(Template, { expect(renderToHtml(Template, {onChange: () => counter++}, deps))
onChange: () => counter++ .toEqual(`<div mydir="" role="button"></div>`);
})).toEqual(`<div role="button"></div>`);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
myDir !.changeStream.next(); myDir !.changeStream.next();
@ -426,15 +460,16 @@ describe('elementProperty', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]); elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']);
elementEnd(); elementEnd();
elementStart(1, 'div', ['role', 'listbox'], [MyDirB]); elementStart(1, 'div', ['role', 'listbox', 'myDirB', '']);
elementEnd(); elementEnd();
} }
} }
expect(renderToHtml(Template, {})) expect(renderToHtml(Template, {}, deps))
.toEqual(`<div dir="rtl" role="button"></div><div role="listbox"></div>`); .toEqual(
`<div dir="rtl" mydir="" role="button"></div><div mydirb="" role="listbox"></div>`);
expect(myDir !.role).toEqual('button'); expect(myDir !.role).toEqual('button');
expect(myDir !.direction).toEqual('rtl'); expect(myDir !.direction).toEqual('rtl');
expect(dirB !.roleB).toEqual('listbox'); expect(dirB !.roleB).toEqual('listbox');
@ -452,7 +487,7 @@ describe('elementProperty', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'listbox'], [MyDir]); elementStart(0, 'div', ['role', 'listbox', 'myDir', '']);
elementEnd(); elementEnd();
container(1); container(1);
} }
@ -460,7 +495,7 @@ describe('elementProperty', () => {
{ {
if (ctx.condition) { if (ctx.condition) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'div', ['role', 'button'], [MyDirB]); elementStart(0, 'div', ['role', 'button', 'myDirB', '']);
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();
@ -476,16 +511,14 @@ describe('elementProperty', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
expect(renderToHtml(Template, { expect(renderToHtml(Template, {condition: true}, deps))
condition: true .toEqual(`<div mydir="" role="listbox"></div><div mydirb="" role="button"></div>`);
})).toEqual(`<div role="listbox"></div><div role="button"></div>`);
expect(myDir !.role).toEqual('listbox'); expect(myDir !.role).toEqual('listbox');
expect(dirB !.roleB).toEqual('button'); expect(dirB !.roleB).toEqual('button');
expect((dirB !as any).role).toBeUndefined(); expect((dirB !as any).role).toBeUndefined();
expect(renderToHtml(Template, { expect(renderToHtml(Template, {condition: false}, deps))
condition: false .toEqual(`<div mydir="" role="listbox"></div><div role="menu"></div>`);
})).toEqual(`<div role="listbox"></div><div role="menu"></div>`);
expect(myDir !.role).toEqual('listbox'); expect(myDir !.role).toEqual('listbox');
}); });
@ -494,18 +527,19 @@ describe('elementProperty', () => {
class Comp { class Comp {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: Comp, type: Comp,
tag: 'comp', selector: [[['comp'], null]],
/** <div role="button" dir #dir="myDir"></div> {{ dir.role }} */ /** <div role="button" dir #dir="myDir"></div> {{ dir.role }} */
template: function(ctx: any, cm: boolean) { template: function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir], ['dir', 'myDir']); elementStart(0, 'div', ['role', 'button', 'myDir', ''], ['dir', 'myDir']);
elementEnd(); elementEnd();
text(1); text(1);
} }
// TODO: remove this loadDirective when removing MyDir // TODO: remove this loadDirective when removing MyDir
textBinding(1, bind(loadDirective<MyDir>(0).role)); textBinding(1, bind(loadDirective<MyDir>(0).role));
}, },
factory: () => new Comp() factory: () => new Comp(),
directiveDefs: () => [MyDir.ngDirectiveDef]
}); });
} }
@ -522,7 +556,7 @@ describe('elementProperty', () => {
{ {
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, Comp); elementStart(0, 'comp');
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();
@ -531,9 +565,9 @@ describe('elementProperty', () => {
containerRefreshEnd(); containerRefreshEnd();
} }
expect(renderToHtml(Template, {})) expect(renderToHtml(Template, {}, [Comp.ngComponentDef]))
.toEqual( .toEqual(
`<comp><div role="button"></div>button</comp><comp><div role="button"></div>button</comp>`); `<comp><div mydir="" role="button"></div>button</comp><comp><div mydir="" role="button"></div>button</comp>`);
}); });
}); });

View File

@ -18,34 +18,36 @@ describe('array literals', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return myComp = new MyComp(); }, factory: function MyComp_Factory() { return myComp = new MyComp(); },
template: function MyComp_Template(ctx: MyComp, cm: boolean) {}, template: function MyComp_Template(ctx: MyComp, cm: boolean) {},
inputs: {names: 'names'} inputs: {names: 'names'}
}); });
} }
const defs = [MyComp.ngComponentDef];
it('should support an array literal with a binding', () => { it('should support an array literal with a binding', () => {
const e0_ff = (v: any) => ['Nancy', v, 'Bess']; const e0_ff = (v: any) => ['Nancy', v, 'Bess'];
/** <my-comp [names]="['Nancy', customName, 'Bess']"></my-comp> */ /** <my-comp [names]="['Nancy', customName, 'Bess']"></my-comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'my-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'names', bind(pureFunction1(e0_ff, ctx.customName))); elementProperty(0, 'names', bind(pureFunction1(e0_ff, ctx.customName)));
} }
renderToHtml(Template, {customName: 'Carson'}); renderToHtml(Template, {customName: 'Carson'}, defs);
const firstArray = myComp !.names; const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']); expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']);
renderToHtml(Template, {customName: 'Carson'}); renderToHtml(Template, {customName: 'Carson'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']); expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']);
expect(firstArray).toBe(myComp !.names); expect(firstArray).toBe(myComp !.names);
renderToHtml(Template, {customName: 'Hannah'}); renderToHtml(Template, {customName: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Hannah', 'Bess']); expect(myComp !.names).toEqual(['Nancy', 'Hannah', 'Bess']);
// Identity must change if binding changes // 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 // The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten. // setting the property to ensure it's not overwritten.
myComp !.names = ['should not be overwritten']; myComp !.names = ['should not be overwritten'];
renderToHtml(Template, {customName: 'Hannah'}); renderToHtml(Template, {customName: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['should not be overwritten']); expect(myComp !.names).toEqual(['should not be overwritten']);
}); });
@ -67,7 +69,7 @@ describe('array literals', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ManyPropComp, type: ManyPropComp,
tag: 'many-prop-comp', selector: [[['many-prop-comp'], null]],
factory: function ManyPropComp_Factory() { return manyPropComp = new ManyPropComp(); }, factory: function ManyPropComp_Factory() { return manyPropComp = new ManyPropComp(); },
template: function ManyPropComp_Template(ctx: ManyPropComp, cm: boolean) {}, template: function ManyPropComp_Template(ctx: ManyPropComp, cm: boolean) {},
inputs: {names1: 'names1', names2: 'names2'} inputs: {names1: 'names1', names2: 'names2'}
@ -83,18 +85,19 @@ describe('array literals', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ManyPropComp); elementStart(0, 'many-prop-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'names1', bind(pureFunction1(e0_ff, ctx.customName))); elementProperty(0, 'names1', bind(pureFunction1(e0_ff, ctx.customName)));
elementProperty(0, 'names2', bind(pureFunction1(e0_ff_1, ctx.customName2))); 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 !.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp !.names2).toEqual(['George']); 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 !.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp !.names2).toEqual(['Carson']); expect(manyPropComp !.names2).toEqual(['Carson']);
}); });
@ -115,36 +118,37 @@ describe('array literals', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ParentComp, type: ParentComp,
tag: 'parent-comp', selector: [[['parent-comp'], null]],
factory: () => new ParentComp(), factory: () => new ParentComp(),
template: function(ctx: any, cm: boolean) { template: function(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'my-comp');
myComps.push(loadDirective(0)); myComps.push(loadDirective(0));
elementEnd(); elementEnd();
} }
elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(e0_ff, ctx.customName)))); elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(e0_ff, ctx.customName))));
} },
directiveDefs: defs
}); });
} }
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ParentComp); elementStart(0, 'parent-comp');
elementEnd(); elementEnd();
elementStart(1, ParentComp); elementStart(1, 'parent-comp');
elementEnd(); elementEnd();
} }
} }
renderToHtml(Template, {}); renderToHtml(Template, {}, [ParentComp.ngComponentDef]);
const firstArray = myComps[0].names; const firstArray = myComps[0].names;
const secondArray = myComps[1].names; const secondArray = myComps[1].names;
expect(firstArray).toEqual(['NANCY', 'Bess']); expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']); expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).not.toBe(secondArray); expect(firstArray).not.toBe(secondArray);
renderToHtml(Template, {}); renderToHtml(Template, {}, [ParentComp.ngComponentDef]);
expect(firstArray).toEqual(['NANCY', 'Bess']); expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']); expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).toBe(myComps[0].names); expect(firstArray).toBe(myComps[0].names);
@ -157,31 +161,31 @@ describe('array literals', () => {
/** <my-comp [names]="['Nancy', customName, 'Bess', customName2]"></my-comp> */ /** <my-comp [names]="['Nancy', customName, 'Bess', customName2]"></my-comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'my-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'names', bind(pureFunction2(e0_ff, ctx.customName, ctx.customName2))); 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; const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']); 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(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
expect(firstArray).toBe(myComp !.names); 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(myComp !.names).toEqual(['Nancy', 'George', 'Bess', 'Hannah']);
expect(firstArray).not.toBe(myComp !.names); 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']); expect(myComp !.names).toEqual(['Nancy', 'Frank', 'Bess', 'Ned']);
// The property should not be set if the exp value is the same, so artificially // The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten. // setting the property to ensure it's not overwritten.
myComp !.names = ['should not be 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']); expect(myComp !.names).toEqual(['should not be overwritten']);
}); });
@ -210,22 +214,22 @@ describe('array literals', () => {
function Template(c: any, cm: boolean) { function Template(c: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'my-comp');
f3Comp = loadDirective(0); f3Comp = loadDirective(0);
elementEnd(); elementEnd();
elementStart(1, MyComp); elementStart(1, 'my-comp');
f4Comp = loadDirective(1); f4Comp = loadDirective(1);
elementEnd(); elementEnd();
elementStart(2, MyComp); elementStart(2, 'my-comp');
f5Comp = loadDirective(2); f5Comp = loadDirective(2);
elementEnd(); elementEnd();
elementStart(3, MyComp); elementStart(3, 'my-comp');
f6Comp = loadDirective(3); f6Comp = loadDirective(3);
elementEnd(); elementEnd();
elementStart(4, MyComp); elementStart(4, 'my-comp');
f7Comp = loadDirective(4); f7Comp = loadDirective(4);
elementEnd(); elementEnd();
elementStart(5, MyComp); elementStart(5, 'my-comp');
f8Comp = loadDirective(5); f8Comp = loadDirective(5);
elementEnd(); 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]))); 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(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f4Comp !.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']); 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(f7Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f8Comp !.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(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h1']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', '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']); 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(f7Comp !.names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(f8Comp !.names).toEqual(['a1', '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(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h2']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', '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']); 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']; v8: any) => ['start', v0, v1, v2, v3, v4, v5, v6, v7, v8, 'end'];
const e0_ff_1 = (v: any) => { return {name: v}; }; 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);
/** /**
* <my-comp [names]="['start', v0, v1, v2, v3, {name: v4}, v5, v6, v7, v8, 'end']"> * <my-comp [names]="['start', v0, v1, v2, v3, {name: v4}, v5, v6, v7, v8, 'end']">
* </my-comp> * </my-comp>
*/ */
function Template(c: any, cm: boolean) { function Template(c: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, MyComp); elementStart(0, 'my-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'names', bind(pureFunctionV(e0_ff, [ 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' '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([ expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', {name: 'e'}, 'f', 'g', 'h', 'i', 'end' '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([ expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', {name: 'e5'}, 'f', 'g', 'h', 'i', 'end' 'start', 'a1', 'b', 'c', 'd', {name: 'e5'}, 'f', 'g', 'h', 'i', 'end'
]); ]);
@ -309,34 +313,36 @@ describe('object literals', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: ObjectComp, type: ObjectComp,
tag: 'object-comp', selector: [[['object-comp'], null]],
factory: function ObjectComp_Factory() { return objectComp = new ObjectComp(); }, factory: function ObjectComp_Factory() { return objectComp = new ObjectComp(); },
template: function ObjectComp_Template(ctx: ObjectComp, cm: boolean) {}, template: function ObjectComp_Template(ctx: ObjectComp, cm: boolean) {},
inputs: {config: 'config'} inputs: {config: 'config'}
}); });
} }
const defs = [ObjectComp.ngComponentDef];
it('should support an object literal', () => { it('should support an object literal', () => {
const e0_ff = (v: any) => { return {duration: 500, animation: v}; }; const e0_ff = (v: any) => { return {duration: 500, animation: v}; };
/** <object-comp [config]="{duration: 500, animation: name}"></object-comp> */ /** <object-comp [config]="{duration: 500, animation: name}"></object-comp> */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ObjectComp); elementStart(0, 'object-comp');
elementEnd(); elementEnd();
} }
elementProperty(0, 'config', bind(pureFunction1(e0_ff, ctx.name))); elementProperty(0, 'config', bind(pureFunction1(e0_ff, ctx.name)));
} }
renderToHtml(Template, {name: 'slide'}); renderToHtml(Template, {name: 'slide'}, defs);
const firstObj = objectComp !.config; const firstObj = objectComp !.config;
expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'}); 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(objectComp !.config).toEqual({duration: 500, animation: 'slide'});
expect(firstObj).toBe(objectComp !.config); expect(firstObj).toBe(objectComp !.config);
renderToHtml(Template, {name: 'tap'}); renderToHtml(Template, {name: 'tap'}, defs);
expect(objectComp !.config).toEqual({duration: 500, animation: 'tap'}); expect(objectComp !.config).toEqual({duration: 500, animation: 'tap'});
// Identity must change if binding changes // Identity must change if binding changes
@ -355,7 +361,7 @@ describe('object literals', () => {
*/ */
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ObjectComp); elementStart(0, 'object-comp');
elementEnd(); elementEnd();
} }
elementProperty( elementProperty(
@ -364,34 +370,34 @@ describe('object literals', () => {
e0_ff, ctx.name, pureFunction1(e0_ff_1, pureFunction1(e0_ff_2, ctx.duration))))); 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({ expect(objectComp !.config).toEqual({
animation: 'slide', animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}] actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
}); });
const firstConfig = objectComp !.config; const firstConfig = objectComp !.config;
renderToHtml(Template, {name: 'slide', duration: 100}); renderToHtml(Template, {name: 'slide', duration: 100}, defs);
expect(objectComp !.config).toEqual({ expect(objectComp !.config).toEqual({
animation: 'slide', animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}] actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
}); });
expect(objectComp !.config).toBe(firstConfig); expect(objectComp !.config).toBe(firstConfig);
renderToHtml(Template, {name: 'slide', duration: 50}); renderToHtml(Template, {name: 'slide', duration: 50}, defs);
expect(objectComp !.config).toEqual({ expect(objectComp !.config).toEqual({
animation: 'slide', animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}] actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
}); });
expect(objectComp !.config).not.toBe(firstConfig); expect(objectComp !.config).not.toBe(firstConfig);
renderToHtml(Template, {name: 'tap', duration: 50}); renderToHtml(Template, {name: 'tap', duration: 50}, defs);
expect(objectComp !.config).toEqual({ expect(objectComp !.config).toEqual({
animation: 'tap', animation: 'tap',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}] 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({ expect(objectComp !.config).toEqual({
animation: 'drag', animation: 'drag',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 500}] 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 // The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten. // setting the property to ensure it's not overwritten.
objectComp !.config = ['should not be 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']); expect(objectComp !.config).toEqual(['should not be overwritten']);
}); });
@ -421,7 +427,7 @@ describe('object literals', () => {
{ {
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ObjectComp); elementStart(0, 'object-comp');
objectComps.push(loadDirective(0)); objectComps.push(loadDirective(0));
elementEnd(); elementEnd();
} }
@ -437,12 +443,12 @@ describe('object literals', () => {
const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; }; const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; };
const configs = [{opacity: 0, duration: 500}, {opacity: 1, duration: 600}]; 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[0].config).toEqual({opacity: 0, duration: 500});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
configs[0].duration = 1000; configs[0].duration = 1000;
renderToHtml(Template, {configs}); renderToHtml(Template, {configs}, defs);
expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000}); expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
}); });

View File

@ -62,10 +62,10 @@ describe('query', () => {
if (cm) { if (cm) {
query(0, Child, false); query(0, Child, false);
query(1, Child, true); query(1, Child, true);
elementStart(2, Child); elementStart(2, 'child');
{ {
child1 = loadDirective(0); child1 = loadDirective(0);
elementStart(3, Child); elementStart(3, 'child');
{ child2 = loadDirective(1); } { child2 = loadDirective(1); }
elementEnd(); elementEnd();
} }
@ -73,7 +73,7 @@ describe('query', () => {
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query0 = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query0 = tmp as QueryList<any>);
queryRefresh(tmp = load<QueryList<any>>(1)) && (ctx.query1 = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(1)) && (ctx.query1 = tmp as QueryList<any>);
}); }, [Child.ngComponentDef]);
const parent = renderComponent(Cmp); const parent = renderComponent(Cmp);
expect((parent.query0 as QueryList<any>).toArray()).toEqual([child1]); expect((parent.query0 as QueryList<any>).toArray()).toEqual([child1]);
@ -83,7 +83,7 @@ describe('query', () => {
describe('types predicate', () => { describe('types predicate', () => {
it('should query using type predicate and read a specified token', () => { it('should query using type predicate and read a specified token', () => {
const Child = createDirective(); const Child = createDirective('child');
let elToQuery; let elToQuery;
/** /**
* <div child></div> * <div child></div>
@ -95,11 +95,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, Child, false, QUERY_READ_ELEMENT_REF); query(0, Child, false, QUERY_READ_ELEMENT_REF);
elToQuery = elementStart(1, 'div', null, [Child]); elToQuery = elementStart(1, 'div', ['child', '']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -110,8 +110,8 @@ describe('query', () => {
it('should query using type predicate and read another directive type', () => { it('should query using type predicate and read another directive type', () => {
const Child = createDirective(); const Child = createDirective('child');
const OtherChild = createDirective(); const OtherChild = createDirective('otherChild');
let otherChildInstance; let otherChildInstance;
/** /**
* <div child otherChild></div> * <div child otherChild></div>
@ -123,12 +123,12 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, Child, false, OtherChild); query(0, Child, false, OtherChild);
elementStart(1, 'div', null, [Child, OtherChild]); elementStart(1, 'div', ['child', '', 'otherChild', '']);
{ otherChildInstance = loadDirective(1); } { otherChildInstance = loadDirective(1); }
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef, OtherChild.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -137,8 +137,8 @@ describe('query', () => {
}); });
it('should not add results to query if a requested token cant be read', () => { it('should not add results to query if a requested token cant be read', () => {
const Child = createDirective(); const Child = createDirective('child');
const OtherChild = createDirective(); const OtherChild = createDirective('otherChild');
/** /**
* <div child></div> * <div child></div>
* class Cmpt { * class Cmpt {
@ -149,11 +149,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, Child, false, OtherChild); query(0, Child, false, OtherChild);
elementStart(1, 'div', null, [Child]); elementStart(1, 'div', ['child', '']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef, OtherChild.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -177,7 +177,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_FROM_NODE); query(0, ['foo'], false, QUERY_READ_FROM_NODE);
elToQuery = elementStart(1, 'div', null, null, ['foo', '']); elToQuery = elementStart(1, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
elementStart(2, 'div'); elementStart(2, 'div');
elementEnd(); elementEnd();
@ -207,7 +207,7 @@ describe('query', () => {
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_FROM_NODE); query(0, ['foo'], false, QUERY_READ_FROM_NODE);
query(1, ['bar'], 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(); elementEnd();
elementStart(3, 'div'); elementStart(3, 'div');
elementEnd(); elementEnd();
@ -243,11 +243,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE);
el1ToQuery = elementStart(1, 'div', null, null, ['foo', '']); el1ToQuery = elementStart(1, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
elementStart(2, 'div'); elementStart(2, 'div');
elementEnd(); elementEnd();
el2ToQuery = elementStart(3, 'div', null, null, ['bar', '']); el2ToQuery = elementStart(3, 'div', null, ['bar', '']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
@ -274,7 +274,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); query(0, ['foo'], false, QUERY_READ_ELEMENT_REF);
elToQuery = elementStart(1, 'div', null, null, ['foo', '']); elToQuery = elementStart(1, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
elementStart(2, 'div'); elementStart(2, 'div');
elementEnd(); elementEnd();
@ -300,7 +300,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); query(0, ['foo'], false, QUERY_READ_CONTAINER_REF);
elementStart(1, 'div', null, null, ['foo', '']); elementStart(1, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
@ -323,7 +323,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); query(0, ['foo'], false, QUERY_READ_CONTAINER_REF);
container(1, undefined, undefined, undefined, undefined, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); });
@ -346,7 +346,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); query(0, ['foo'], false, QUERY_READ_ELEMENT_REF);
container(1, undefined, undefined, undefined, undefined, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); });
@ -369,7 +369,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], undefined, QUERY_READ_FROM_NODE); query(0, ['foo'], undefined, QUERY_READ_FROM_NODE);
container(1, undefined, undefined, undefined, undefined, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); });
@ -392,7 +392,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF); query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF);
container(1, undefined, undefined, undefined, undefined, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); });
@ -417,12 +417,12 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
elementStart(1, Child, null, null, ['foo', '']); elementStart(1, 'child', null, ['foo', '']);
{ childInstance = loadDirective(0); } { childInstance = loadDirective(0); }
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngComponentDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -436,7 +436,7 @@ describe('query', () => {
class Child { class Child {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: Child, type: Child,
tag: 'child', selector: [[['child'], null]],
factory: () => childInstance = new Child(), factory: () => childInstance = new Child(),
template: (ctx: Child, cm: boolean) => {}, template: (ctx: Child, cm: boolean) => {},
exportAs: 'child' exportAs: 'child'
@ -453,11 +453,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
elementStart(1, Child, null, null, ['foo', 'child']); elementStart(1, 'child', null, ['foo', 'child']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngComponentDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -467,7 +467,7 @@ describe('query', () => {
it('should read directive instance if element queried for has an exported directive with a matching name', 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; let childInstance;
/** /**
@ -480,12 +480,12 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
elementStart(1, 'div', null, [Child], ['foo', 'child']); elementStart(1, 'div', ['child', ''], ['foo', 'child']);
childInstance = loadDirective(0); childInstance = loadDirective(0);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -494,8 +494,8 @@ describe('query', () => {
}); });
it('should read all matching directive instances from a given element', () => { it('should read all matching directive instances from a given element', () => {
const Child1 = createDirective({exportAs: 'child1'}); const Child1 = createDirective('child1', {exportAs: 'child1'});
const Child2 = createDirective({exportAs: 'child2'}); const Child2 = createDirective('child2', {exportAs: 'child2'});
let child1Instance, child2Instance; let child1Instance, child2Instance;
/** /**
@ -508,7 +508,7 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE); 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); child1Instance = loadDirective(0);
child2Instance = loadDirective(1); child2Instance = loadDirective(1);
@ -516,7 +516,7 @@ describe('query', () => {
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child1.ngDirectiveDef, Child2.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -526,7 +526,7 @@ describe('query', () => {
}); });
it('should read multiple locals exporting the same directive from a given element', () => { 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; let childInstance;
/** /**
@ -541,13 +541,13 @@ describe('query', () => {
if (cm) { if (cm) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
query(1, ['bar'], 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); } { childInstance = loadDirective(0); }
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.fooQuery = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.fooQuery = tmp as QueryList<any>);
queryRefresh(tmp = load<QueryList<any>>(1)) && (ctx.barQuery = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(1)) && (ctx.barQuery = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
@ -561,7 +561,7 @@ describe('query', () => {
}); });
it('should match on exported directive name and read a requested token', () => { 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; let div;
/** /**
@ -574,11 +574,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF); query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF);
div = elementStart(1, 'div', null, [Child], ['foo', 'child']); div = elementStart(1, 'div', ['child', ''], ['foo', 'child']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -587,7 +587,7 @@ describe('query', () => {
}); });
it('should support reading a mix of ElementRef and directive instances', () => { 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; let childInstance, div;
/** /**
@ -600,12 +600,12 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); 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); } { childInstance = loadDirective(0); }
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -615,7 +615,7 @@ describe('query', () => {
}); });
it('should not add results to query if a requested token cant be read', () => { it('should not add results to query if a requested token cant be read', () => {
const Child = createDirective(); const Child = createDirective('child');
/** /**
* <div #foo></div> * <div #foo></div>
@ -627,11 +627,11 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], false, Child); query(0, ['foo'], false, Child);
elementStart(1, 'div', null, null, ['foo', '']); elementStart(1, 'div', ['foo', '']);
elementEnd(); elementEnd();
} }
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>); queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}); }, [Child.ngDirectiveDef]);
const cmptInstance = renderComponent(Cmpt); const cmptInstance = renderComponent(Cmpt);
const qList = (cmptInstance.query as QueryList<any>); const qList = (cmptInstance.query as QueryList<any>);
@ -664,7 +664,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(1); let cm1 = embeddedViewStart(1);
{ {
if (cm1) { if (cm1) {
firstEl = elementStart(0, 'div', null, null, ['foo', '']); firstEl = elementStart(0, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }
@ -706,10 +706,10 @@ describe('query', () => {
let tmp: any; let tmp: any;
if (cm) { if (cm) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
firstEl = elementStart(1, 'b', null, null, ['foo', '']); firstEl = elementStart(1, 'span', null, ['foo', '']);
elementEnd(); elementEnd();
container(2); container(2);
lastEl = elementStart(3, 'i', null, null, ['foo', '']); lastEl = elementStart(3, 'span', null, ['foo', '']);
elementEnd(); elementEnd();
} }
containerRefreshStart(2); containerRefreshStart(2);
@ -718,7 +718,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(1); let cm1 = embeddedViewStart(1);
{ {
if (cm1) { if (cm1) {
viewEl = elementStart(0, 'div', null, null, ['foo', '']); viewEl = elementStart(0, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }
@ -774,7 +774,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(0); let cm1 = embeddedViewStart(0);
{ {
if (cm1) { if (cm1) {
firstEl = elementStart(0, 'div', null, null, ['foo', '']); firstEl = elementStart(0, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }
@ -784,7 +784,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(1); let cm1 = embeddedViewStart(1);
{ {
if (cm1) { if (cm1) {
lastEl = elementStart(0, 'span', null, null, ['foo', '']); lastEl = elementStart(0, 'span', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }
@ -836,7 +836,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(0); let cm1 = embeddedViewStart(0);
{ {
if (cm1) { if (cm1) {
firstEl = elementStart(0, 'div', null, null, ['foo', '']); firstEl = elementStart(0, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
container(1); container(1);
} }
@ -846,7 +846,7 @@ describe('query', () => {
let cm2 = embeddedViewStart(0); let cm2 = embeddedViewStart(0);
{ {
if (cm2) { if (cm2) {
lastEl = elementStart(0, 'span', null, null, ['foo', '']); lastEl = elementStart(0, 'span', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }
@ -894,7 +894,7 @@ describe('query', () => {
query(0, ['foo'], true, QUERY_READ_FROM_NODE); query(0, ['foo'], true, QUERY_READ_FROM_NODE);
query(1, ['foo'], false, QUERY_READ_FROM_NODE); query(1, ['foo'], false, QUERY_READ_FROM_NODE);
container(2); container(2);
elementStart(3, 'span', null, null, ['foo', '']); elementStart(3, 'span', null, ['foo', '']);
elementEnd(); elementEnd();
} }
containerRefreshStart(2); containerRefreshStart(2);
@ -903,7 +903,7 @@ describe('query', () => {
let cm1 = embeddedViewStart(0); let cm1 = embeddedViewStart(0);
{ {
if (cm1) { if (cm1) {
elementStart(0, 'div', null, null, ['foo', '']); elementStart(0, 'div', null, ['foo', '']);
elementEnd(); elementEnd();
} }
} }

View File

@ -9,7 +9,7 @@
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {CreateComponentOptions} from '../../src/render3/component'; 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 {NG_HOST_SYMBOL, renderTemplate} from '../../src/render3/instructions';
import {LElementNode} from '../../src/render3/interfaces/node'; import {LElementNode} from '../../src/render3/interfaces/node';
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
@ -151,9 +151,10 @@ export function resetDOM() {
* @deprecated use `TemplateFixture` or `ComponentFixture` * @deprecated use `TemplateFixture` or `ComponentFixture`
*/ */
export function renderToHtml( export function renderToHtml(
template: ComponentTemplate<any>, ctx: any, providedRendererFactory?: RendererFactory3) { template: ComponentTemplate<any>, ctx: any, defs?: any[],
providedRendererFactory?: RendererFactory3 | null) {
host = renderTemplate( host = renderTemplate(
containerEl, template, ctx, providedRendererFactory || testRendererFactory, host); containerEl, template, ctx, providedRendererFactory || testRendererFactory, host, defs);
return toHtml(containerEl); return toHtml(containerEl);
} }
@ -188,23 +189,27 @@ export function toHtml<T>(componentOrElement: T | RElement): string {
} }
export function createComponent( export function createComponent(
name: string, template: ComponentTemplate<any>): ComponentType<any> { name: string, template: ComponentTemplate<any>,
defs: (ComponentDef<any>| DirectiveDef<any>)[] = []): ComponentType<any> {
return class Component { return class Component {
value: any; value: any;
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: Component, type: Component,
tag: name, selector: [[[name], null]],
factory: () => new Component, factory: () => new Component,
template: template, template: template,
features: [PublicFeature] features: [PublicFeature],
directiveDefs: () => defs
}); });
}; };
} }
export function createDirective({exportAs}: {exportAs?: string} = {}): DirectiveType<any> { export function createDirective(
name: string, {exportAs}: {exportAs?: string} = {}): DirectiveType<any> {
return class Directive { return class Directive {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selector: [[['', name, ''], null]],
factory: () => new Directive(), factory: () => new Directive(),
features: [PublicFeature], features: [PublicFeature],
exportAs: exportAs, exportAs: exportAs,

View File

@ -31,7 +31,7 @@ describe('renderer factory lifecycle', () => {
class SomeComponent { class SomeComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: SomeComponent, type: SomeComponent,
tag: 'some-component', selector: [[['some-component'], null]],
template: function(ctx: SomeComponent, cm: boolean) { template: function(ctx: SomeComponent, cm: boolean) {
logs.push('component'); logs.push('component');
if (cm) { if (cm) {
@ -45,7 +45,7 @@ describe('renderer factory lifecycle', () => {
class SomeComponentWhichThrows { class SomeComponentWhichThrows {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: SomeComponentWhichThrows, type: SomeComponentWhichThrows,
tag: 'some-component-with-Error', selector: [[['some-component-with-Error'], null]],
template: function(ctx: SomeComponentWhichThrows, cm: boolean) { template: function(ctx: SomeComponentWhichThrows, cm: boolean) {
throw(new Error('SomeComponentWhichThrows threw')); 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) { function TemplateWithComponent(ctx: any, cm: boolean) {
logs.push('function_with_component'); logs.push('function_with_component');
if (cm) { if (cm) {
text(0, 'bar'); text(0, 'bar');
elementStart(1, SomeComponent); elementStart(1, 'some-component');
elementEnd(); elementEnd();
} }
} }
@ -86,7 +88,7 @@ describe('renderer factory lifecycle', () => {
}); });
it('should work with a template', () => { it('should work with a template', () => {
renderToHtml(Template, {}, rendererFactory); renderToHtml(Template, {}, [], rendererFactory);
expect(logs).toEqual(['create', 'begin', 'function', 'end']); expect(logs).toEqual(['create', 'begin', 'function', 'end']);
logs = []; logs = [];
@ -95,12 +97,12 @@ describe('renderer factory lifecycle', () => {
}); });
it('should work with a template which contains a component', () => { it('should work with a template which contains a component', () => {
renderToHtml(TemplateWithComponent, {}, rendererFactory); renderToHtml(TemplateWithComponent, {}, defs, rendererFactory);
expect(logs).toEqual( expect(logs).toEqual(
['create', 'begin', 'function_with_component', 'create', 'component', 'end']); ['create', 'begin', 'function_with_component', 'create', 'component', 'end']);
logs = []; logs = [];
renderToHtml(TemplateWithComponent, {}); renderToHtml(TemplateWithComponent, {}, defs);
expect(logs).toEqual(['begin', 'function_with_component', 'component', 'end']); expect(logs).toEqual(['begin', 'function_with_component', 'component', 'end']);
}); });
@ -122,7 +124,7 @@ describe('animation renderer factory', () => {
class SomeComponent { class SomeComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: SomeComponent, type: SomeComponent,
tag: 'some-component', selector: [[['some-component'], null]],
template: function(ctx: SomeComponent, cm: boolean) { template: function(ctx: SomeComponent, cm: boolean) {
if (cm) { if (cm) {
text(0, 'foo'); text(0, 'foo');
@ -139,7 +141,7 @@ describe('animation renderer factory', () => {
} }
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: SomeComponentWithAnimation, type: SomeComponentWithAnimation,
tag: 'some-component', selector: [[['some-component'], null]],
template: function(ctx: SomeComponentWithAnimation, cm: boolean) { template: function(ctx: SomeComponentWithAnimation, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'div'); elementStart(0, 'div');

View File

@ -18,6 +18,7 @@ describe('ViewContainerRef', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: TestDirective, type: TestDirective,
selector: [[['', 'testDir', ''], null]],
factory: () => new TestDirective(injectViewContainerRef(), injectTemplateRef(), ), factory: () => new TestDirective(injectViewContainerRef(), injectTemplateRef(), ),
}); });
} }
@ -27,7 +28,7 @@ describe('ViewContainerRef', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: TestComponent, type: TestComponent,
tag: 'test-cmp', selector: [[['test-cmp'], null]],
factory: () => new TestComponent(), factory: () => new TestComponent(),
template: (cmp: TestComponent, cm: boolean) => { template: (cmp: TestComponent, cm: boolean) => {
if (cm) { if (cm) {
@ -37,12 +38,13 @@ describe('ViewContainerRef', () => {
} }
textBinding(0, bind(ctx.$implicit)); textBinding(0, bind(ctx.$implicit));
}; };
container(0, [TestDirective], subTemplate); container(0, subTemplate, undefined, ['testDir', '']);
} }
containerRefreshStart(0); containerRefreshStart(0);
cmp.testDir = loadDirective<TestDirective>(0); cmp.testDir = loadDirective<TestDirective>(0);
containerRefreshEnd(); containerRefreshEnd();
}, },
directiveDefs: [TestDirective.ngDirectiveDef]
}); });
} }