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 */
static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({
type: LargeTableComponent,
tag: 'largetable',
selector: [[['largetable'], null]],
template: function(ctx: LargeTableComponent, cm: boolean) {
if (cm) {
E(0, 'table');

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,8 @@ import {Type} from '../type';
import {resolveRendererType2} from '../view/util';
import {diPublic} from './di';
import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, PipeDef} from './interfaces/definition';
import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, DirectiveDefListOrFactory, PipeDef} from './interfaces/definition';
import {CssSelector} from './interfaces/projection';
@ -41,6 +42,9 @@ export function defineComponent<T>(componentDefinition: {
*/
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.
*/
@ -90,11 +94,6 @@ export function defineComponent<T>(componentDefinition: {
*/
exportAs?: string;
/**
* HTML tag name to use in place where this component should be instantiated.
*/
tag: string;
/**
* Template function use for rendering DOM.
*
@ -147,13 +146,20 @@ export function defineComponent<T>(componentDefinition: {
* Defines the set of injectable objects that are visible to its view DOM children.
*/
viewProviders?: Provider[];
/**
* Registry of directives and components that may be found in this component's view.
*
* The property is either an array of `DirectiveDef`s or a function which returns the array of
* `DirectiveDef`s. The function is necessary to be able to support forward declarations.
*/
directiveDefs?: DirectiveDefListOrFactory | null;
}): ComponentDef<T> {
const type = componentDefinition.type;
const def = <ComponentDef<any>>{
type: type,
diPublic: null,
factory: componentDefinition.factory,
tag: componentDefinition.tag || null !,
template: componentDefinition.template || null !,
hostBindings: componentDefinition.hostBindings || null,
attributes: componentDefinition.attributes || null,
@ -168,7 +174,9 @@ export function defineComponent<T>(componentDefinition: {
afterViewInit: type.prototype.ngAfterViewInit || null,
afterViewChecked: type.prototype.ngAfterViewChecked || null,
onDestroy: type.prototype.ngOnDestroy || null,
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
directiveDefs: componentDefinition.directiveDefs || null,
selector: componentDefinition.selector
};
const feature = componentDefinition.features;
feature && feature.forEach((fn) => fn(def));
@ -300,6 +308,9 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
*/
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.
*/

View File

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

View File

@ -45,7 +45,8 @@ export function queueLifecycleHooks(flags: number, currentView: LView): void {
const tView = currentView.tView;
if (tView.firstTemplatePass === true) {
const start = flags >> TNodeFlags.INDX_SHIFT;
const end = start + (flags & TNodeFlags.SIZE_MASK);
const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
const end = start + size;
// It's necessary to loop through the directives at elementEnd() (rather than processing in
// directiveCreate) so we can preserve the current hook order. Content, view, and destroy

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 {assertNodeType} from './node_assert';
import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode} from './node_manipulation';
import {matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition';
import {isNodeMatchingSelector, matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType} from './interfaces/definition';
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {isDifferent, stringify} from './util';
import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
@ -48,7 +48,7 @@ export type Sanitizer = (value: any) => string;
*
* Saved here to avoid re-instantiating an array on every change detection run.
*/
const rootDirectiveIndices = [0, 0];
export const _ROOT_DIRECTIVE_INDICES = [0, 0];
/**
@ -243,7 +243,7 @@ function refreshDirectives() {
}
/** Sets the host bindings for the current view. */
function setHostBindings(bindings: number[] | null): void {
export function setHostBindings(bindings: number[] | null): void {
if (bindings != null) {
const defs = currentView.tView.directives !;
for (let i = 0; i < bindings.length; i += 2) {
@ -263,7 +263,7 @@ function refreshChildComponents(components: number[] | null): void {
}
}
function executeInitAndContentHooks(): void {
export function executeInitAndContentHooks(): void {
if (!checkNoChangesMode) {
const tView = currentView.tView;
executeInitHooks(currentView, tView, creationMode);
@ -395,21 +395,25 @@ function resetApplicationState() {
/**
*
* @param host Existing node to render into.
* @param hostNode Existing node to render into.
* @param template Template function with the instructions.
* @param context to pass into the template.
* @param providedRendererFactory renderer factory to use
* @param host The host element node to use
* @param directiveRegistry Any directive defs that should be used to match nodes to directives
*/
export function renderTemplate<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) {
resetApplicationState();
rendererFactory = providedRendererFactory;
host = createLNode(
null, LNodeType.Element, hostNode,
createLView(
-1, providedRendererFactory.createRenderer(null, null), getOrCreateTView(template),
null, {}, LViewFlags.CheckAlways));
-1, providedRendererFactory.createRenderer(null, null),
getOrCreateTView(template, directiveRegistry), null, {}, LViewFlags.CheckAlways));
}
const hostView = host.data !;
ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.');
@ -427,8 +431,9 @@ export function renderEmbeddedTemplate<T>(
previousOrParentNode = null !;
let cm: boolean = false;
if (viewNode == null) {
const view =
createLView(-1, renderer, createTView(), template, context, LViewFlags.CheckAlways);
const view = createLView(
-1, renderer, createTView(currentView.tView.directiveRegistry), template, context,
LViewFlags.CheckAlways);
viewNode = createLNode(null, LNodeType.View, null, view);
cm = true;
}
@ -460,7 +465,7 @@ export function renderComponentOrTemplate<T>(
// Element was stored at 0 in data and directive was stored at 0 in directives
// in renderComponent()
setHostBindings(rootDirectiveIndices);
setHostBindings(_ROOT_DIRECTIVE_INDICES);
componentRefresh(0, 0);
}
} finally {
@ -479,9 +484,8 @@ export function renderComponentOrTemplate<T>(
* Create DOM element. The instruction must later be followed by `elementEnd()` call.
*
* @param index Index of the element in the data array
* @param nameOrComponentType Name of the DOM Node or `ComponentType` to create.
* @param name Name of the DOM Node
* @param attrs Statically bound set of attributes to be written into the DOM element on creation.
* @param directiveTypes A set of directives declared on this element.
* @param localRefs A set of local reference bindings on the element.
*
* Attributes and localRefs are passed as an array of strings where elements with an even index
@ -489,77 +493,71 @@ export function renderComponentOrTemplate<T>(
* ['id', 'warning5', 'class', 'alert']
*/
export function elementStart(
index: number, nameOrComponentType?: string | ComponentType<any>, attrs?: string[] | null,
directiveTypes?: DirectiveType<any>[] | null, localRefs?: string[] | null): RElement {
index: number, name?: string, attrs?: string[] | null, localRefs?: string[] | null): RElement {
let node: LElementNode;
let native: RElement;
if (nameOrComponentType == null) {
if (name == null) {
// native node retrieval - used for exporting elements as tpl local variables (<div #foo>)
const node = data[index] !;
native = node && (node as LElementNode).native;
} else {
ngDevMode &&
assertNull(currentView.bindingStartIndex, 'elements should be created before any bindings');
const isHostElement = typeof nameOrComponentType !== 'string';
let hostComponentDef: ComponentDef<any>|null = null;
let name = nameOrComponentType as string;
native = renderer.createElement(name);
node = createLNode(index, LNodeType.Element, native !, null);
let directiveIndex = getNextDirectiveIndex(index);
if (attrs) setUpAttributes(native, attrs);
appendChild(node.parent !, native, currentView);
if (isHostElement) {
hostComponentDef = firstTemplatePass ?
(nameOrComponentType as ComponentType<any>).ngComponentDef :
currentView.tView.directives ![directiveIndex] as ComponentDef<any>;
if (firstTemplatePass) {
const tNode = createTNode(name, attrs || null, null, null);
cacheMatchingDirectivesForNode(tNode);
name = hostComponentDef !.tag;
}
if (name === null) {
// TODO: future support for nameless components.
throw 'for now name is required';
} else {
native = renderer.createElement(name);
let componentView: LView|null = null;
if (isHostElement) {
const tView = getOrCreateTView(hostComponentDef !.template);
const hostView = createLView(
-1, rendererFactory.createRenderer(native, hostComponentDef !.rendererType), tView,
null, null, hostComponentDef !.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
componentView = addToViewTree(hostView);
}
// Only component views should be added to the view tree directly. Embedded views are
// accessed through their containers because they may be removed / re-added later.
node = createLNode(index, LNodeType.Element, native, componentView);
if (node.tNode == null) {
const localNames: (string | number)[]|null = findMatchingLocalNames(
hostComponentDef, localRefs, isHostElement ? directiveIndex : -1, '');
ngDevMode && assertDataInRange(index - 1);
node.tNode = tData[index] = createTNode(name, attrs || null, null, localNames);
}
if (attrs) setUpAttributes(native, attrs);
appendChild(node.parent !, native, currentView);
if (hostComponentDef) {
// TODO(mhevery): This assumes that the directives come in correct order, which
// is not guaranteed. Must be refactored to take it into account.
const instance = hostComponentDef.factory();
directiveCreate(directiveIndex, index, instance, hostComponentDef, null);
initChangeDetectorIfExisting(node.nodeInjector, instance);
queueComponentIndexForCheck(directiveIndex, index);
directiveIndex++;
}
hack_declareDirectives(directiveIndex, index, directiveTypes, localRefs);
ngDevMode && assertDataInRange(index - 1);
node.tNode = tData[index] = tNode;
if (!isComponent(tNode)) {
tNode.localNames = findMatchingLocalNames(null, localRefs, -1, '');
}
}
hack_declareDirectives(index, localRefs);
}
return native;
}
function cacheMatchingDirectivesForNode(tNode: TNode): void {
const registry = currentView.tView.directiveRegistry;
const startIndex = directives ? directives.length : 0;
if (registry) {
let componentFlag = 0;
let size = 0;
for (let i = 0; i < registry.length; i++) {
const def = registry[i];
if (isNodeMatchingSelector(tNode, def.selector !)) {
if ((def as ComponentDef<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. */
function queueComponentIndexForCheck(dirIndex: number, elIndex: number): void {
if (firstTemplatePass) {
@ -576,30 +574,35 @@ function queueHostBindingForCheck(dirIndex: number, elIndex: number): void {
}
/** Sets the context for a ChangeDetectorRef to the given instance. */
export function initChangeDetectorIfExisting(injector: LInjector | null, instance: any): void {
export function initChangeDetectorIfExisting(
injector: LInjector | null, instance: any, view: LView): void {
if (injector && injector.changeDetectorRef != null) {
(injector.changeDetectorRef as ViewRef<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
* come in the correct order for DI.
*/
function hack_declareDirectives(
index: number, elementIndex: number, directiveTypes: DirectiveType<any>[] | null | undefined,
localRefs: string[] | null | undefined, ) {
if (directiveTypes) {
const defs = currentView.tView.directives !;
function hack_declareDirectives(elementIndex: number, localRefs: string[] | null | undefined) {
const size = (previousOrParentNode.tNode !.flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
if (size > 0) {
let startIndex = previousOrParentNode.tNode !.flags >> TNodeFlags.INDX_SHIFT;
const endIndex = startIndex + size;
const tDirectives = currentView.tView.directives !;
// TODO(mhevery): This assumes that the directives come in correct order, which
// is not guaranteed. Must be refactored to take it into account.
for (let i = 0; i < directiveTypes.length; i++) {
const directiveType = directiveTypes[i];
const directiveDef =
firstTemplatePass ? directiveType.ngDirectiveDef : defs[index] as DirectiveDef<any>;
directiveCreate(index, elementIndex, directiveDef.factory(), directiveDef, localRefs);
index++;
for (let i = startIndex; i < endIndex; i++) {
const def = tDirectives[i] as DirectiveDef<any>;
directiveCreate(elementIndex, def.factory(), def, localRefs);
startIndex++;
}
}
}
@ -633,12 +636,13 @@ function findMatchingLocalNames(
* @param template The template from which to get static data
* @returns TView
*/
function getOrCreateTView(template: ComponentTemplate<any>): TView {
return template.ngPrivateData || (template.ngPrivateData = createTView() as never);
function getOrCreateTView(
template: ComponentTemplate<any>, defs: DirectiveDefListOrFactory | null): TView {
return template.ngPrivateData || (template.ngPrivateData = createTView(defs) as never);
}
/** Creates a TView instance */
export function createTView(): TView {
export function createTView(defs: DirectiveDefListOrFactory | null): TView {
return {
data: [],
directives: null,
@ -652,7 +656,8 @@ export function createTView(): TView {
destroyHooks: null,
pipeDestroyHooks: null,
hostBindings: null,
components: null
components: null,
directiveRegistry: typeof defs === 'function' ? defs() : defs
};
}
@ -708,13 +713,22 @@ export function locateHostElement(
*
* @returns LElementNode created
*/
export function hostElement(rNode: RElement | null, def: ComponentDef<any>): LElementNode {
export function hostElement(
tag: string, rNode: RElement | null, def: ComponentDef<any>): LElementNode {
resetApplicationState();
const node = createLNode(
0, LNodeType.Element, rNode, createLView(
-1, renderer, getOrCreateTView(def.template), null, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways));
if (firstTemplatePass) node.tNode = createTNode(def.tag, null, null, null);
0, LNodeType.Element, rNode,
createLView(
-1, renderer, getOrCreateTView(def.template, def.directiveDefs), null, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways));
if (firstTemplatePass) {
node.tNode = createTNode(tag as string, null, null, null);
// Root directive is stored at index 0, size 1
buildTNodeFlags(node.tNode, 0, 1, TNodeFlags.Component);
currentView.tView.directives = [def];
}
return node;
}
@ -898,7 +912,7 @@ function setInputsForProperty(inputs: PropertyAliasValue, value: any): void {
*/
function generatePropertyAliases(
tNodeFlags: TNodeFlags, direction: BindingDirection): PropertyAliases|null {
const size = tNodeFlags & TNodeFlags.SIZE_MASK;
const size = (tNodeFlags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
let propStore: PropertyAliases|null = null;
if (size > 0) {
@ -1101,20 +1115,25 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
* NOTE: directives can be created in order other than the index order. They can also
* be retrieved before they are created in which case the value will be null.
*
* @param index Index to save the directive in the directives array
* @param elementIndex Index of the host element in the data array
* @param directive The directive instance.
* @param directiveDef DirectiveDef object which contains information about the template.
* @param localRefs Names under which a query can retrieve the directive instance
*/
export function directiveCreate<T>(
index: number, elementIndex: number, directive: T, directiveDef: DirectiveDef<T>,
elementIndex: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>,
localRefs?: string[] | null): T {
const index = directives ? directives.length : 0;
const instance = baseDirectiveCreate(index, directive, directiveDef);
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
const tNode: TNode|null = previousOrParentNode.tNode !;
const isComponent = (directiveDef as ComponentDef<any>).template;
if (isComponent) {
addComponentLogic(index, elementIndex, directive, directiveDef as ComponentDef<any>);
}
if (firstTemplatePass) {
// Init hooks are queued now so ngOnInit is called in host components before
// any projected components.
@ -1123,7 +1142,8 @@ export function directiveCreate<T>(
if (directiveDef.hostBindings) queueHostBindingForCheck(index, elementIndex);
if (localRefs) {
const localNames = findMatchingLocalNames(directiveDef, localRefs, index);
const localNames =
findMatchingLocalNames(directiveDef, localRefs, index, isComponent ? '' : undefined);
tNode.localNames =
localNames && tNode.localNames ? tNode.localNames.concat(localNames) : localNames;
}
@ -1136,6 +1156,26 @@ export function directiveCreate<T>(
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
*
@ -1143,7 +1183,7 @@ export function directiveCreate<T>(
* current Angular. Example: local refs and inputs on root component.
*/
export function baseDirectiveCreate<T>(
index: number, directive: T, directiveDef: DirectiveDef<T>): T {
index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<any>): T {
ngDevMode &&
assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings');
ngDevMode && assertPreviousIsParent();
@ -1156,17 +1196,6 @@ export function baseDirectiveCreate<T>(
ngDevMode && assertDataNext(index, directives);
directives[index] = directive;
if (firstTemplatePass) {
const defs = currentView.tView.directives || (currentView.tView.directives = []);
ngDevMode && assertDataNext(index, defs);
defs[index] = (directiveDef);
const flags = previousOrParentNode.tNode !.flags;
previousOrParentNode.tNode !.flags =
(flags & TNodeFlags.SIZE_MASK) === 0 ? (index << TNodeFlags.INDX_SHIFT) | 1 : flags + 1;
}
const diPublic = directiveDef !.diPublic;
if (diPublic) {
diPublic(directiveDef !);
@ -1253,8 +1282,8 @@ function generateInitialInputs(
* @param localRefs A set of local reference bindings on the element.
*/
export function container(
index: number, directiveTypes?: DirectiveType<any>[], template?: ComponentTemplate<any>,
tagName?: string, attrs?: string[], localRefs?: string[] | null): void {
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[],
localRefs?: string[] | null): void {
ngDevMode &&
assertNull(
currentView.bindingStartIndex, 'container nodes should be created before any bindings');
@ -1285,7 +1314,9 @@ export function container(
// Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted.
addToViewTree(node.data);
hack_declareDirectives(getNextDirectiveIndex(index), index, directiveTypes, localRefs);
if (firstTemplatePass) cacheMatchingDirectivesForNode(node.tNode);
hack_declareDirectives(index, localRefs);
isParent = false;
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container);
@ -1298,12 +1329,6 @@ export function container(
}
}
/** Retrieves the next directive index to write */
function getNextDirectiveIndex(index: number): number {
return firstTemplatePass ? (directives ? directives.length : 0) :
(tData[index] as TNode).flags >> TNodeFlags.INDX_SHIFT;
}
/**
* Sets a container up to receive views.
*
@ -1421,7 +1446,6 @@ export function embeddedViewStart(viewBlockId: number): boolean {
enterView(newView, createLNode(null, LNodeType.View, null, newView));
}
return !existingViewNode;
}
@ -1441,7 +1465,7 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV
ngDevMode && assertNodeType(parent, LNodeType.Container);
const tContainer = (parent !.tNode as TContainerNode).data;
if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) {
tContainer[viewIndex] = createTView();
tContainer[viewIndex] = createTView(currentView.tView.directiveRegistry);
}
return tContainer[viewIndex];
}
@ -1487,9 +1511,10 @@ export function componentRefresh<T>(directiveIndex: number, elementIndex: number
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
if (viewAttached(hostView) && hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
ngDevMode && assertDataInRange(directiveIndex, directives !);
const template = (currentView.tView.directives ![directiveIndex] as ComponentDef<any>).template;
const def = currentView.tView.directives ![directiveIndex] as ComponentDef<any>;
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);
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
const componentIndex = hostNode.tNode !.flags >> TNodeFlags.INDX_SHIFT;
const template = (hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>).template;
detectChangesInternal(hostNode.data as LView, hostNode, template, component);
const def = hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>;
detectChangesInternal(hostNode.data as LView, hostNode, def, component);
}
@ -1833,8 +1858,10 @@ function throwErrorIfNoChangesMode(oldValue: any, currValue: any): never|void {
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
export function detectChangesInternal<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 template = def.template;
try {
template(component, creationMode);
refreshDynamicChildren();
@ -2150,3 +2177,4 @@ export function _getComponentHostLElementNode<T>(component: T): LElementNode {
}
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 {Type} from '../../type';
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. */
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
* are their aliases if any, or their original unminified property names
@ -122,13 +125,6 @@ export interface DirectiveDef<T> {
* See: {@link defineComponent}
*/
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.
*
@ -157,6 +153,14 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
* children only.
*/
readonly viewProviders?: Provider[];
/**
* Registry of directives and components that may be found in this view.
*
* The property is either an array of `DirectiveDef`s or a function which returns the array of
* `DirectiveDef`s. The function is necessary to be able to support forward declarations.
*/
directiveDefs: DirectiveDefListOrFactory|null;
}
/**
@ -195,6 +199,15 @@ export interface PipeDef<T> {
export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<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
// failure based on types.
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
* (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.

View File

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

View File

@ -196,8 +196,8 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
function geIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
const defs = node.view.tView.directives !;
const flags = node.tNode !.flags;
for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + (flags & TNodeFlags.SIZE_MASK); i < ii;
i++) {
const size = (flags & TNodeFlags.SIZE_MASK) >> TNodeFlags.SIZE_SHIFT;
for (let i = flags >> TNodeFlags.INDX_SHIFT, ii = i + size; i < ii; i++) {
const def = defs[i] as DirectiveDef<any>;
if (def.diPublic && def.type === type) {
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 !; }
/** @internal */
_setComponentContext(context: T) { this.context = context; }
_setComponentContext(view: LView, context: T) {
this._view = view;
this.context = context;
}
destroy(): void { notImplemented(); }
destroyed: boolean;
@ -223,9 +226,9 @@ export class EmbeddedViewRef<T> extends ViewRef<T> {
* @param context The context for this view
* @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
return addDestroyable(new ViewRef(view, context));
return addDestroyable(new ViewRef(view !, context));
}
/** Interface for destroy logic. Implemented by addDestroyable. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,9 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {defineComponent} from '../../src/render3/definition';
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding} from '../../src/render3/instructions';
import {renderToHtml} from './render_util';
import {ComponentFixture, createComponent, renderToHtml} from './render_util';
describe('JS control flow', () => {
it('should work with if block', () => {
@ -531,6 +532,136 @@ describe('JS control flow', () => {
ctx.condition = true;
expect(renderToHtml(Template, ctx)).toEqual('<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', () => {

View File

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

View File

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

View File

@ -178,7 +178,7 @@ describe('render3 integration test', () => {
static ngComponentDef = defineComponent({
type: TodoComponent,
tag: 'todo',
selector: [[['todo'], null]],
template: function TodoTemplate(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'p');
@ -194,26 +194,28 @@ describe('render3 integration test', () => {
});
}
const defs = [TodoComponent.ngComponentDef];
it('should support a basic component template', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, TodoComponent);
elementStart(0, 'todo');
elementEnd();
}
}
expect(renderToHtml(Template, null)).toEqual('<todo><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', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, TodoComponent);
elementStart(0, 'todo');
elementEnd();
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', () => {
@ -223,13 +225,13 @@ describe('render3 integration test', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, TodoComponent);
elementStart(0, 'todo');
elementEnd();
elementStart(1, TodoComponent);
elementStart(1, 'todo');
elementEnd();
}
}
expect(renderToHtml(Template, null))
expect(renderToHtml(Template, null, defs))
.toEqual('<todo><p>Todo one</p></todo><todo><p>Todo one</p></todo>');
});
@ -240,7 +242,7 @@ describe('render3 integration test', () => {
title = 'one';
static ngComponentDef = defineComponent({
type: TodoComponentHostBinding,
tag: 'todo',
selector: [[['todo'], null]],
template: function TodoComponentHostBindingTemplate(
ctx: TodoComponentHostBinding, cm: boolean) {
if (cm) {
@ -260,21 +262,22 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, TodoComponentHostBinding);
elementStart(0, 'todo');
elementEnd();
}
}
expect(renderToHtml(Template, {})).toEqual('<todo title="one">one</todo>');
const defs = [TodoComponentHostBinding.ngComponentDef];
expect(renderToHtml(Template, {}, defs)).toEqual('<todo title="one">one</todo>');
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', () => {
class HostAttributeComp {
static ngComponentDef = defineComponent({
type: HostAttributeComp,
tag: 'host-attr-comp',
selector: [[['host-attr-comp'], null]],
factory: () => new HostAttributeComp(),
template: (ctx: HostAttributeComp, cm: boolean) => {},
attributes: ['role', 'button']
@ -291,7 +294,7 @@ describe('render3 integration test', () => {
name = 'Bess';
static ngComponentDef = defineComponent({
type: MyComp,
tag: 'comp',
selector: [[['comp'], null]],
template: function MyCompTemplate(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'p');
@ -306,12 +309,13 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'comp');
elementEnd();
}
}
expect(renderToHtml(Template, null)).toEqual('<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', () => {
@ -324,7 +328,7 @@ describe('render3 integration test', () => {
condition: boolean;
static ngComponentDef = defineComponent({
type: MyComp,
tag: 'comp',
selector: [[['comp'], null]],
template: function MyCompTemplate(ctx: any, cm: boolean) {
if (cm) {
container(0);
@ -350,14 +354,16 @@ describe('render3 integration test', () => {
/** <comp [condition]="condition"></comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'comp');
elementEnd();
}
elementProperty(0, 'condition', bind(ctx.condition));
}
expect(renderToHtml(Template, {condition: true})).toEqual('<comp><div>text</div></comp>');
expect(renderToHtml(Template, {condition: false})).toEqual('<comp></comp>');
const defs = [MyComp.ngComponentDef];
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;
afterTree: Tree;
static ngComponentDef = defineComponent({
tag: 'child',
selector: [[['child'], null]],
type: ChildComponent,
template: function ChildComponentTemplate(
ctx: {beforeTree: Tree, afterTree: Tree}, cm: boolean) {
@ -460,7 +466,7 @@ describe('render3 integration test', () => {
function parentTemplate(ctx: ParentCtx, cm: boolean) {
if (cm) {
elementStart(0, ChildComponent);
elementStart(0, 'child');
{ container(1); }
elementEnd();
}
@ -482,14 +488,15 @@ describe('render3 integration test', () => {
projectedTree: {beforeLabel: 'p'},
afterTree: {afterLabel: 'z'}
};
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>apz</child>');
const defs = [ChildComponent.ngComponentDef];
expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>apz</child>');
ctx.projectedTree = {subTrees: [{}, {}, {subTrees: [{}, {}]}, {}]};
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';
expect(renderToHtml(parentTemplate, ctx)).toEqual('<child>abhz</child>');
expect(renderToHtml(parentTemplate, ctx, defs)).toEqual('<child>abhz</child>');
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:
// console.log(JSON.stringify(ctx));
@ -633,6 +640,7 @@ describe('render3 integration test', () => {
static ngDirectiveDef = defineDirective({
type: HostBindingDir,
selector: [[['', 'hostBindingDir', ''], null]],
factory: function HostBindingDir_Factory() {
return hostBindingDir = new HostBindingDir();
},
@ -645,16 +653,17 @@ describe('render3 integration test', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['hostBindingDir', ''], [HostBindingDir]);
elementStart(0, 'div', ['hostBindingDir', '']);
elementEnd();
}
}
expect(renderToHtml(Template, {}))
const defs = [HostBindingDir.ngDirectiveDef];
expect(renderToHtml(Template, {}, defs))
.toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
hostBindingDir !.label = 'other label';
expect(renderToHtml(Template, {}))
expect(renderToHtml(Template, {}, defs))
.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({
type: MyComp,
tag: 'comp',
selector: [[['comp'], null]],
/** <button (click)="onClick()"> Click me </button> */
template: function CompTemplate(ctx: any, cm: boolean) {
if (cm) {
@ -61,7 +61,7 @@ describe('event listeners', () => {
static ngComponentDef = defineComponent({
type: PreventDefaultComp,
tag: 'prevent-default-comp',
selector: [[['prevent-default-comp'], null]],
factory: () => new PreventDefaultComp(),
/** <button (click)="onClick($event)">Click</button> */
template: (ctx: PreventDefaultComp, cm: boolean) => {
@ -235,6 +235,7 @@ describe('event listeners', () => {
static ngDirectiveDef = defineDirective({
type: HostListenerDir,
selector: [[['', 'hostListenerDir', ''], null]],
factory: function HostListenerDir_Factory() {
const $dir$ = new HostListenerDir();
listener('click', function() { return $dir$.onClick(); });
@ -245,13 +246,13 @@ describe('event listeners', () => {
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', ['hostListenerDir', ''], [HostListenerDir]);
elementStart(0, 'button', ['hostListenerDir', '']);
text(1, 'Click');
elementEnd();
}
}
renderToHtml(Template, {});
renderToHtml(Template, {}, [HostListenerDir.ngDirectiveDef]);
const button = containerEl.querySelector('button') !;
button.click();
expect(events).toEqual(['click!']);
@ -337,9 +338,9 @@ describe('event listeners', () => {
if (ctx.showing) {
if (embeddedViewStart(0)) {
text(0, 'Hello');
elementStart(1, MyComp);
elementStart(1, 'comp');
elementEnd();
elementStart(2, MyComp);
elementStart(2, 'comp');
elementEnd();
}
embeddedViewEnd();
@ -349,7 +350,7 @@ describe('event listeners', () => {
}
const ctx = {showing: true};
renderToHtml(Template, ctx);
renderToHtml(Template, ctx, [MyComp.ngComponentDef]);
const buttons = containerEl.querySelectorAll('button') !;
buttons[0].click();
@ -360,7 +361,7 @@ describe('event listeners', () => {
// the child view listener should be removed when the parent view is removed
ctx.showing = false;
renderToHtml(Template, ctx);
renderToHtml(Template, ctx, [MyComp.ngComponentDef]);
buttons[0].click();
buttons[1].click();
expect(comps[0] !.counter).toEqual(1);

View File

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

View File

@ -6,15 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform, SimpleChange, SimpleChanges, WrappedValue} from '@angular/core';
import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform} from '@angular/core';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {NgOnChangesFeature, defineComponent, defineDirective, definePipe} from '../../src/render3/definition';
import {defineDirective, definePipe} from '../../src/render3/definition';
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, text, textBinding} from '../../src/render3/instructions';
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
import {renderComponent, renderToHtml} from './render_util';
import {renderToHtml} from './render_util';
let log: string[] = [];
@ -51,8 +51,12 @@ describe('pipe', () => {
constructor() { this.dirProp = ''; }
static ngDirectiveDef =
defineDirective({type: MyDir, factory: () => new MyDir(), inputs: {dirProp: 'elprop'}});
static ngDirectiveDef = defineDirective({
type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => new MyDir(),
inputs: {dirProp: 'elprop'}
});
}
@Pipe({name: 'double'})
@ -67,14 +71,14 @@ describe('pipe', () => {
function Template(ctx: string, cm: boolean) {
if (cm) {
elementStart(0, 'div', null, [MyDir]);
elementStart(0, 'div', ['myDir', '']);
pipe(1, DoublePipe.ngPipeDef);
elementEnd();
}
elementProperty(0, 'elprop', bind(pipeBind1(1, ctx)));
directive = loadDirective(0);
}
renderToHtml(Template, 'a');
renderToHtml(Template, 'a', [MyDir.ngDirectiveDef]);
expect(directive !.dirProp).toEqual('aa');
});
@ -124,11 +128,11 @@ describe('pipe', () => {
}
elementProperty(0, 'someProp', bind(pipeBind1(1, 'Megatron')));
}
renderToHtml(Template, person, rendererFactory2);
renderToHtml(Template, person, [], rendererFactory2);
expect(renderLog.log).toEqual(['someProp=Megatron']);
renderLog.clear();
renderToHtml(Template, person, rendererFactory2);
renderToHtml(Template, person, [], rendererFactory2);
expect(renderLog.log).toEqual([]);
});
@ -192,7 +196,7 @@ describe('pipe', () => {
containerRefreshEnd();
}
const pipeInstances: CountingPipe[] = [];
renderToHtml(Template, {}, rendererFactory2);
renderToHtml(Template, {}, [], rendererFactory2);
expect(pipeInstances.length).toEqual(4);
expect(pipeInstances[0]).toBeAnInstanceOf(CountingPipe);
expect(pipeInstances[1]).toBe(pipeInstances[0]);
@ -249,7 +253,7 @@ describe('pipe', () => {
containerRefreshEnd();
}
const pipeInstances: CountingImpurePipe[] = [];
renderToHtml(Template, {}, rendererFactory2);
renderToHtml(Template, {}, [], rendererFactory2);
expect(pipeInstances.length).toEqual(4);
expect(pipeInstances[0]).toBeAnInstanceOf(CountingImpurePipe);
expect(pipeInstances[1]).toBeAnInstanceOf(CountingImpurePipe);

View File

@ -11,7 +11,7 @@ import {EventEmitter} from '@angular/core';
import {defineComponent, defineDirective, tick} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, loadDirective, text, textBinding} from '../../src/render3/instructions';
import {ComponentFixture, renderToHtml} from './render_util';
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util';
describe('elementProperty', () => {
@ -68,7 +68,7 @@ describe('elementProperty', () => {
static ngComponentDef = defineComponent({
type: HostBindingComp,
tag: 'host-binding-comp',
selector: [[['host-binding-comp'], null]],
factory: () => new HostBindingComp(),
hostBindings: (dirIndex: number, elIndex: number) => {
const instance = loadDirective(dirIndex) as HostBindingComp;
@ -89,12 +89,18 @@ describe('elementProperty', () => {
describe('input properties', () => {
let button: MyButton;
let otherDir: OtherDir;
let otherDisabledDir: OtherDisabledDir;
let idDir: IdDir;
class MyButton {
disabled: boolean;
static ngDirectiveDef = defineDirective(
{type: MyButton, factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}});
static ngDirectiveDef = defineDirective({
type: MyButton,
selector: [[['', 'myButton', ''], null]],
factory: () => button = new MyButton(),
inputs: {disabled: 'disabled'}
});
}
class OtherDir {
@ -103,18 +109,47 @@ describe('elementProperty', () => {
static ngDirectiveDef = defineDirective({
type: OtherDir,
selector: [[['', 'otherDir', ''], null]],
factory: () => otherDir = new OtherDir(),
inputs: {id: 'id'},
outputs: {clickStream: 'click'}
});
}
class OtherDisabledDir {
disabled: boolean;
static ngDirectiveDef = defineDirective({
type: OtherDisabledDir,
selector: [[['', 'otherDisabledDir', ''], null]],
factory: () => otherDisabledDir = new OtherDisabledDir(),
inputs: {disabled: 'disabled'}
});
}
class IdDir {
idNumber: number;
static ngDirectiveDef = defineDirective({
type: IdDir,
selector: [[['', 'idDir', ''], null]],
factory: () => idDir = new IdDir(),
inputs: {idNumber: 'id'}
});
}
const deps = [
MyButton.ngDirectiveDef, OtherDir.ngDirectiveDef, OtherDisabledDir.ngDirectiveDef,
IdDir.ngDirectiveDef
];
it('should check input properties before setting (directives)', () => {
/** <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) {
if (cm) {
elementStart(0, 'button', null, [MyButton, OtherDir]);
elementStart(0, 'button', ['otherDir', '', 'myButton', '']);
{ text(1, 'Click me'); }
elementEnd();
}
@ -124,13 +159,15 @@ describe('elementProperty', () => {
}
const ctx: any = {isDisabled: true, id: 0};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`);
expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdir="">Click me</button>`);
expect(button !.disabled).toEqual(true);
expect(otherDir !.id).toEqual(0);
ctx.isDisabled = false;
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(otherDir !.id).toEqual(1);
});
@ -140,7 +177,7 @@ describe('elementProperty', () => {
/** <button myButton [id]="id" [disabled]="isDisabled">Click me</button> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', null, [MyButton]);
elementStart(0, 'button', ['myButton', '']);
{ text(1, 'Click me'); }
elementEnd();
}
@ -150,23 +187,27 @@ describe('elementProperty', () => {
}
const ctx: any = {isDisabled: true, id: 0};
expect(renderToHtml(Template, ctx)).toEqual(`<button id="0">Click me</button>`);
expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button id="0" mybutton="">Click me</button>`);
expect(button !.disabled).toEqual(true);
ctx.isDisabled = false;
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);
});
it('should check that property is not an input property before setting (component)', () => {
let comp: Comp;
class Comp {
id: number;
static ngComponentDef = defineComponent({
type: Comp,
tag: 'comp',
selector: [[['comp'], null]],
template: function(ctx: any, cm: boolean) {},
factory: () => comp = new Comp(),
inputs: {id: 'id'}
@ -176,36 +217,26 @@ describe('elementProperty', () => {
/** <comp [id]="id"></comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, Comp);
elementStart(0, 'comp');
elementEnd();
}
elementProperty(0, 'id', bind(ctx.id));
}
expect(renderToHtml(Template, {id: 1})).toEqual(`<comp></comp>`);
const deps = [Comp.ngComponentDef];
expect(renderToHtml(Template, {id: 1}, deps)).toEqual(`<comp></comp>`);
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);
});
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> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', null, [MyButton, OtherDisabledDir]);
elementStart(0, 'button', ['myButton', '', 'otherDisabledDir', '']);
{ text(1, 'Click me'); }
elementEnd();
}
@ -213,12 +244,14 @@ describe('elementProperty', () => {
}
const ctx: any = {isDisabled: true};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`);
expect(renderToHtml(Template, ctx, deps))
.toEqual(`<button mybutton="" otherdisableddir="">Click me</button>`);
expect(button !.disabled).toEqual(true);
expect(otherDisabledDir !.disabled).toEqual(true);
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(otherDisabledDir !.disabled).toEqual(false);
});
@ -227,7 +260,7 @@ describe('elementProperty', () => {
/** <button otherDir [id]="id" (click)="onClick()">Click me</button> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', null, [OtherDir]);
elementStart(0, 'button', ['otherDir', '']);
{
listener('click', ctx.onClick.bind(ctx));
text(1, 'Click me');
@ -239,27 +272,18 @@ describe('elementProperty', () => {
let counter = 0;
const ctx: any = {id: 1, onClick: () => counter++};
expect(renderToHtml(Template, ctx)).toEqual(`<button>Click me</button>`);
expect(renderToHtml(Template, ctx, deps)).toEqual(`<button otherdir="">Click me</button>`);
expect(otherDir !.id).toEqual(1);
otherDir !.clickStream.next();
expect(counter).toEqual(1);
ctx.id = 2;
renderToHtml(Template, ctx);
renderToHtml(Template, ctx, deps);
expect(otherDir !.id).toEqual(2);
});
it('should support unrelated element properties at same index in if-else block', () => {
let idDir: IdDir;
class IdDir {
idNumber: number;
static ngDirectiveDef = defineDirective(
{type: IdDir, factory: () => idDir = new IdDir(), inputs: {idNumber: 'id'}});
}
/**
* <button idDir [id]="id1">Click me</button> // inputs: {'id': [0, 'idNumber']}
* % if (condition) {
@ -270,7 +294,7 @@ describe('elementProperty', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', null, [IdDir]);
elementStart(0, 'button', ['idDir', '']);
{ text(1, 'Click me'); }
elementEnd();
container(2);
@ -288,7 +312,7 @@ describe('elementProperty', () => {
embeddedViewEnd();
} else {
if (embeddedViewStart(1)) {
elementStart(0, 'button', null, [OtherDir]);
elementStart(0, 'button', ['otherDir', '']);
{ text(1, 'Click me too'); }
elementEnd();
}
@ -299,12 +323,13 @@ describe('elementProperty', () => {
containerRefreshEnd();
}
expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'}))
.toEqual(`<button>Click me</button><button id="two">Click me too</button>`);
expect(renderToHtml(Template, {condition: true, id1: 'one', id2: 'two', id3: 'three'}, deps))
.toEqual(`<button iddir="">Click me</button><button id="two">Click me too</button>`);
expect(idDir !.idNumber).toEqual('one');
expect(renderToHtml(Template, {condition: false, id1: 'four', id2: 'two', id3: 'three'}))
.toEqual(`<button>Click me</button><button>Click me too</button>`);
expect(
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(otherDir !.id).toEqual('three');
});
@ -320,6 +345,7 @@ describe('elementProperty', () => {
static ngDirectiveDef = defineDirective({
type: MyDir,
selector: [[['', 'myDir', ''], null]],
factory: () => myDir = new MyDir(),
inputs: {role: 'role', direction: 'dir'},
outputs: {changeStream: 'change'},
@ -331,21 +357,27 @@ describe('elementProperty', () => {
class MyDirB {
roleB: string;
static ngDirectiveDef = defineDirective(
{type: MyDirB, factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}});
static ngDirectiveDef = defineDirective({
type: MyDirB,
selector: [[['', 'myDirB', ''], null]],
factory: () => dirB = new MyDirB(),
inputs: {roleB: 'role'}
});
}
const deps = [MyDir.ngDirectiveDef, MyDirB.ngDirectiveDef];
it('should set input property based on attribute if existing', () => {
/** <div role="button" myDir></div> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]);
elementStart(0, 'div', ['role', 'button', 'myDir', '']);
elementEnd();
}
}
expect(renderToHtml(Template, {})).toEqual(`<div role="button"></div>`);
expect(renderToHtml(Template, {}, deps)).toEqual(`<div mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('button');
});
@ -354,16 +386,17 @@ describe('elementProperty', () => {
/** <div role="button" [role]="role" myDir></div> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]);
elementStart(0, 'div', ['role', 'button', 'myDir', '']);
elementEnd();
}
elementProperty(0, 'role', bind(ctx.role));
}
expect(renderToHtml(Template, {role: 'listbox'})).toEqual(`<div role="button"></div>`);
expect(renderToHtml(Template, {role: 'listbox'}, deps))
.toEqual(`<div mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('listbox');
renderToHtml(Template, {role: 'button'});
renderToHtml(Template, {role: 'button'}, deps);
expect(myDir !.role).toEqual('button');
});
@ -372,12 +405,13 @@ describe('elementProperty', () => {
/** <div role="button" myDir myDirB></div> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir, MyDirB]);
elementStart(0, 'div', ['role', 'button', 'myDir', '', 'myDirB', '']);
elementEnd();
}
}
expect(renderToHtml(Template, {})).toEqual(`<div role="button"></div>`);
expect(renderToHtml(Template, {}, deps))
.toEqual(`<div mydir="" mydirb="" role="button"></div>`);
expect(myDir !.role).toEqual('button');
expect(dirB !.roleB).toEqual('button');
});
@ -387,12 +421,13 @@ describe('elementProperty', () => {
/** <div role="button" dir="rtl" myDir></div> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]);
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']);
elementEnd();
}
}
expect(renderToHtml(Template, {})).toEqual(`<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 !.direction).toEqual('rtl');
});
@ -402,16 +437,15 @@ describe('elementProperty', () => {
/** <div role="button" (change)="onChange()" myDir></div> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir]);
elementStart(0, 'div', ['role', 'button', 'myDir', '']);
{ listener('change', ctx.onChange.bind(ctx)); }
elementEnd();
}
}
let counter = 0;
expect(renderToHtml(Template, {
onChange: () => counter++
})).toEqual(`<div role="button"></div>`);
expect(renderToHtml(Template, {onChange: () => counter++}, deps))
.toEqual(`<div mydir="" role="button"></div>`);
expect(myDir !.role).toEqual('button');
myDir !.changeStream.next();
@ -426,15 +460,16 @@ describe('elementProperty', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl'], [MyDir]);
elementStart(0, 'div', ['role', 'button', 'dir', 'rtl', 'myDir', '']);
elementEnd();
elementStart(1, 'div', ['role', 'listbox'], [MyDirB]);
elementStart(1, 'div', ['role', 'listbox', 'myDirB', '']);
elementEnd();
}
}
expect(renderToHtml(Template, {}))
.toEqual(`<div dir="rtl" role="button"></div><div role="listbox"></div>`);
expect(renderToHtml(Template, {}, deps))
.toEqual(
`<div dir="rtl" mydir="" role="button"></div><div mydirb="" role="listbox"></div>`);
expect(myDir !.role).toEqual('button');
expect(myDir !.direction).toEqual('rtl');
expect(dirB !.roleB).toEqual('listbox');
@ -452,7 +487,7 @@ describe('elementProperty', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'listbox'], [MyDir]);
elementStart(0, 'div', ['role', 'listbox', 'myDir', '']);
elementEnd();
container(1);
}
@ -460,7 +495,7 @@ describe('elementProperty', () => {
{
if (ctx.condition) {
if (embeddedViewStart(0)) {
elementStart(0, 'div', ['role', 'button'], [MyDirB]);
elementStart(0, 'div', ['role', 'button', 'myDirB', '']);
elementEnd();
}
embeddedViewEnd();
@ -476,16 +511,14 @@ describe('elementProperty', () => {
containerRefreshEnd();
}
expect(renderToHtml(Template, {
condition: true
})).toEqual(`<div role="listbox"></div><div role="button"></div>`);
expect(renderToHtml(Template, {condition: true}, deps))
.toEqual(`<div mydir="" role="listbox"></div><div mydirb="" role="button"></div>`);
expect(myDir !.role).toEqual('listbox');
expect(dirB !.roleB).toEqual('button');
expect((dirB !as any).role).toBeUndefined();
expect(renderToHtml(Template, {
condition: false
})).toEqual(`<div role="listbox"></div><div role="menu"></div>`);
expect(renderToHtml(Template, {condition: false}, deps))
.toEqual(`<div mydir="" role="listbox"></div><div role="menu"></div>`);
expect(myDir !.role).toEqual('listbox');
});
@ -494,18 +527,19 @@ describe('elementProperty', () => {
class Comp {
static ngComponentDef = defineComponent({
type: Comp,
tag: 'comp',
selector: [[['comp'], null]],
/** <div role="button" dir #dir="myDir"></div> {{ dir.role }} */
template: function(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['role', 'button'], [MyDir], ['dir', 'myDir']);
elementStart(0, 'div', ['role', 'button', 'myDir', ''], ['dir', 'myDir']);
elementEnd();
text(1);
}
// TODO: remove this loadDirective when removing MyDir
textBinding(1, bind(loadDirective<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++) {
if (embeddedViewStart(0)) {
elementStart(0, Comp);
elementStart(0, 'comp');
elementEnd();
}
embeddedViewEnd();
@ -531,9 +565,9 @@ describe('elementProperty', () => {
containerRefreshEnd();
}
expect(renderToHtml(Template, {}))
expect(renderToHtml(Template, {}, [Comp.ngComponentDef]))
.toEqual(
`<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({
type: MyComp,
tag: 'my-comp',
selector: [[['my-comp'], null]],
factory: function MyComp_Factory() { return myComp = new MyComp(); },
template: function MyComp_Template(ctx: MyComp, cm: boolean) {},
inputs: {names: 'names'}
});
}
const defs = [MyComp.ngComponentDef];
it('should support an array literal with a binding', () => {
const e0_ff = (v: any) => ['Nancy', v, 'Bess'];
/** <my-comp [names]="['Nancy', customName, 'Bess']"></my-comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'my-comp');
elementEnd();
}
elementProperty(0, 'names', bind(pureFunction1(e0_ff, ctx.customName)));
}
renderToHtml(Template, {customName: 'Carson'});
renderToHtml(Template, {customName: 'Carson'}, defs);
const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']);
renderToHtml(Template, {customName: 'Carson'});
renderToHtml(Template, {customName: 'Carson'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']);
expect(firstArray).toBe(myComp !.names);
renderToHtml(Template, {customName: 'Hannah'});
renderToHtml(Template, {customName: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Hannah', 'Bess']);
// Identity must change if binding changes
@ -54,7 +56,7 @@ describe('array literals', () => {
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp !.names = ['should not be overwritten'];
renderToHtml(Template, {customName: 'Hannah'});
renderToHtml(Template, {customName: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['should not be overwritten']);
});
@ -67,7 +69,7 @@ describe('array literals', () => {
static ngComponentDef = defineComponent({
type: ManyPropComp,
tag: 'many-prop-comp',
selector: [[['many-prop-comp'], null]],
factory: function ManyPropComp_Factory() { return manyPropComp = new ManyPropComp(); },
template: function ManyPropComp_Template(ctx: ManyPropComp, cm: boolean) {},
inputs: {names1: 'names1', names2: 'names2'}
@ -83,18 +85,19 @@ describe('array literals', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, ManyPropComp);
elementStart(0, 'many-prop-comp');
elementEnd();
}
elementProperty(0, 'names1', bind(pureFunction1(e0_ff, ctx.customName)));
elementProperty(0, 'names2', bind(pureFunction1(e0_ff_1, ctx.customName2)));
}
renderToHtml(Template, {customName: 'Carson', customName2: 'George'});
const defs = [ManyPropComp.ngComponentDef];
renderToHtml(Template, {customName: 'Carson', customName2: 'George'}, defs);
expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp !.names2).toEqual(['George']);
renderToHtml(Template, {customName: 'George', customName2: 'Carson'});
renderToHtml(Template, {customName: 'George', customName2: 'Carson'}, defs);
expect(manyPropComp !.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp !.names2).toEqual(['Carson']);
});
@ -115,36 +118,37 @@ describe('array literals', () => {
static ngComponentDef = defineComponent({
type: ParentComp,
tag: 'parent-comp',
selector: [[['parent-comp'], null]],
factory: () => new ParentComp(),
template: function(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'my-comp');
myComps.push(loadDirective(0));
elementEnd();
}
elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(e0_ff, ctx.customName))));
}
},
directiveDefs: defs
});
}
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, ParentComp);
elementStart(0, 'parent-comp');
elementEnd();
elementStart(1, ParentComp);
elementStart(1, 'parent-comp');
elementEnd();
}
}
renderToHtml(Template, {});
renderToHtml(Template, {}, [ParentComp.ngComponentDef]);
const firstArray = myComps[0].names;
const secondArray = myComps[1].names;
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).not.toBe(secondArray);
renderToHtml(Template, {});
renderToHtml(Template, {}, [ParentComp.ngComponentDef]);
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).toBe(myComps[0].names);
@ -157,31 +161,31 @@ describe('array literals', () => {
/** <my-comp [names]="['Nancy', customName, 'Bess', customName2]"></my-comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'my-comp');
elementEnd();
}
elementProperty(0, 'names', bind(pureFunction2(e0_ff, ctx.customName, ctx.customName2)));
}
renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'});
renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}, defs);
const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'});
renderToHtml(Template, {customName: 'Carson', customName2: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
expect(firstArray).toBe(myComp !.names);
renderToHtml(Template, {customName: 'George', customName2: 'Hannah'});
renderToHtml(Template, {customName: 'George', customName2: 'Hannah'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'George', 'Bess', 'Hannah']);
expect(firstArray).not.toBe(myComp !.names);
renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'});
renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}, defs);
expect(myComp !.names).toEqual(['Nancy', 'Frank', 'Bess', 'Ned']);
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp !.names = ['should not be overwritten'];
renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'});
renderToHtml(Template, {customName: 'Frank', customName2: 'Ned'}, defs);
expect(myComp !.names).toEqual(['should not be overwritten']);
});
@ -210,22 +214,22 @@ describe('array literals', () => {
function Template(c: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'my-comp');
f3Comp = loadDirective(0);
elementEnd();
elementStart(1, MyComp);
elementStart(1, 'my-comp');
f4Comp = loadDirective(1);
elementEnd();
elementStart(2, MyComp);
elementStart(2, 'my-comp');
f5Comp = loadDirective(2);
elementEnd();
elementStart(3, MyComp);
elementStart(3, 'my-comp');
f6Comp = loadDirective(3);
elementEnd();
elementStart(4, MyComp);
elementStart(4, 'my-comp');
f7Comp = loadDirective(4);
elementEnd();
elementStart(5, MyComp);
elementStart(5, 'my-comp');
f8Comp = loadDirective(5);
elementEnd();
}
@ -239,7 +243,7 @@ describe('array literals', () => {
5, 'names', bind(pureFunction8(e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
}
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
@ -247,7 +251,7 @@ describe('array literals', () => {
expect(f7Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f8Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1', 'i1']);
renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1', 'i1'], defs);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h1']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h1']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h1']);
@ -255,7 +259,7 @@ describe('array literals', () => {
expect(f7Comp !.names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(f8Comp !.names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2', 'i1']);
renderToHtml(Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2', 'i1'], defs);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h2']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h2']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h2']);
@ -270,14 +274,14 @@ describe('array literals', () => {
v8: any) => ['start', v0, v1, v2, v3, v4, v5, v6, v7, v8, 'end'];
const e0_ff_1 = (v: any) => { return {name: v}; };
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs);
/**
* <my-comp [names]="['start', v0, v1, v2, v3, {name: v4}, v5, v6, v7, v8, 'end']">
* </my-comp>
*/
function Template(c: any, cm: boolean) {
if (cm) {
elementStart(0, MyComp);
elementStart(0, 'my-comp');
elementEnd();
}
elementProperty(0, 'names', bind(pureFunctionV(e0_ff, [
@ -289,12 +293,12 @@ describe('array literals', () => {
'start', 'a', 'b', 'c', 'd', {name: 'e'}, 'f', 'g', 'h', 'i', 'end'
]);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], defs);
expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', {name: 'e'}, 'f', 'g', 'h', 'i', 'end'
]);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e5', 'f', 'g', 'h', 'i']);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e5', 'f', 'g', 'h', 'i'], defs);
expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', {name: 'e5'}, 'f', 'g', 'h', 'i', 'end'
]);
@ -309,34 +313,36 @@ describe('object literals', () => {
static ngComponentDef = defineComponent({
type: ObjectComp,
tag: 'object-comp',
selector: [[['object-comp'], null]],
factory: function ObjectComp_Factory() { return objectComp = new ObjectComp(); },
template: function ObjectComp_Template(ctx: ObjectComp, cm: boolean) {},
inputs: {config: 'config'}
});
}
const defs = [ObjectComp.ngComponentDef];
it('should support an object literal', () => {
const e0_ff = (v: any) => { return {duration: 500, animation: v}; };
/** <object-comp [config]="{duration: 500, animation: name}"></object-comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, ObjectComp);
elementStart(0, 'object-comp');
elementEnd();
}
elementProperty(0, 'config', bind(pureFunction1(e0_ff, ctx.name)));
}
renderToHtml(Template, {name: 'slide'});
renderToHtml(Template, {name: 'slide'}, defs);
const firstObj = objectComp !.config;
expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'});
renderToHtml(Template, {name: 'slide'});
renderToHtml(Template, {name: 'slide'}, defs);
expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'});
expect(firstObj).toBe(objectComp !.config);
renderToHtml(Template, {name: 'tap'});
renderToHtml(Template, {name: 'tap'}, defs);
expect(objectComp !.config).toEqual({duration: 500, animation: 'tap'});
// Identity must change if binding changes
@ -355,7 +361,7 @@ describe('object literals', () => {
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, ObjectComp);
elementStart(0, 'object-comp');
elementEnd();
}
elementProperty(
@ -364,34 +370,34 @@ describe('object literals', () => {
e0_ff, ctx.name, pureFunction1(e0_ff_1, pureFunction1(e0_ff_2, ctx.duration)))));
}
renderToHtml(Template, {name: 'slide', duration: 100});
renderToHtml(Template, {name: 'slide', duration: 100}, defs);
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
const firstConfig = objectComp !.config;
renderToHtml(Template, {name: 'slide', duration: 100});
renderToHtml(Template, {name: 'slide', duration: 100}, defs);
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
expect(objectComp !.config).toBe(firstConfig);
renderToHtml(Template, {name: 'slide', duration: 50});
renderToHtml(Template, {name: 'slide', duration: 50}, defs);
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
expect(objectComp !.config).not.toBe(firstConfig);
renderToHtml(Template, {name: 'tap', duration: 50});
renderToHtml(Template, {name: 'tap', duration: 50}, defs);
expect(objectComp !.config).toEqual({
animation: 'tap',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
renderToHtml(Template, {name: 'drag', duration: 500});
renderToHtml(Template, {name: 'drag', duration: 500}, defs);
expect(objectComp !.config).toEqual({
animation: 'drag',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 500}]
@ -400,7 +406,7 @@ describe('object literals', () => {
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
objectComp !.config = ['should not be overwritten'];
renderToHtml(Template, {name: 'drag', duration: 500});
renderToHtml(Template, {name: 'drag', duration: 500}, defs);
expect(objectComp !.config).toEqual(['should not be overwritten']);
});
@ -421,7 +427,7 @@ describe('object literals', () => {
{
for (let i = 0; i < 2; i++) {
if (embeddedViewStart(0)) {
elementStart(0, ObjectComp);
elementStart(0, 'object-comp');
objectComps.push(loadDirective(0));
elementEnd();
}
@ -437,12 +443,12 @@ describe('object literals', () => {
const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; };
const configs = [{opacity: 0, duration: 500}, {opacity: 1, duration: 600}];
renderToHtml(Template, {configs});
renderToHtml(Template, {configs}, defs);
expect(objectComps[0].config).toEqual({opacity: 0, duration: 500});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
configs[0].duration = 1000;
renderToHtml(Template, {configs});
renderToHtml(Template, {configs}, defs);
expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
});

View File

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

View File

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

View File

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

View File

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