parent
0adb97bffb
commit
65417374f1
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition} from './types';
|
||||
|
||||
export function anchorDef(
|
||||
flags: NodeFlags, childCount: number, template?: ViewDefinition): NodeDef {
|
||||
return {
|
||||
type: NodeType.Anchor,
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
reverseChildIndex: undefined,
|
||||
parent: undefined,
|
||||
childFlags: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childCount,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
component: undefined, template
|
||||
};
|
||||
}
|
||||
|
||||
export function createAnchor(view: ViewData, renderHost: any, def: NodeDef): NodeData {
|
||||
const parentNode = def.parent != null ? view.nodes[def.parent].renderNode : renderHost;
|
||||
let renderNode: any;
|
||||
if (view.renderer) {
|
||||
renderNode = view.renderer.createTemplateAnchor(parentNode);
|
||||
} else {
|
||||
renderNode = document.createComment('');
|
||||
if (parentNode) {
|
||||
parentNode.appendChild(renderNode);
|
||||
}
|
||||
}
|
||||
return {
|
||||
renderNode,
|
||||
provider: undefined,
|
||||
embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
|
||||
componentView: undefined
|
||||
};
|
||||
}
|
|
@ -8,9 +8,33 @@
|
|||
|
||||
import {SecurityContext} from '../security';
|
||||
|
||||
import {BindingDef, BindingType, DisposableFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewFlags} from './types';
|
||||
import {BindingDef, BindingType, DisposableFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition, ViewFlags} from './types';
|
||||
import {checkAndUpdateBinding, setBindingDebugInfo} from './util';
|
||||
|
||||
export function anchorDef(
|
||||
flags: NodeFlags, childCount: number, template?: ViewDefinition): NodeDef {
|
||||
return {
|
||||
type: NodeType.Element,
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
reverseChildIndex: undefined,
|
||||
parent: undefined,
|
||||
childFlags: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childCount,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
element: {name: undefined, attrs: undefined, outputs: [], template},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function elementDef(
|
||||
flags: NodeFlags, childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
|
||||
bindings?:
|
||||
|
@ -65,11 +89,10 @@ export function elementDef(
|
|||
childCount,
|
||||
bindings: bindingDefs,
|
||||
disposableCount: outputDefs.length,
|
||||
element: {name, attrs: fixedAttrs, outputs: outputDefs},
|
||||
element: {name, attrs: fixedAttrs, outputs: outputDefs, template: undefined},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
component: undefined,
|
||||
template: undefined
|
||||
pureExpression: undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -78,9 +101,10 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No
|
|||
const elDef = def.element;
|
||||
let el: any;
|
||||
if (view.renderer) {
|
||||
el = view.renderer.createElement(parentNode, elDef.name);
|
||||
el = elDef.name ? view.renderer.createElement(parentNode, elDef.name) :
|
||||
view.renderer.createTemplateAnchor(parentNode);
|
||||
} else {
|
||||
el = document.createElement(elDef.name);
|
||||
el = elDef.name ? document.createElement(elDef.name) : document.createComment('');
|
||||
if (parentNode) {
|
||||
parentNode.appendChild(el);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export {anchorDef} from './anchor';
|
||||
export {elementDef} from './element';
|
||||
export {anchorDef, elementDef} from './element';
|
||||
export {providerDef} from './provider';
|
||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||
export {textDef} from './text';
|
||||
export {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
|
||||
export {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach';
|
||||
|
|
|
@ -75,9 +75,9 @@ export function providerDef(
|
|||
childCount: 0, bindings,
|
||||
disposableCount: outputDefs.length,
|
||||
element: undefined,
|
||||
provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs},
|
||||
text: undefined, component,
|
||||
template: undefined
|
||||
provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs, component},
|
||||
text: undefined,
|
||||
pureExpression: undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -147,8 +147,7 @@ export function checkAndUpdateProviderInline(
|
|||
}
|
||||
}
|
||||
|
||||
export function checkAndUpdateProviderDynamic(
|
||||
view: ViewData, index: number, def: NodeDef, values: any[]) {
|
||||
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const provider = view.nodes[def.index].provider;
|
||||
let changes: SimpleChanges;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {resolveDep, tokenKey} from './provider';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, PureExpressionData, PureExpressionType, ViewData} from './types';
|
||||
import {checkAndUpdateBinding} from './util';
|
||||
|
||||
export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
|
||||
return _pureExpressionDef(
|
||||
PureExpressionType.Pipe, new Array(argCount),
|
||||
{token: pipeToken, tokenKey: tokenKey(pipeToken), flags: DepFlags.None});
|
||||
}
|
||||
|
||||
export function pureArrayDef(argCount: number): NodeDef {
|
||||
return _pureExpressionDef(PureExpressionType.Array, new Array(argCount), undefined);
|
||||
}
|
||||
|
||||
export function pureObjectDef(propertyNames: string[]): NodeDef {
|
||||
return _pureExpressionDef(PureExpressionType.Object, propertyNames, undefined);
|
||||
}
|
||||
|
||||
function _pureExpressionDef(
|
||||
type: PureExpressionType, propertyNames: string[], pipeDep: DepDef): NodeDef {
|
||||
const bindings: BindingDef[] = new Array(propertyNames.length);
|
||||
for (let i = 0; i < propertyNames.length; i++) {
|
||||
const prop = propertyNames[i];
|
||||
bindings[i] = {
|
||||
type: BindingType.PureExpressionProperty,
|
||||
name: prop,
|
||||
nonMinifiedName: prop,
|
||||
securityContext: undefined,
|
||||
suffix: undefined
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: NodeType.PureExpression,
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
reverseChildIndex: undefined,
|
||||
parent: undefined,
|
||||
childFlags: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
childCount: 0, bindings,
|
||||
disposableCount: 0,
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: {type, pipeDep}
|
||||
};
|
||||
}
|
||||
|
||||
export function createPureExpression(view: ViewData, def: NodeDef): NodeData {
|
||||
const pipe = def.pureExpression.pipeDep ?
|
||||
resolveDep(view, def.parent, def.pureExpression.pipeDep) :
|
||||
undefined;
|
||||
const data: PureExpressionData = {value: undefined, pipe: pipe};
|
||||
return {
|
||||
renderNode: undefined,
|
||||
provider: data,
|
||||
embeddedViews: undefined,
|
||||
componentView: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function checkAndUpdatePureExpressionInline(
|
||||
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
||||
v7: any, v8: any, v9: any) {
|
||||
const bindings = def.bindings;
|
||||
let changed = false;
|
||||
// Note: fallthrough is intended!
|
||||
switch (bindings.length) {
|
||||
case 10:
|
||||
if (checkAndUpdateBinding(view, def, 9, v9)) changed = true;
|
||||
case 9:
|
||||
if (checkAndUpdateBinding(view, def, 8, v8)) changed = true;
|
||||
case 8:
|
||||
if (checkAndUpdateBinding(view, def, 7, v7)) changed = true;
|
||||
case 7:
|
||||
if (checkAndUpdateBinding(view, def, 6, v6)) changed = true;
|
||||
case 6:
|
||||
if (checkAndUpdateBinding(view, def, 5, v5)) changed = true;
|
||||
case 5:
|
||||
if (checkAndUpdateBinding(view, def, 4, v4)) changed = true;
|
||||
case 4:
|
||||
if (checkAndUpdateBinding(view, def, 3, v3)) changed = true;
|
||||
case 3:
|
||||
if (checkAndUpdateBinding(view, def, 2, v2)) changed = true;
|
||||
case 2:
|
||||
if (checkAndUpdateBinding(view, def, 1, v1)) changed = true;
|
||||
case 1:
|
||||
if (checkAndUpdateBinding(view, def, 0, v0)) changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
const data: PureExpressionData = view.nodes[def.index].provider;
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
value = new Array(bindings.length);
|
||||
// Note: fallthrough is intended!
|
||||
switch (bindings.length) {
|
||||
case 10:
|
||||
value[9] = v9;
|
||||
case 9:
|
||||
value[8] = v8;
|
||||
case 8:
|
||||
value[7] = v7;
|
||||
case 7:
|
||||
value[6] = v6;
|
||||
case 6:
|
||||
value[5] = v5;
|
||||
case 5:
|
||||
value[4] = v4;
|
||||
case 4:
|
||||
value[3] = v3;
|
||||
case 3:
|
||||
value[2] = v2;
|
||||
case 2:
|
||||
value[1] = v1;
|
||||
case 1:
|
||||
value[0] = v0;
|
||||
}
|
||||
break;
|
||||
case PureExpressionType.Object:
|
||||
value = {};
|
||||
// Note: fallthrough is intended!
|
||||
switch (bindings.length) {
|
||||
case 10:
|
||||
value[bindings[9].name] = v9;
|
||||
case 9:
|
||||
value[bindings[8].name] = v8;
|
||||
case 8:
|
||||
value[bindings[7].name] = v7;
|
||||
case 7:
|
||||
value[bindings[6].name] = v6;
|
||||
case 6:
|
||||
value[bindings[5].name] = v5;
|
||||
case 5:
|
||||
value[bindings[4].name] = v4;
|
||||
case 4:
|
||||
value[bindings[3].name] = v3;
|
||||
case 3:
|
||||
value[bindings[2].name] = v2;
|
||||
case 2:
|
||||
value[bindings[1].name] = v1;
|
||||
case 1:
|
||||
value[bindings[0].name] = v0;
|
||||
}
|
||||
break;
|
||||
case PureExpressionType.Pipe:
|
||||
switch (bindings.length) {
|
||||
case 10:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
break;
|
||||
case 9:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7, v8);
|
||||
break;
|
||||
case 8:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7);
|
||||
break;
|
||||
case 7:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6);
|
||||
break;
|
||||
case 6:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5);
|
||||
break;
|
||||
case 5:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4);
|
||||
break;
|
||||
case 4:
|
||||
value = data.pipe.transform(v0, v1, v2, v3);
|
||||
break;
|
||||
case 3:
|
||||
value = data.pipe.transform(v0, v1, v2);
|
||||
break;
|
||||
case 2:
|
||||
value = data.pipe.transform(v0, v1);
|
||||
break;
|
||||
case 1:
|
||||
value = data.pipe.transform(v0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
data.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const bindings = def.bindings;
|
||||
let changed = false;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
// Note: We need to loop over all values, so that
|
||||
// the old values are updates as well!
|
||||
if (checkAndUpdateBinding(view, def, i, values[i])) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
const data: PureExpressionData = view.nodes[def.index].provider;
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
value = values;
|
||||
break;
|
||||
case PureExpressionType.Object:
|
||||
value = {};
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
value[bindings[i].name] = values[i];
|
||||
}
|
||||
break;
|
||||
case PureExpressionType.Pipe:
|
||||
value = data.pipe.transform(values[0], ...values.slice(1));
|
||||
break;
|
||||
}
|
||||
data.value = value;
|
||||
}
|
||||
}
|
|
@ -39,8 +39,7 @@ export function textDef(constants: string[]): NodeDef {
|
|||
element: undefined,
|
||||
provider: undefined,
|
||||
text: {prefix: constants[0]},
|
||||
component: undefined,
|
||||
template: undefined
|
||||
pureExpression: undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -124,9 +123,13 @@ export function checkAndUpdateTextInline(
|
|||
|
||||
export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const bindings = def.bindings;
|
||||
let changed = view.firstChange;
|
||||
for (let i = 0; i < values.length && !changed; i++) {
|
||||
changed = changed || checkAndUpdateBinding(view, def, i, values[i]);
|
||||
let changed = false;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
// Note: We need to loop over all values, so that
|
||||
// the old values are updates as well!
|
||||
if (checkAndUpdateBinding(view, def, i, values[i])) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
let value = '';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {PipeTransform} from '../change_detection/change_detection';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
||||
|
@ -42,8 +43,8 @@ export type ViewUpdateFn = (updater: NodeUpdater, view: ViewData) => void;
|
|||
export interface NodeUpdater {
|
||||
checkInline(
|
||||
view: ViewData, nodeIndex: number, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
|
||||
v6?: any, v7?: any, v8?: any, v9?: any): void;
|
||||
checkDynamic(view: ViewData, nodeIndex: number, values: any[]): void;
|
||||
v6?: any, v7?: any, v8?: any, v9?: any): any;
|
||||
checkDynamic(view: ViewData, nodeIndex: number, values: any[]): any;
|
||||
}
|
||||
|
||||
export type ViewHandleEventFn =
|
||||
|
@ -68,24 +69,22 @@ export interface NodeDef {
|
|||
childCount: number;
|
||||
/** aggregated NodeFlags for all children **/
|
||||
childFlags: NodeFlags;
|
||||
providerIndices: {[tokenKey: string]: number};
|
||||
bindingIndex: number;
|
||||
bindings: BindingDef[];
|
||||
disposableIndex: number;
|
||||
disposableCount: number;
|
||||
element: ElementDef;
|
||||
providerIndices: {[tokenKey: string]: number};
|
||||
provider: ProviderDef;
|
||||
text: TextDef;
|
||||
// closure to allow recursive components
|
||||
component: () => ViewDefinition;
|
||||
template: ViewDefinition;
|
||||
pureExpression: PureExpressionDef;
|
||||
}
|
||||
|
||||
export enum NodeType {
|
||||
Element,
|
||||
Text,
|
||||
Anchor,
|
||||
Provider
|
||||
Provider,
|
||||
PureExpression
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,6 +108,7 @@ export interface ElementDef {
|
|||
name: string;
|
||||
attrs: {[name: string]: string};
|
||||
outputs: ElementOutputDef[];
|
||||
template: ViewDefinition;
|
||||
}
|
||||
|
||||
export interface ElementOutputDef {
|
||||
|
@ -140,17 +140,31 @@ export interface ProviderDef {
|
|||
ctor: any;
|
||||
deps: DepDef[];
|
||||
outputs: ProviderOutputDef[];
|
||||
// closure to allow recursive components
|
||||
component: () => ViewDefinition;
|
||||
}
|
||||
|
||||
export interface TextDef { prefix: string; }
|
||||
|
||||
export interface PureExpressionDef {
|
||||
type: PureExpressionType;
|
||||
pipeDep: DepDef;
|
||||
}
|
||||
|
||||
export enum PureExpressionType {
|
||||
Array,
|
||||
Object,
|
||||
Pipe
|
||||
}
|
||||
|
||||
export enum BindingType {
|
||||
ElementAttribute,
|
||||
ElementClass,
|
||||
ElementStyle,
|
||||
ElementProperty,
|
||||
ProviderProperty,
|
||||
Interpolation
|
||||
Interpolation,
|
||||
PureExpressionProperty
|
||||
}
|
||||
|
||||
export interface BindingDef {
|
||||
|
@ -193,11 +207,16 @@ export type DisposableFn = () => void;
|
|||
*/
|
||||
export interface NodeData {
|
||||
renderNode: any;
|
||||
provider: any;
|
||||
provider: PureExpressionData|any;
|
||||
componentView: ViewData;
|
||||
embeddedViews: ViewData[];
|
||||
}
|
||||
|
||||
export interface PureExpressionData {
|
||||
value: any;
|
||||
pipe: PipeTransform;
|
||||
}
|
||||
|
||||
export interface Services {
|
||||
renderComponent(rcp: RenderComponentType): Renderer;
|
||||
sanitize(context: SecurityContext, value: string): string;
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
||||
import {RenderComponentType, Renderer} from '../render/api';
|
||||
|
||||
import {createAnchor} from './anchor';
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider} from './provider';
|
||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types';
|
||||
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderDef, PureExpressionData, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types';
|
||||
import {checkBindingNoChanges} from './util';
|
||||
|
||||
const NOOP = (): any => undefined;
|
||||
|
@ -123,16 +123,17 @@ function calculateReverseChildIndex(
|
|||
}
|
||||
|
||||
function validateNode(parent: NodeDef, node: NodeDef) {
|
||||
if (node.template) {
|
||||
if (node.template.lastRootNode != null &&
|
||||
node.template.nodes[node.template.lastRootNode].flags & NodeFlags.HasEmbeddedViews) {
|
||||
const template = node.element && node.element.template;
|
||||
if (template) {
|
||||
if (template.lastRootNode != null &&
|
||||
template.nodes[template.lastRootNode].flags & NodeFlags.HasEmbeddedViews) {
|
||||
throw new Error(
|
||||
`Illegal State: Last root node of a template can't have embedded views, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.provider) {
|
||||
const parentType = parent ? parent.type : null;
|
||||
if (parentType !== NodeType.Element && parentType !== NodeType.Anchor) {
|
||||
if (parentType !== NodeType.Element) {
|
||||
throw new Error(
|
||||
`Illegal State: Provider nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
}
|
||||
|
@ -175,7 +176,7 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
|||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||
// embedded views are seen as siblings to the anchor, so we need
|
||||
// to get the parent of the anchor and use it as parentIndex.
|
||||
const view = createView(parent.services, parent, anchorDef.parent, anchorDef.template);
|
||||
const view = createView(parent.services, parent, anchorDef.parent, anchorDef.element.template);
|
||||
initView(view, null, parent.component, context);
|
||||
return view;
|
||||
}
|
||||
|
@ -223,16 +224,16 @@ function initView(view: ViewData, renderHost: any, component: any, context: any)
|
|||
case NodeType.Text:
|
||||
nodeData = createText(view, renderHost, nodeDef);
|
||||
break;
|
||||
case NodeType.Anchor:
|
||||
nodeData = createAnchor(view, renderHost, nodeDef);
|
||||
break;
|
||||
case NodeType.Provider:
|
||||
let componentView: ViewData;
|
||||
if (nodeDef.component) {
|
||||
componentView = createView(view.services, view, i, nodeDef.component());
|
||||
if (nodeDef.provider.component) {
|
||||
componentView = createView(view.services, view, i, nodeDef.provider.component());
|
||||
}
|
||||
nodeData = createProvider(view, nodeDef, componentView);
|
||||
break;
|
||||
case NodeType.PureExpression:
|
||||
nodeData = createPureExpression(view, nodeDef);
|
||||
break;
|
||||
}
|
||||
nodes[i] = nodeData;
|
||||
}
|
||||
|
@ -272,12 +273,22 @@ const CheckNoChanges: NodeUpdater = {
|
|||
case 1:
|
||||
checkBindingNoChanges(view, nodeDef, 0, v0);
|
||||
}
|
||||
if (nodeDef.type === NodeType.PureExpression) {
|
||||
const data: PureExpressionData = view.nodes[index].provider;
|
||||
return data.value;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
checkDynamic: (view: ViewData, index: number, values: any[]): void => {
|
||||
const oldValues = view.oldValues;
|
||||
const nodeDef = view.def.nodes[index];
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
checkBindingNoChanges(view, view.def.nodes[index], i, values[i]);
|
||||
checkBindingNoChanges(view, nodeDef, i, values[i]);
|
||||
}
|
||||
if (nodeDef.type === NodeType.PureExpression) {
|
||||
const data: PureExpressionData = view.nodes[index].provider;
|
||||
return data.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -301,13 +312,17 @@ const CheckAndUpdate: NodeUpdater = {
|
|||
switch (nodeDef.type) {
|
||||
case NodeType.Element:
|
||||
checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
break;
|
||||
return undefined;
|
||||
case NodeType.Text:
|
||||
checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
break;
|
||||
return undefined;
|
||||
case NodeType.Provider:
|
||||
checkAndUpdateProviderInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
break;
|
||||
return undefined;
|
||||
case NodeType.PureExpression:
|
||||
checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
const data: PureExpressionData = view.nodes[index].provider;
|
||||
return data.value;
|
||||
}
|
||||
},
|
||||
checkDynamic: (view: ViewData, index: number, values: any[]): void => {
|
||||
|
@ -315,13 +330,17 @@ const CheckAndUpdate: NodeUpdater = {
|
|||
switch (nodeDef.type) {
|
||||
case NodeType.Element:
|
||||
checkAndUpdateElementDynamic(view, nodeDef, values);
|
||||
break;
|
||||
return undefined;
|
||||
case NodeType.Text:
|
||||
checkAndUpdateTextDynamic(view, nodeDef, values);
|
||||
break;
|
||||
return undefined;
|
||||
case NodeType.Provider:
|
||||
checkAndUpdateProviderDynamic(view, index, nodeDef, values);
|
||||
break;
|
||||
checkAndUpdateProviderDynamic(view, nodeDef, values);
|
||||
return undefined;
|
||||
case NodeType.PureExpression:
|
||||
checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
|
||||
const data: PureExpressionData = view.nodes[index].provider;
|
||||
return data.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import {RootRenderer} from '@angular/core';
|
||||
import {NodeUpdater, ViewData} from '@angular/core/src/view/index';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
|
@ -34,3 +35,21 @@ export function setupAndCheckRenderer(config: {directDom: boolean}) {
|
|||
afterEach(() => { expect(rootRenderer.renderComponent).toHaveBeenCalled(); });
|
||||
}
|
||||
}
|
||||
|
||||
export enum InlineDynamic {
|
||||
Inline,
|
||||
Dynamic
|
||||
}
|
||||
|
||||
export const INLINE_DYNAMIC_VALUES = [InlineDynamic.Inline, InlineDynamic.Dynamic];
|
||||
|
||||
export function callUpdater(
|
||||
updater: NodeUpdater, inlineDynamic: InlineDynamic, view: ViewData, nodeIndex: number,
|
||||
values: any[]): any {
|
||||
switch (inlineDynamic) {
|
||||
case InlineDynamic.Inline:
|
||||
return (<any>updater.checkInline)(view, nodeIndex, ...values);
|
||||
case InlineDynamic.Dynamic:
|
||||
return updater.checkDynamic(view, nodeIndex, values);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`View Pure Expressions`, () => {
|
||||
let services: Services;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(
|
||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
||||
services = new DefaultServices(rootRenderer, sanitizer);
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
}));
|
||||
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(services, viewDef);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
|
||||
class Service {
|
||||
data: any;
|
||||
}
|
||||
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support pure arrays in ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
let values: any[];
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, 2, 'span'), pureArrayDef(2),
|
||||
providerDef(NodeFlags.None, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 2,
|
||||
[callUpdater(updater, inlineDynamic, view, 1, values)]);
|
||||
}));
|
||||
const service = view.nodes[2].provider;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
const arr0 = service.data;
|
||||
expect(arr0).toEqual([1, 2]);
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
expect(service.data).toBe(arr0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
const arr1 = service.data;
|
||||
expect(arr1).not.toBe(arr0);
|
||||
expect(arr1).toEqual([3, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support pure objects in ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
let values: any[];
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, 2, 'span'), pureObjectDef(['a', 'b']),
|
||||
providerDef(NodeFlags.None, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 2,
|
||||
[callUpdater(updater, inlineDynamic, view, 1, values)]);
|
||||
}));
|
||||
const service = view.nodes[2].provider;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
const obj0 = service.data;
|
||||
expect(obj0).toEqual({a: 1, b: 2});
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
expect(service.data).toBe(obj0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
const obj1 = service.data;
|
||||
expect(obj1).not.toBe(obj0);
|
||||
expect(obj1).toEqual({a: 3, b: 2});
|
||||
});
|
||||
});
|
||||
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support pure pipes in ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
class SomePipe implements PipeTransform {
|
||||
transform(v1: any, v2: any) { return [v1 + 10, v2 + 20]; }
|
||||
}
|
||||
|
||||
let values: any[];
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, 3, 'span'), providerDef(NodeFlags.None, SomePipe, []),
|
||||
purePipeDef(SomePipe, 2),
|
||||
providerDef(NodeFlags.None, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 3,
|
||||
[callUpdater(updater, inlineDynamic, view, 2, values)]);
|
||||
}));
|
||||
const service = view.nodes[3].provider;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
const obj0 = service.data;
|
||||
expect(obj0).toEqual([11, 22]);
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
expect(service.data).toBe(obj0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
const obj1 = service.data;
|
||||
expect(obj1).not.toBe(obj0);
|
||||
expect(obj1).toEqual([13, 22]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue