parent
f20d1a8af5
commit
0adb97bffb
|
@ -18,11 +18,13 @@ export function anchorDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
childFlags: undefined,
|
childFlags: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
|
disposableIndex: undefined,
|
||||||
providerIndices: undefined,
|
providerIndices: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childCount,
|
childCount,
|
||||||
bindings: [],
|
bindings: [],
|
||||||
|
disposableCount: 0,
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: undefined,
|
text: undefined,
|
||||||
|
|
|
@ -8,14 +8,16 @@
|
||||||
|
|
||||||
import {SecurityContext} from '../security';
|
import {SecurityContext} from '../security';
|
||||||
|
|
||||||
import {BindingDef, BindingType, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewFlags} from './types';
|
import {BindingDef, BindingType, DisposableFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewFlags} from './types';
|
||||||
import {checkAndUpdateBinding, setBindingDebugInfo} from './util';
|
import {checkAndUpdateBinding, setBindingDebugInfo} from './util';
|
||||||
|
|
||||||
export function elementDef(
|
export function elementDef(
|
||||||
flags: NodeFlags, childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
|
flags: NodeFlags, childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
|
||||||
bindings: ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] | [
|
bindings?:
|
||||||
BindingType.ElementAttribute | BindingType.ElementProperty, string, SecurityContext
|
([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
||||||
])[] = []): NodeDef {
|
[BindingType.ElementAttribute | BindingType.ElementProperty, string, SecurityContext])[],
|
||||||
|
outputs?: (string | [string, string])[]): NodeDef {
|
||||||
|
bindings = bindings || [];
|
||||||
const bindingDefs = new Array(bindings.length);
|
const bindingDefs = new Array(bindings.length);
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
const entry = bindings[i];
|
const entry = bindings[i];
|
||||||
|
@ -35,6 +37,19 @@ export function elementDef(
|
||||||
}
|
}
|
||||||
bindingDefs[i] = {type: bindingType, name, nonMinfiedName: name, securityContext, suffix};
|
bindingDefs[i] = {type: bindingType, name, nonMinfiedName: name, securityContext, suffix};
|
||||||
}
|
}
|
||||||
|
outputs = outputs || [];
|
||||||
|
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
|
||||||
|
for (let i = 0; i < outputs.length; i++) {
|
||||||
|
const output = outputs[i];
|
||||||
|
let target: string;
|
||||||
|
let eventName: string;
|
||||||
|
if (Array.isArray(output)) {
|
||||||
|
[target, eventName] = output;
|
||||||
|
} else {
|
||||||
|
eventName = output;
|
||||||
|
}
|
||||||
|
outputDefs[i] = {eventName: eventName, target: target};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type: NodeType.Element,
|
type: NodeType.Element,
|
||||||
// will bet set by the view definition
|
// will bet set by the view definition
|
||||||
|
@ -43,12 +58,14 @@ export function elementDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
childFlags: undefined,
|
childFlags: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
|
disposableIndex: undefined,
|
||||||
providerIndices: undefined,
|
providerIndices: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childCount,
|
childCount,
|
||||||
bindings: bindingDefs,
|
bindings: bindingDefs,
|
||||||
element: {name, attrs: fixedAttrs},
|
disposableCount: outputDefs.length,
|
||||||
|
element: {name, attrs: fixedAttrs, outputs: outputDefs},
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: undefined,
|
text: undefined,
|
||||||
component: undefined,
|
component: undefined,
|
||||||
|
@ -62,22 +79,52 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No
|
||||||
let el: any;
|
let el: any;
|
||||||
if (view.renderer) {
|
if (view.renderer) {
|
||||||
el = view.renderer.createElement(parentNode, elDef.name);
|
el = view.renderer.createElement(parentNode, elDef.name);
|
||||||
if (elDef.attrs) {
|
|
||||||
for (let attrName in elDef.attrs) {
|
|
||||||
view.renderer.setElementAttribute(el, attrName, elDef.attrs[attrName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
el = document.createElement(elDef.name);
|
el = document.createElement(elDef.name);
|
||||||
if (parentNode) {
|
if (parentNode) {
|
||||||
parentNode.appendChild(el);
|
parentNode.appendChild(el);
|
||||||
}
|
}
|
||||||
if (elDef.attrs) {
|
}
|
||||||
for (let attrName in elDef.attrs) {
|
if (elDef.attrs) {
|
||||||
|
for (let attrName in elDef.attrs) {
|
||||||
|
if (view.renderer) {
|
||||||
|
view.renderer.setElementAttribute(el, attrName, elDef.attrs[attrName]);
|
||||||
|
} else {
|
||||||
el.setAttribute(attrName, elDef.attrs[attrName]);
|
el.setAttribute(attrName, elDef.attrs[attrName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (elDef.outputs.length) {
|
||||||
|
for (let i = 0; i < elDef.outputs.length; i++) {
|
||||||
|
const output = elDef.outputs[i];
|
||||||
|
let disposable: DisposableFn;
|
||||||
|
if (view.renderer) {
|
||||||
|
const handleEventClosure = renderEventHandlerClosure(view, def.index, output.eventName);
|
||||||
|
if (output.target) {
|
||||||
|
disposable =
|
||||||
|
<any>view.renderer.listenGlobal(output.target, output.eventName, handleEventClosure);
|
||||||
|
} else {
|
||||||
|
disposable = <any>view.renderer.listen(el, output.eventName, handleEventClosure);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let target: any;
|
||||||
|
switch (output.target) {
|
||||||
|
case 'window':
|
||||||
|
target = window;
|
||||||
|
break;
|
||||||
|
case 'document':
|
||||||
|
target = document;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
target = el;
|
||||||
|
}
|
||||||
|
const handleEventClosure = directDomEventHandlerClosure(view, def.index, output.eventName);
|
||||||
|
target.addEventListener(output.eventName, handleEventClosure);
|
||||||
|
disposable = target.removeEventListener.bind(target, output.eventName, handleEventClosure);
|
||||||
|
}
|
||||||
|
view.disposables[def.disposableIndex + i] = disposable;
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
renderNode: el,
|
renderNode: el,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
|
@ -86,6 +133,21 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||||
|
return (event: any) => { return view.def.handleEvent(view, index, eventName, event); };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function directDomEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||||
|
return (event: any) => {
|
||||||
|
const result = view.def.handleEvent(view, index, eventName, event);
|
||||||
|
if (result === false) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function checkAndUpdateElementInline(
|
export function checkAndUpdateElementInline(
|
||||||
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
||||||
v7: any, v8: any, v9: any) {
|
v7: any, v8: any, v9: any) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {Renderer} from '../render/api';
|
import {Renderer} from '../render/api';
|
||||||
|
|
||||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeFlags, NodeType, Services, ViewData, ViewDefinition, ViewFlags} from './types';
|
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderOutputDef, Services, ViewData, ViewDefinition, ViewFlags} from './types';
|
||||||
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, setBindingDebugInfo} from './util';
|
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, setBindingDebugInfo} from './util';
|
||||||
|
|
||||||
const _tokenKeyCache = new Map<any, string>();
|
const _tokenKeyCache = new Map<any, string>();
|
||||||
|
@ -26,7 +26,8 @@ const TemplateRefTokenKey = tokenKey(TemplateRef);
|
||||||
|
|
||||||
export function providerDef(
|
export function providerDef(
|
||||||
flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[],
|
flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[],
|
||||||
props?: {[name: string]: [number, string]}, component?: () => ViewDefinition): NodeDef {
|
props?: {[name: string]: [number, string]}, outputs?: {[name: string]: string},
|
||||||
|
component?: () => ViewDefinition): NodeDef {
|
||||||
const bindings: BindingDef[] = [];
|
const bindings: BindingDef[] = [];
|
||||||
if (props) {
|
if (props) {
|
||||||
for (let prop in props) {
|
for (let prop in props) {
|
||||||
|
@ -39,6 +40,12 @@ export function providerDef(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const outputDefs: ProviderOutputDef[] = [];
|
||||||
|
if (outputs) {
|
||||||
|
for (let propName in outputs) {
|
||||||
|
outputDefs.push({propName, eventName: outputs[propName]});
|
||||||
|
}
|
||||||
|
}
|
||||||
const depDefs: DepDef[] = deps.map(value => {
|
const depDefs: DepDef[] = deps.map(value => {
|
||||||
let token: any;
|
let token: any;
|
||||||
let flags: DepFlags;
|
let flags: DepFlags;
|
||||||
|
@ -61,16 +68,14 @@ export function providerDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
childFlags: undefined,
|
childFlags: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
|
disposableIndex: undefined,
|
||||||
providerIndices: undefined,
|
providerIndices: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childCount: 0, bindings,
|
childCount: 0, bindings,
|
||||||
|
disposableCount: outputDefs.length,
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: {
|
provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs},
|
||||||
tokenKey: tokenKey(ctor),
|
|
||||||
ctor,
|
|
||||||
deps: depDefs,
|
|
||||||
},
|
|
||||||
text: undefined, component,
|
text: undefined, component,
|
||||||
template: undefined
|
template: undefined
|
||||||
};
|
};
|
||||||
|
@ -87,10 +92,19 @@ export function tokenKey(token: any): string {
|
||||||
|
|
||||||
export function createProvider(view: ViewData, def: NodeDef, componentView: ViewData): NodeData {
|
export function createProvider(view: ViewData, def: NodeDef, componentView: ViewData): NodeData {
|
||||||
const providerDef = def.provider;
|
const providerDef = def.provider;
|
||||||
|
const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps);
|
||||||
|
if (providerDef.outputs.length) {
|
||||||
|
for (let i = 0; i < providerDef.outputs.length; i++) {
|
||||||
|
const output = providerDef.outputs[i];
|
||||||
|
const subscription = provider[output.propName].subscribe(
|
||||||
|
view.def.handleEvent.bind(null, view, def.parent, output.eventName));
|
||||||
|
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
renderNode: undefined,
|
renderNode: undefined,
|
||||||
provider: createInstance(view, def.parent, providerDef.ctor, providerDef.deps),
|
provider,
|
||||||
embeddedViews: undefined, componentView
|
embeddedViews: undefined, componentView,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,12 @@ export function textDef(constants: string[]): NodeDef {
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
childFlags: undefined,
|
childFlags: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
|
disposableIndex: undefined,
|
||||||
providerIndices: undefined,
|
providerIndices: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags: 0,
|
flags: 0,
|
||||||
childCount: 0, bindings,
|
childCount: 0, bindings,
|
||||||
|
disposableCount: 0,
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: {prefix: constants[0]},
|
text: {prefix: constants[0]},
|
||||||
|
|
|
@ -19,6 +19,7 @@ export interface ViewDefinition {
|
||||||
flags: ViewFlags;
|
flags: ViewFlags;
|
||||||
componentType: RenderComponentType;
|
componentType: RenderComponentType;
|
||||||
update: ViewUpdateFn;
|
update: ViewUpdateFn;
|
||||||
|
handleEvent: ViewHandleEventFn;
|
||||||
/**
|
/**
|
||||||
* Order: Depth first.
|
* Order: Depth first.
|
||||||
* Especially providers are before elements / anchros.
|
* Especially providers are before elements / anchros.
|
||||||
|
@ -33,10 +34,10 @@ export interface ViewDefinition {
|
||||||
reverseChildNodes: NodeDef[];
|
reverseChildNodes: NodeDef[];
|
||||||
lastRootNode: number;
|
lastRootNode: number;
|
||||||
bindingCount: number;
|
bindingCount: number;
|
||||||
|
disposableCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewUpdateFn = (updater: NodeUpdater, view: ViewData, component: any, context: any) =>
|
export type ViewUpdateFn = (updater: NodeUpdater, view: ViewData) => void;
|
||||||
void;
|
|
||||||
|
|
||||||
export interface NodeUpdater {
|
export interface NodeUpdater {
|
||||||
checkInline(
|
checkInline(
|
||||||
|
@ -45,6 +46,9 @@ export interface NodeUpdater {
|
||||||
checkDynamic(view: ViewData, nodeIndex: number, values: any[]): void;
|
checkDynamic(view: ViewData, nodeIndex: number, values: any[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ViewHandleEventFn =
|
||||||
|
(view: ViewData, nodeIndex: number, eventName: string, event: any) => boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitmask for ViewDefintion.flags.
|
* Bitmask for ViewDefintion.flags.
|
||||||
*/
|
*/
|
||||||
|
@ -66,6 +70,8 @@ export interface NodeDef {
|
||||||
childFlags: NodeFlags;
|
childFlags: NodeFlags;
|
||||||
bindingIndex: number;
|
bindingIndex: number;
|
||||||
bindings: BindingDef[];
|
bindings: BindingDef[];
|
||||||
|
disposableIndex: number;
|
||||||
|
disposableCount: number;
|
||||||
element: ElementDef;
|
element: ElementDef;
|
||||||
providerIndices: {[tokenKey: string]: number};
|
providerIndices: {[tokenKey: string]: number};
|
||||||
provider: ProviderDef;
|
provider: ProviderDef;
|
||||||
|
@ -102,6 +108,12 @@ export enum NodeFlags {
|
||||||
export interface ElementDef {
|
export interface ElementDef {
|
||||||
name: string;
|
name: string;
|
||||||
attrs: {[name: string]: string};
|
attrs: {[name: string]: string};
|
||||||
|
outputs: ElementOutputDef[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElementOutputDef {
|
||||||
|
target: string;
|
||||||
|
eventName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,10 +130,16 @@ export interface DepDef {
|
||||||
tokenKey: string;
|
tokenKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProviderOutputDef {
|
||||||
|
propName: string;
|
||||||
|
eventName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProviderDef {
|
export interface ProviderDef {
|
||||||
tokenKey: string;
|
tokenKey: string;
|
||||||
ctor: any;
|
ctor: any;
|
||||||
deps: DepDef[];
|
deps: DepDef[];
|
||||||
|
outputs: ProviderOutputDef[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextDef { prefix: string; }
|
export interface TextDef { prefix: string; }
|
||||||
|
@ -164,8 +182,11 @@ export interface ViewData {
|
||||||
nodes: NodeData[];
|
nodes: NodeData[];
|
||||||
firstChange: boolean;
|
firstChange: boolean;
|
||||||
oldValues: any[];
|
oldValues: any[];
|
||||||
|
disposables: DisposableFn[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DisposableFn = () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node instance data.
|
* Node instance data.
|
||||||
* Attention: Adding fields to this is performance sensitive!
|
* Attention: Adding fields to this is performance sensitive!
|
||||||
|
|
|
@ -13,14 +13,14 @@ import {createAnchor} from './anchor';
|
||||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider} from './provider';
|
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider} from './provider';
|
||||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||||
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn} from './types';
|
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types';
|
||||||
import {checkBindingNoChanges} from './util';
|
import {checkBindingNoChanges} from './util';
|
||||||
|
|
||||||
const NOOP_UPDATE = (): any => undefined;
|
const NOOP = (): any => undefined;
|
||||||
|
|
||||||
export function viewDef(
|
export function viewDef(
|
||||||
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
|
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
|
||||||
componentType?: RenderComponentType): ViewDefinition {
|
handleEvent?: ViewHandleEventFn, componentType?: RenderComponentType): ViewDefinition {
|
||||||
// clone nodes and set auto calculated values
|
// clone nodes and set auto calculated values
|
||||||
if (nodesWithoutIndices.length === 0) {
|
if (nodesWithoutIndices.length === 0) {
|
||||||
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
||||||
|
@ -28,6 +28,7 @@ export function viewDef(
|
||||||
const nodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
const nodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
||||||
const reverseChildNodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
const reverseChildNodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
||||||
let viewBindingCount = 0;
|
let viewBindingCount = 0;
|
||||||
|
let viewDisposableCount = 0;
|
||||||
let viewFlags = 0;
|
let viewFlags = 0;
|
||||||
let currentParent: NodeDef = null;
|
let currentParent: NodeDef = null;
|
||||||
let lastRootNode: NodeDef = null;
|
let lastRootNode: NodeDef = null;
|
||||||
|
@ -44,7 +45,8 @@ export function viewDef(
|
||||||
const node = cloneAndModifyNode(nodesWithoutIndices[i], {
|
const node = cloneAndModifyNode(nodesWithoutIndices[i], {
|
||||||
index: i,
|
index: i,
|
||||||
parent: currentParent ? currentParent.index : undefined,
|
parent: currentParent ? currentParent.index : undefined,
|
||||||
bindingIndex: viewBindingCount, reverseChildIndex,
|
bindingIndex: viewBindingCount,
|
||||||
|
disposableIndex: viewDisposableCount, reverseChildIndex,
|
||||||
providerIndices: Object.create(currentParent ? currentParent.providerIndices : null)
|
providerIndices: Object.create(currentParent ? currentParent.providerIndices : null)
|
||||||
});
|
});
|
||||||
nodes[i] = node;
|
nodes[i] = node;
|
||||||
|
@ -53,6 +55,7 @@ export function viewDef(
|
||||||
|
|
||||||
viewFlags |= node.flags;
|
viewFlags |= node.flags;
|
||||||
viewBindingCount += node.bindings.length;
|
viewBindingCount += node.bindings.length;
|
||||||
|
viewDisposableCount += node.disposableCount;
|
||||||
if (currentParent) {
|
if (currentParent) {
|
||||||
currentParent.childFlags |= node.flags;
|
currentParent.childFlags |= node.flags;
|
||||||
}
|
}
|
||||||
|
@ -72,8 +75,10 @@ export function viewDef(
|
||||||
nodeFlags: viewFlags,
|
nodeFlags: viewFlags,
|
||||||
flags,
|
flags,
|
||||||
nodes: nodes, reverseChildNodes,
|
nodes: nodes, reverseChildNodes,
|
||||||
update: update || NOOP_UPDATE, componentType,
|
update: update || NOOP,
|
||||||
|
handleEvent: handleEvent || NOOP, componentType,
|
||||||
bindingCount: viewBindingCount,
|
bindingCount: viewBindingCount,
|
||||||
|
disposableCount: viewDisposableCount,
|
||||||
lastRootNode: lastRootNode.index
|
lastRootNode: lastRootNode.index
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -148,6 +153,7 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
||||||
reverseChildIndex: number,
|
reverseChildIndex: number,
|
||||||
parent: number,
|
parent: number,
|
||||||
bindingIndex: number,
|
bindingIndex: number,
|
||||||
|
disposableIndex: number,
|
||||||
providerIndices: {[tokenKey: string]: number}
|
providerIndices: {[tokenKey: string]: number}
|
||||||
}): NodeDef {
|
}): NodeDef {
|
||||||
const clonedNode: NodeDef = <any>{};
|
const clonedNode: NodeDef = <any>{};
|
||||||
|
@ -157,6 +163,7 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
||||||
|
|
||||||
clonedNode.index = values.index;
|
clonedNode.index = values.index;
|
||||||
clonedNode.bindingIndex = values.bindingIndex;
|
clonedNode.bindingIndex = values.bindingIndex;
|
||||||
|
clonedNode.disposableIndex = values.disposableIndex;
|
||||||
clonedNode.parent = values.parent;
|
clonedNode.parent = values.parent;
|
||||||
clonedNode.reverseChildIndex = values.reverseChildIndex;
|
clonedNode.reverseChildIndex = values.reverseChildIndex;
|
||||||
clonedNode.providerIndices = values.providerIndices;
|
clonedNode.providerIndices = values.providerIndices;
|
||||||
|
@ -188,6 +195,7 @@ function createView(
|
||||||
} else {
|
} else {
|
||||||
renderer = def.componentType ? services.renderComponent(def.componentType) : parent.renderer;
|
renderer = def.componentType ? services.renderComponent(def.componentType) : parent.renderer;
|
||||||
}
|
}
|
||||||
|
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
|
||||||
const view: ViewData = {
|
const view: ViewData = {
|
||||||
def,
|
def,
|
||||||
parent,
|
parent,
|
||||||
|
@ -195,7 +203,7 @@ function createView(
|
||||||
context: undefined,
|
context: undefined,
|
||||||
component: undefined, nodes,
|
component: undefined, nodes,
|
||||||
firstChange: true, renderer, services,
|
firstChange: true, renderer, services,
|
||||||
oldValues: new Array(def.bindingCount)
|
oldValues: new Array(def.bindingCount), disposables
|
||||||
};
|
};
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -232,7 +240,7 @@ function initView(view: ViewData, renderHost: any, component: any, context: any)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkNoChangesView(view: ViewData) {
|
export function checkNoChangesView(view: ViewData) {
|
||||||
view.def.update(CheckNoChanges, view, view.component, view.context);
|
view.def.update(CheckNoChanges, view);
|
||||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
}
|
}
|
||||||
|
@ -274,7 +282,7 @@ const CheckNoChanges: NodeUpdater = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function checkAndUpdateView(view: ViewData) {
|
export function checkAndUpdateView(view: ViewData) {
|
||||||
view.def.update(CheckAndUpdate, view, view.component, view.context);
|
view.def.update(CheckAndUpdate, view);
|
||||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||||
|
|
||||||
callLifecycleHooksChildrenFirst(
|
callLifecycleHooksChildrenFirst(
|
||||||
|
@ -320,6 +328,11 @@ const CheckAndUpdate: NodeUpdater = {
|
||||||
|
|
||||||
export function destroyView(view: ViewData) {
|
export function destroyView(view: ViewData) {
|
||||||
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
||||||
|
if (view.disposables) {
|
||||||
|
for (let i = 0; i < view.disposables.length; i++) {
|
||||||
|
view.disposables[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
execComponentViewsAction(view, ViewAction.Destroy);
|
execComponentViewsAction(view, ViewAction.Destroy);
|
||||||
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,8 +34,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,8 +34,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
|
@ -45,56 +46,57 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should create and attach component views', () => {
|
it('should create and attach component views', () => {
|
||||||
class AComp {}
|
let instance: AComp;
|
||||||
|
class AComp {
|
||||||
|
constructor() { instance = this; }
|
||||||
|
}
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, 1, 'div'),
|
elementDef(NodeFlags.None, 1, 'div'),
|
||||||
providerDef(NodeFlags.None, AComp, [], null, () => compViewDef([
|
providerDef(NodeFlags.None, AComp, [], null, null, () => compViewDef([
|
||||||
elementDef(NodeFlags.None, 0, 'span'),
|
elementDef(NodeFlags.None, 0, 'span'),
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
const compView = view.nodes[1].componentView;
|
||||||
|
|
||||||
|
expect(compView.context).toBe(instance);
|
||||||
|
expect(compView.component).toBe(instance);
|
||||||
|
|
||||||
const compRootEl = getDOM().childNodes(rootNodes[0])[0];
|
const compRootEl = getDOM().childNodes(rootNodes[0])[0];
|
||||||
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dirty check component views', () => {
|
it('should dirty check component views', () => {
|
||||||
let value = 'v1';
|
let value = 'v1';
|
||||||
let instance: AComp;
|
|
||||||
class AComp {
|
class AComp {
|
||||||
a: any;
|
a: any;
|
||||||
constructor() { instance = this; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updater = jasmine.createSpy('updater').and.callFake(
|
const update = jasmine.createSpy('updater').and.callFake(
|
||||||
(updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, value));
|
(updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, value));
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, 1, 'div'),
|
elementDef(NodeFlags.None, 1, 'div'),
|
||||||
providerDef(NodeFlags.None, AComp, [], null, () => compViewDef(
|
providerDef(NodeFlags.None, AComp, [], null, null, () => compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
elementDef(NodeFlags.None, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||||
], updater
|
], update
|
||||||
)),
|
)),
|
||||||
], jasmine.createSpy('parentUpdater')));
|
], jasmine.createSpy('parentUpdater')));
|
||||||
|
const compView = view.nodes[1].componentView;
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(updater).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
// component
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
expect(updater.calls.mostRecent().args[2]).toBe(instance);
|
|
||||||
// view context
|
|
||||||
expect(updater.calls.mostRecent().args[3]).toBe(instance);
|
|
||||||
|
|
||||||
updater.calls.reset();
|
update.calls.reset();
|
||||||
checkNoChangesView(view);
|
checkNoChangesView(view);
|
||||||
|
|
||||||
expect(updater).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
// component
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
expect(updater.calls.mostRecent().args[2]).toBe(instance);
|
|
||||||
// view context
|
|
||||||
expect(updater.calls.mostRecent().args[3]).toBe(instance);
|
|
||||||
|
|
||||||
value = 'v2';
|
value = 'v2';
|
||||||
expect(() => checkNoChangesView(view))
|
expect(() => checkNoChangesView(view))
|
||||||
|
@ -114,10 +116,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, 1, 'div'),
|
elementDef(NodeFlags.None, 1, 'div'),
|
||||||
providerDef(
|
providerDef(
|
||||||
NodeFlags.None, AComp, [], null, () => compViewDef([
|
NodeFlags.None, AComp, [], null, null,
|
||||||
elementDef(NodeFlags.None, 1, 'span'),
|
() => compViewDef([
|
||||||
providerDef(NodeFlags.OnDestroy, ChildProvider, [])
|
elementDef(NodeFlags.None, 1, 'span'),
|
||||||
])),
|
providerDef(NodeFlags.OnDestroy, ChildProvider, [])
|
||||||
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
destroyView(view);
|
destroyView(view);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,8 +34,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
|
@ -101,12 +102,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe('change properties', () => {
|
describe('change properties', () => {
|
||||||
[{
|
[{
|
||||||
name: 'inline',
|
name: 'inline',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'v1', 'v2')
|
update: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'v1', 'v2')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dynamic',
|
name: 'dynamic',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) =>
|
update: (updater: NodeUpdater, view: ViewData) =>
|
||||||
updater.checkDynamic(view, 0, ['v1', 'v2'])
|
updater.checkDynamic(view, 0, ['v1', 'v2'])
|
||||||
}].forEach((config) => {
|
}].forEach((config) => {
|
||||||
it(`should update ${config.name}`, () => {
|
it(`should update ${config.name}`, () => {
|
||||||
|
|
||||||
|
@ -119,7 +120,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
config.updater));
|
config.update));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -133,12 +134,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe('change attributes', () => {
|
describe('change attributes', () => {
|
||||||
[{
|
[{
|
||||||
name: 'inline',
|
name: 'inline',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'v1', 'v2')
|
update: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'v1', 'v2')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dynamic',
|
name: 'dynamic',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) =>
|
update: (updater: NodeUpdater, view: ViewData) =>
|
||||||
updater.checkDynamic(view, 0, ['v1', 'v2'])
|
updater.checkDynamic(view, 0, ['v1', 'v2'])
|
||||||
}].forEach((config) => {
|
}].forEach((config) => {
|
||||||
it(`should update ${config.name}`, () => {
|
it(`should update ${config.name}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
@ -150,7 +151,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
config.updater));
|
config.update));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -192,12 +193,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe('change styles', () => {
|
describe('change styles', () => {
|
||||||
[{
|
[{
|
||||||
name: 'inline',
|
name: 'inline',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 10, 'red')
|
update: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 10, 'red')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dynamic',
|
name: 'dynamic',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) =>
|
update: (updater: NodeUpdater, view: ViewData) =>
|
||||||
updater.checkDynamic(view, 0, [10, 'red'])
|
updater.checkDynamic(view, 0, [10, 'red'])
|
||||||
}].forEach((config) => {
|
}].forEach((config) => {
|
||||||
it(`should update ${config.name}`, () => {
|
it(`should update ${config.name}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
@ -209,7 +210,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementStyle, 'color', null]
|
[BindingType.ElementStyle, 'color', null]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
config.updater));
|
config.update));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -219,5 +220,131 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (getDOM().supportsDOMEvents()) {
|
||||||
|
describe('listen to DOM events', () => {
|
||||||
|
let removeNodes: Node[];
|
||||||
|
beforeEach(() => { removeNodes = []; });
|
||||||
|
afterEach(() => {
|
||||||
|
removeNodes.forEach((node) => {
|
||||||
|
if (node.parentNode) {
|
||||||
|
node.parentNode.removeChild(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createAndAttachAndGetRootNodes(viewDef: ViewDefinition):
|
||||||
|
{rootNodes: any[], view: ViewData} {
|
||||||
|
const result = createAndGetRootNodes(viewDef);
|
||||||
|
// Note: We need to append the node to the document.body, otherwise `click` events
|
||||||
|
// won't work in IE.
|
||||||
|
result.rootNodes.forEach((node) => {
|
||||||
|
document.body.appendChild(node);
|
||||||
|
removeNodes.push(node);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should listen to DOM events', () => {
|
||||||
|
const handleEventSpy = jasmine.createSpy('handleEvent');
|
||||||
|
const removeListenerSpy =
|
||||||
|
spyOn(HTMLElement.prototype, 'removeEventListener').and.callThrough();
|
||||||
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||||
|
[elementDef(NodeFlags.None, 0, 'button', null, null, ['click'])], null,
|
||||||
|
handleEventSpy));
|
||||||
|
|
||||||
|
rootNodes[0].click();
|
||||||
|
|
||||||
|
expect(handleEventSpy).toHaveBeenCalled();
|
||||||
|
let handleEventArgs = handleEventSpy.calls.mostRecent().args;
|
||||||
|
expect(handleEventArgs[0]).toBe(view);
|
||||||
|
expect(handleEventArgs[1]).toBe(0);
|
||||||
|
expect(handleEventArgs[2]).toBe('click');
|
||||||
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
|
destroyView(view);
|
||||||
|
|
||||||
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to window events', () => {
|
||||||
|
const handleEventSpy = jasmine.createSpy('handleEvent');
|
||||||
|
const addListenerSpy = spyOn(window, 'addEventListener');
|
||||||
|
const removeListenerSpy = spyOn(window, 'removeEventListener');
|
||||||
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||||
|
[elementDef(NodeFlags.None, 0, 'button', null, null, [['window', 'windowClick']])],
|
||||||
|
null, handleEventSpy));
|
||||||
|
|
||||||
|
expect(addListenerSpy).toHaveBeenCalled();
|
||||||
|
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('windowClick');
|
||||||
|
addListenerSpy.calls.mostRecent().args[1]({name: 'windowClick'});
|
||||||
|
|
||||||
|
expect(handleEventSpy).toHaveBeenCalled();
|
||||||
|
const handleEventArgs = handleEventSpy.calls.mostRecent().args;
|
||||||
|
expect(handleEventArgs[0]).toBe(view);
|
||||||
|
expect(handleEventArgs[1]).toBe(0);
|
||||||
|
expect(handleEventArgs[2]).toBe('windowClick');
|
||||||
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
|
destroyView(view);
|
||||||
|
|
||||||
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to document events', () => {
|
||||||
|
const handleEventSpy = jasmine.createSpy('handleEvent');
|
||||||
|
const addListenerSpy = spyOn(document, 'addEventListener');
|
||||||
|
const removeListenerSpy = spyOn(document, 'removeEventListener');
|
||||||
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||||
|
[elementDef(
|
||||||
|
NodeFlags.None, 0, 'button', null, null, [['document', 'documentClick']])],
|
||||||
|
null, handleEventSpy));
|
||||||
|
|
||||||
|
expect(addListenerSpy).toHaveBeenCalled();
|
||||||
|
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('documentClick');
|
||||||
|
addListenerSpy.calls.mostRecent().args[1]({name: 'documentClick'});
|
||||||
|
|
||||||
|
expect(handleEventSpy).toHaveBeenCalled();
|
||||||
|
const handleEventArgs = handleEventSpy.calls.mostRecent().args;
|
||||||
|
expect(handleEventArgs[0]).toBe(view);
|
||||||
|
expect(handleEventArgs[1]).toBe(0);
|
||||||
|
expect(handleEventArgs[2]).toBe('documentClick');
|
||||||
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
|
destroyView(view);
|
||||||
|
|
||||||
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preventDefault only if the handler returns false', () => {
|
||||||
|
let eventHandlerResult: any;
|
||||||
|
let preventDefaultSpy: jasmine.Spy;
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||||
|
[elementDef(NodeFlags.None, 0, 'button', null, null, ['click'])], null,
|
||||||
|
(view, index, eventName, event) => {
|
||||||
|
preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough();
|
||||||
|
return eventHandlerResult;
|
||||||
|
}));
|
||||||
|
|
||||||
|
eventHandlerResult = undefined;
|
||||||
|
rootNodes[0].click();
|
||||||
|
expect(preventDefaultSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
eventHandlerResult = true;
|
||||||
|
rootNodes[0].click();
|
||||||
|
expect(preventDefaultSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
eventHandlerResult = 'someString';
|
||||||
|
rootNodes[0].click();
|
||||||
|
expect(preventDefaultSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
eventHandlerResult = false;
|
||||||
|
rootNodes[0].click();
|
||||||
|
expect(preventDefaultSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,12 +34,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, updater);
|
return viewDef(config.viewFlags, nodes, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
|
@ -49,6 +50,23 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('should create embedded views with the right context', () => {
|
||||||
|
const parentContext = new Object();
|
||||||
|
const childContext = new Object();
|
||||||
|
|
||||||
|
const {view: parentView, rootNodes} = createAndGetRootNodes(
|
||||||
|
compViewDef([
|
||||||
|
elementDef(NodeFlags.None, 2, 'div'),
|
||||||
|
anchorDef(NodeFlags.HasEmbeddedViews, 0, embeddedViewDef([elementDef(
|
||||||
|
NodeFlags.None, 0, 'span')])),
|
||||||
|
]),
|
||||||
|
parentContext);
|
||||||
|
|
||||||
|
const childView = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||||
|
expect(childView.component).toBe(parentContext);
|
||||||
|
expect(childView.context).toBe(childContext);
|
||||||
|
});
|
||||||
|
|
||||||
it('should attach and detach embedded views', () => {
|
it('should attach and detach embedded views', () => {
|
||||||
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, 2, 'div'),
|
elementDef(NodeFlags.None, 2, 'div'),
|
||||||
|
@ -97,45 +115,35 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
it('should dirty check embedded views', () => {
|
it('should dirty check embedded views', () => {
|
||||||
let childValue = 'v1';
|
let childValue = 'v1';
|
||||||
const parentContext = new Object();
|
const update = jasmine.createSpy('updater').and.callFake(
|
||||||
const childContext = new Object();
|
|
||||||
const updater = jasmine.createSpy('updater').and.callFake(
|
|
||||||
(updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, childValue));
|
(updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, childValue));
|
||||||
|
|
||||||
const {view: parentView, rootNodes} = createAndGetRootNodes(
|
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
compViewDef([
|
elementDef(NodeFlags.None, 1, 'div'),
|
||||||
elementDef(NodeFlags.None, 1, 'div'),
|
anchorDef(
|
||||||
anchorDef(
|
NodeFlags.HasEmbeddedViews, 0,
|
||||||
NodeFlags.HasEmbeddedViews, 0,
|
embeddedViewDef(
|
||||||
embeddedViewDef(
|
[elementDef(
|
||||||
[elementDef(
|
NodeFlags.None, 0, 'span', null,
|
||||||
NodeFlags.None, 0, 'span', null,
|
[[BindingType.ElementAttribute, 'name', SecurityContext.NONE]])],
|
||||||
[[BindingType.ElementAttribute, 'name', SecurityContext.NONE]])],
|
update))
|
||||||
updater))
|
]));
|
||||||
]),
|
|
||||||
parentContext);
|
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
const rootEl = rootNodes[0];
|
const rootEl = rootNodes[0];
|
||||||
attachEmbeddedView(parentView.nodes[1], 0, childView0);
|
attachEmbeddedView(parentView.nodes[1], 0, childView0);
|
||||||
|
|
||||||
checkAndUpdateView(parentView);
|
checkAndUpdateView(parentView);
|
||||||
|
|
||||||
expect(updater).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
// component
|
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||||
expect(updater.calls.mostRecent().args[2]).toBe(parentContext);
|
|
||||||
// view context
|
|
||||||
expect(updater.calls.mostRecent().args[3]).toBe(childContext);
|
|
||||||
|
|
||||||
updater.calls.reset();
|
update.calls.reset();
|
||||||
checkNoChangesView(parentView);
|
checkNoChangesView(parentView);
|
||||||
|
|
||||||
expect(updater).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
// component
|
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||||
expect(updater.calls.mostRecent().args[2]).toBe(parentContext);
|
|
||||||
// view context
|
|
||||||
expect(updater.calls.mostRecent().args[3]).toBe(childContext);
|
|
||||||
|
|
||||||
childValue = 'v2';
|
childValue = 'v2';
|
||||||
expect(() => checkNoChangesView(parentView))
|
expect(() => checkNoChangesView(parentView))
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, ElementRef, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, ElementRef, EventEmitter, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,12 +34,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, updater);
|
return viewDef(config.viewFlags, nodes, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
|
@ -110,10 +111,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
createAndGetRootNodes(compViewDef([
|
createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, 1, 'div'),
|
elementDef(NodeFlags.None, 1, 'div'),
|
||||||
providerDef(
|
providerDef(
|
||||||
NodeFlags.None, Dep, [], null, () => compViewDef([
|
NodeFlags.None, Dep, [], null, null,
|
||||||
elementDef(NodeFlags.None, 1, 'span'),
|
() => compViewDef([
|
||||||
providerDef(NodeFlags.None, SomeService, [Dep])
|
elementDef(NodeFlags.None, 1, 'span'),
|
||||||
])),
|
providerDef(NodeFlags.None, SomeService, [Dep])
|
||||||
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
expect(instance.dep instanceof Dep).toBeTruthy();
|
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||||
|
@ -173,12 +175,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe('data binding', () => {
|
describe('data binding', () => {
|
||||||
[{
|
[{
|
||||||
name: 'inline',
|
name: 'inline',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 1, 'v1', 'v2')
|
update: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 1, 'v1', 'v2')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dynamic',
|
name: 'dynamic',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) =>
|
update: (updater: NodeUpdater, view: ViewData) =>
|
||||||
updater.checkDynamic(view, 1, ['v1', 'v2'])
|
updater.checkDynamic(view, 1, ['v1', 'v2'])
|
||||||
}].forEach((config) => {
|
}].forEach((config) => {
|
||||||
it(`should update ${config.name}`, () => {
|
it(`should update ${config.name}`, () => {
|
||||||
let instance: SomeService;
|
let instance: SomeService;
|
||||||
|
@ -194,7 +196,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, 1, 'span'),
|
elementDef(NodeFlags.None, 1, 'span'),
|
||||||
providerDef(NodeFlags.None, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
providerDef(NodeFlags.None, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
||||||
],
|
],
|
||||||
config.updater));
|
config.update));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -226,6 +228,39 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('outputs', () => {
|
||||||
|
it('should listen to provider events', () => {
|
||||||
|
let emitter = new EventEmitter<any>();
|
||||||
|
let unsubscribeSpy: any;
|
||||||
|
|
||||||
|
class SomeService {
|
||||||
|
emitter = {
|
||||||
|
subscribe: (callback: any) => {
|
||||||
|
const subscription = emitter.subscribe(callback);
|
||||||
|
unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough();
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEvent = jasmine.createSpy('handleEvent');
|
||||||
|
const subscribe = spyOn(emitter, 'subscribe').and.callThrough();
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
[
|
||||||
|
elementDef(NodeFlags.None, 1, 'span'),
|
||||||
|
providerDef(NodeFlags.None, SomeService, [], null, {emitter: 'someEventName'})
|
||||||
|
],
|
||||||
|
null, handleEvent));
|
||||||
|
|
||||||
|
emitter.emit('someEventInstance');
|
||||||
|
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
|
||||||
|
|
||||||
|
destroyView(view);
|
||||||
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('lifecycle hooks', () => {
|
describe('lifecycle hooks', () => {
|
||||||
it('should call the lifecycle hooks in the right order', () => {
|
it('should call the lifecycle hooks in the right order', () => {
|
||||||
let instanceCount = 0;
|
let instanceCount = 0;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -34,8 +34,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compViewDef(nodes: NodeDef[], updater?: ViewUpdateFn): ViewDefinition {
|
function compViewDef(
|
||||||
return viewDef(config.viewFlags, nodes, updater, renderComponentType);
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
|
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
|
@ -88,19 +89,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe('change text', () => {
|
describe('change text', () => {
|
||||||
[{
|
[{
|
||||||
name: 'inline',
|
name: 'inline',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'a', 'b')
|
update: (updater: NodeUpdater, view: ViewData) => updater.checkInline(view, 0, 'a', 'b')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dynamic',
|
name: 'dynamic',
|
||||||
updater: (updater: NodeUpdater, view: ViewData) =>
|
update: (updater: NodeUpdater, view: ViewData) =>
|
||||||
updater.checkDynamic(view, 0, ['a', 'b'])
|
updater.checkDynamic(view, 0, ['a', 'b'])
|
||||||
}].forEach((config) => {
|
}].forEach((config) => {
|
||||||
it(`should update ${config.name}`, () => {
|
it(`should update ${config.name}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
textDef(['0', '1', '2']),
|
textDef(['0', '1', '2']),
|
||||||
],
|
],
|
||||||
config.updater));
|
config.update));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,18 @@ let viewFlags = ViewFlags.DirectDom;
|
||||||
|
|
||||||
const TreeComponent_Host: ViewDefinition = viewDef(viewFlags, [
|
const TreeComponent_Host: ViewDefinition = viewDef(viewFlags, [
|
||||||
elementDef(NodeFlags.None, 1, 'tree'),
|
elementDef(NodeFlags.None, 1, 'tree'),
|
||||||
providerDef(NodeFlags.None, TreeComponent, [], null, () => TreeComponent_0),
|
providerDef(NodeFlags.None, TreeComponent, [], null, null, () => TreeComponent_0),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const TreeComponent_1: ViewDefinition = viewDef(
|
const TreeComponent_1: ViewDefinition = viewDef(
|
||||||
viewFlags,
|
viewFlags,
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, 1, 'tree'),
|
elementDef(NodeFlags.None, 1, 'tree'),
|
||||||
providerDef(NodeFlags.None, TreeComponent, [], {data: [0, 'data']}, () => TreeComponent_0),
|
providerDef(
|
||||||
|
NodeFlags.None, TreeComponent, [], {data: [0, 'data']}, null, () => TreeComponent_0),
|
||||||
],
|
],
|
||||||
(updater: NodeUpdater, view: ViewData, cmp: TreeComponent) => {
|
(updater: NodeUpdater, view: ViewData) => {
|
||||||
|
const cmp = view.component;
|
||||||
updater.checkInline(view, 1, cmp.data.left);
|
updater.checkInline(view, 1, cmp.data.left);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -42,9 +44,11 @@ const TreeComponent_2: ViewDefinition = viewDef(
|
||||||
viewFlags,
|
viewFlags,
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, 1, 'tree'),
|
elementDef(NodeFlags.None, 1, 'tree'),
|
||||||
providerDef(NodeFlags.None, TreeComponent, [], {data: [0, 'data']}, () => TreeComponent_0),
|
providerDef(
|
||||||
|
NodeFlags.None, TreeComponent, [], {data: [0, 'data']}, null, () => TreeComponent_0),
|
||||||
],
|
],
|
||||||
(updater: NodeUpdater, view: ViewData, cmp: TreeComponent) => {
|
(updater: NodeUpdater, view: ViewData) => {
|
||||||
|
const cmp = view.component;
|
||||||
updater.checkInline(view, 1, cmp.data.right);
|
updater.checkInline(view, 1, cmp.data.right);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,7 +63,8 @@ const TreeComponent_0: ViewDefinition = viewDef(
|
||||||
anchorDef(NodeFlags.HasEmbeddedViews, 1, TreeComponent_2),
|
anchorDef(NodeFlags.HasEmbeddedViews, 1, TreeComponent_2),
|
||||||
providerDef(NodeFlags.None, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
providerDef(NodeFlags.None, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||||
],
|
],
|
||||||
(updater: NodeUpdater, view: ViewData, cmp: TreeComponent) => {
|
(updater: NodeUpdater, view: ViewData) => {
|
||||||
|
const cmp = view.component;
|
||||||
updater.checkInline(view, 0, cmp.bgColor);
|
updater.checkInline(view, 0, cmp.bgColor);
|
||||||
updater.checkInline(view, 1, cmp.data.value);
|
updater.checkInline(view, 1, cmp.data.value);
|
||||||
updater.checkInline(view, 3, cmp.data.left != null);
|
updater.checkInline(view, 3, cmp.data.left != null);
|
||||||
|
|
Loading…
Reference in New Issue