feat(compiler): integrate compiler with view engine - change detection tests work (#14412)
Included refactoring: - make ViewData.parentIndex point to component provider index - split NodeType.Provider into Provider / Directive / Pipe - make purePipe take the real pipe as argument to detect changes - order change detection: 1) directive props 2) renderer props Part of #14013 PR Close #14412
This commit is contained in:

committed by
Miško Hevery

parent
1dc9be4b7d
commit
e4e9dbe33d
@ -10,7 +10,7 @@ import {isDevMode} from '../application_ref';
|
||||
import {SecurityContext} from '../security';
|
||||
|
||||
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData} from './types';
|
||||
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, resolveViewDefinition, sliceErrorStack, unwrapValue} from './util';
|
||||
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, resolveViewDefinition, sliceErrorStack} from './util';
|
||||
|
||||
export function anchorDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
||||
@ -209,8 +209,6 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||
if (!checkAndUpdateBinding(view, def, bindingIdx, value)) {
|
||||
return;
|
||||
}
|
||||
value = unwrapValue(value);
|
||||
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const name = binding.name;
|
||||
const renderNode = asElementData(view, def.index).renderElement;
|
||||
|
@ -31,7 +31,6 @@ export function viewDebugError(msg: string, context: DebugContext): Error {
|
||||
const err = new Error(msg);
|
||||
(err as any)[ERROR_DEBUG_CONTEXT] = context;
|
||||
err.stack = context.source;
|
||||
context.view.state |= ViewState.Errored;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
export {anchorDef, elementDef} from './element';
|
||||
export {ngContentDef} from './ng_content';
|
||||
export {directiveDef, providerDef} from './provider';
|
||||
export {directiveDef, pipeDef, providerDef} from './provider';
|
||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||
export {queryDef} from './query';
|
||||
export {createComponentFactory} from './refs';
|
||||
export {initServicesIfNeeded} from './services';
|
||||
export {textDef} from './text';
|
||||
export {elementEventFullName, nodeValue, rootRenderNodes} from './util';
|
||||
export {elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
|
||||
export {viewDef} from './view';
|
||||
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||
|
||||
|
@ -15,8 +15,8 @@ import * as v1renderer from '../render/api';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {checkAndUpdateBinding, dispatchEvent, isComponentView, tokenKey, unwrapValue, viewParentDiIndex} from './util';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DirectiveOutputDef, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {checkAndUpdateBinding, dispatchEvent, isComponentView, tokenKey, viewParentElIndex} from './util';
|
||||
|
||||
const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
|
||||
const ElementRefTokenKey = tokenKey(ElementRef);
|
||||
@ -31,45 +31,55 @@ export function directiveDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor: any,
|
||||
deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
|
||||
return _providerDef(
|
||||
flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps, props, outputs,
|
||||
component);
|
||||
}
|
||||
|
||||
export function providerDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], type: ProviderType, token: any,
|
||||
value: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
return _providerDef(flags, matchedQueries, 0, type, token, value, deps);
|
||||
}
|
||||
|
||||
export function _providerDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number,
|
||||
type: ProviderType, token: any, value: any, deps: ([DepFlags, any] | any)[],
|
||||
props?: {[name: string]: [number, string]}, outputs?: {[name: string]: string},
|
||||
component?: () => ViewDefinition): NodeDef {
|
||||
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
|
||||
if (matchedQueries) {
|
||||
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
||||
}
|
||||
|
||||
const bindings: BindingDef[] = [];
|
||||
if (props) {
|
||||
for (let prop in props) {
|
||||
const [bindingIndex, nonMinifiedName] = props[prop];
|
||||
bindings[bindingIndex] = {
|
||||
type: BindingType.ProviderProperty,
|
||||
type: BindingType.DirectiveProperty,
|
||||
name: prop, nonMinifiedName,
|
||||
securityContext: undefined,
|
||||
suffix: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
const outputDefs: ProviderOutputDef[] = [];
|
||||
const outputDefs: DirectiveOutputDef[] = [];
|
||||
if (outputs) {
|
||||
for (let propName in outputs) {
|
||||
outputDefs.push({propName, eventName: outputs[propName]});
|
||||
}
|
||||
}
|
||||
return _def(
|
||||
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
|
||||
bindings, outputDefs, component);
|
||||
}
|
||||
|
||||
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
return _def(NodeType.Pipe, flags, null, 0, ProviderType.Class, ctor, ctor, deps);
|
||||
}
|
||||
|
||||
export function providerDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], type: ProviderType, token: any,
|
||||
value: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
return _def(NodeType.Provider, flags, matchedQueries, 0, type, token, value, deps);
|
||||
}
|
||||
|
||||
export function _def(
|
||||
type: NodeType, flags: NodeFlags, matchedQueries: [string, QueryValueType][],
|
||||
childCount: number, providerType: ProviderType, token: any, value: any,
|
||||
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
|
||||
component?: () => ViewDefinition): NodeDef {
|
||||
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
|
||||
if (matchedQueries) {
|
||||
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
||||
}
|
||||
if (!outputs) {
|
||||
outputs = [];
|
||||
}
|
||||
if (!bindings) {
|
||||
bindings = [];
|
||||
}
|
||||
|
||||
const depDefs: DepDef[] = deps.map(value => {
|
||||
let token: any;
|
||||
let flags: DepFlags;
|
||||
@ -86,7 +96,7 @@ export function _providerDef(
|
||||
}
|
||||
|
||||
return {
|
||||
type: NodeType.Provider,
|
||||
type,
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
reverseChildIndex: undefined,
|
||||
@ -99,14 +109,13 @@ export function _providerDef(
|
||||
flags,
|
||||
matchedQueries: matchedQueryDefs,
|
||||
ngContentIndex: undefined, childCount, bindings,
|
||||
disposableCount: outputDefs.length,
|
||||
disposableCount: outputs.length,
|
||||
element: undefined,
|
||||
provider: {
|
||||
type,
|
||||
type: providerType,
|
||||
token,
|
||||
tokenKey: tokenKey(token), value,
|
||||
deps: depDefs,
|
||||
outputs: outputDefs, component
|
||||
deps: depDefs, outputs, component
|
||||
},
|
||||
text: undefined,
|
||||
pureExpression: undefined,
|
||||
@ -116,97 +125,115 @@ export function _providerDef(
|
||||
}
|
||||
|
||||
export function createProviderInstance(view: ViewData, def: NodeDef): any {
|
||||
return def.flags & NodeFlags.LazyProvider ? NOT_CREATED : _createProviderInstance(view, def);
|
||||
}
|
||||
|
||||
export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
||||
// deps are looked up from component.
|
||||
let compView = view;
|
||||
while (compView.parent && !isComponentView(compView)) {
|
||||
compView = compView.parent;
|
||||
}
|
||||
// pipes are always eager and classes!
|
||||
return createClass(
|
||||
compView.parent, compView.parentIndex, viewParentElIndex(compView), def.provider.value,
|
||||
def.provider.deps);
|
||||
}
|
||||
|
||||
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||
const providerDef = def.provider;
|
||||
return def.flags & NodeFlags.LazyProvider ? NOT_CREATED : createInstance(view, def);
|
||||
// directives are always eager and classes!
|
||||
const instance = createClass(view, def.index, def.parent, def.provider.value, def.provider.deps);
|
||||
if (providerDef.outputs.length) {
|
||||
for (let i = 0; i < providerDef.outputs.length; i++) {
|
||||
const output = providerDef.outputs[i];
|
||||
const subscription = instance[output.propName].subscribe(
|
||||
eventHandlerClosure(view, def.parent, output.eventName));
|
||||
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||
return (event: any) => dispatchEvent(view, index, eventName, event);
|
||||
}
|
||||
|
||||
export function checkAndUpdateProviderInline(
|
||||
export function checkAndUpdateDirectiveInline(
|
||||
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 provider = asProviderData(view, def.index).instance;
|
||||
const providerData = asProviderData(view, def.index);
|
||||
const directive = providerData.instance;
|
||||
let changes: SimpleChanges;
|
||||
// Note: fallthrough is intended!
|
||||
switch (def.bindings.length) {
|
||||
case 10:
|
||||
changes = checkAndUpdateProp(view, provider, def, 9, v9, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 9, v9, changes);
|
||||
case 9:
|
||||
changes = checkAndUpdateProp(view, provider, def, 8, v8, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 8, v8, changes);
|
||||
case 8:
|
||||
changes = checkAndUpdateProp(view, provider, def, 7, v7, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 7, v7, changes);
|
||||
case 7:
|
||||
changes = checkAndUpdateProp(view, provider, def, 6, v6, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 6, v6, changes);
|
||||
case 6:
|
||||
changes = checkAndUpdateProp(view, provider, def, 5, v5, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 5, v5, changes);
|
||||
case 5:
|
||||
changes = checkAndUpdateProp(view, provider, def, 4, v4, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 4, v4, changes);
|
||||
case 4:
|
||||
changes = checkAndUpdateProp(view, provider, def, 3, v3, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 3, v3, changes);
|
||||
case 3:
|
||||
changes = checkAndUpdateProp(view, provider, def, 2, v2, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 2, v2, changes);
|
||||
case 2:
|
||||
changes = checkAndUpdateProp(view, provider, def, 1, v1, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 1, v1, changes);
|
||||
case 1:
|
||||
changes = checkAndUpdateProp(view, provider, def, 0, v0, changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, 0, v0, changes);
|
||||
}
|
||||
if (changes) {
|
||||
provider.ngOnChanges(changes);
|
||||
directive.ngOnChanges(changes);
|
||||
}
|
||||
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
provider.ngOnInit();
|
||||
directive.ngOnInit();
|
||||
}
|
||||
if (def.flags & NodeFlags.DoCheck) {
|
||||
provider.ngDoCheck();
|
||||
directive.ngDoCheck();
|
||||
}
|
||||
}
|
||||
|
||||
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const provider = asProviderData(view, def.index).instance;
|
||||
export function checkAndUpdateDirectiveDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const providerData = asProviderData(view, def.index);
|
||||
const directive = providerData.instance;
|
||||
let changes: SimpleChanges;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
changes = checkAndUpdateProp(view, provider, def, i, values[i], changes);
|
||||
changes = checkAndUpdateProp(view, providerData, def, i, values[i], changes);
|
||||
}
|
||||
if (changes) {
|
||||
provider.ngOnChanges(changes);
|
||||
directive.ngOnChanges(changes);
|
||||
}
|
||||
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
provider.ngOnInit();
|
||||
directive.ngOnInit();
|
||||
}
|
||||
if (def.flags & NodeFlags.DoCheck) {
|
||||
provider.ngDoCheck();
|
||||
directive.ngDoCheck();
|
||||
}
|
||||
}
|
||||
|
||||
function createInstance(view: ViewData, nodeDef: NodeDef): any {
|
||||
const providerDef = nodeDef.provider;
|
||||
function _createProviderInstance(view: ViewData, def: NodeDef): any {
|
||||
const providerDef = def.provider;
|
||||
let injectable: any;
|
||||
switch (providerDef.type) {
|
||||
case ProviderType.Class:
|
||||
injectable =
|
||||
createClass(view, nodeDef.index, nodeDef.parent, providerDef.value, providerDef.deps);
|
||||
injectable = createClass(view, def.index, def.parent, providerDef.value, providerDef.deps);
|
||||
break;
|
||||
case ProviderType.Factory:
|
||||
injectable =
|
||||
callFactory(view, nodeDef.index, nodeDef.parent, providerDef.value, providerDef.deps);
|
||||
injectable = callFactory(view, def.index, def.parent, providerDef.value, providerDef.deps);
|
||||
break;
|
||||
case ProviderType.UseExisting:
|
||||
injectable = resolveDep(view, nodeDef.index, nodeDef.parent, providerDef.deps[0]);
|
||||
injectable = resolveDep(view, def.index, def.parent, providerDef.deps[0]);
|
||||
break;
|
||||
case ProviderType.Value:
|
||||
injectable = providerDef.value;
|
||||
break;
|
||||
}
|
||||
if (providerDef.outputs.length) {
|
||||
for (let i = 0; i < providerDef.outputs.length; i++) {
|
||||
const output = providerDef.outputs[i];
|
||||
const subscription = injectable[output.propName].subscribe(
|
||||
eventHandlerClosure(view, nodeDef.parent, output.eventName));
|
||||
view.disposables[nodeDef.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
}
|
||||
}
|
||||
return injectable;
|
||||
}
|
||||
|
||||
@ -291,7 +318,7 @@ export function resolveDep(
|
||||
requestNodeIndex = null;
|
||||
elIndex = view.def.nodes[elIndex].parent;
|
||||
while (elIndex == null && view) {
|
||||
elIndex = viewParentDiIndex(view);
|
||||
elIndex = viewParentElIndex(view);
|
||||
view = view.parent;
|
||||
}
|
||||
}
|
||||
@ -334,20 +361,20 @@ export function resolveDep(
|
||||
if (providerIndex != null) {
|
||||
const providerData = asProviderData(view, providerIndex);
|
||||
if (providerData.instance === NOT_CREATED) {
|
||||
providerData.instance = createInstance(view, view.def.nodes[providerIndex]);
|
||||
providerData.instance = _createProviderInstance(view, view.def.nodes[providerIndex]);
|
||||
}
|
||||
return providerData.instance;
|
||||
}
|
||||
}
|
||||
requestNodeIndex = null;
|
||||
elIndex = viewParentDiIndex(view);
|
||||
elIndex = viewParentElIndex(view);
|
||||
view = view.parent;
|
||||
}
|
||||
return startView.root.injector.get(depDef.token, notFoundValue);
|
||||
}
|
||||
|
||||
function checkAndUpdateProp(
|
||||
view: ViewData, provider: any, def: NodeDef, bindingIdx: number, value: any,
|
||||
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
|
||||
changes: SimpleChanges): SimpleChanges {
|
||||
let change: SimpleChange;
|
||||
let changed: boolean;
|
||||
@ -361,13 +388,18 @@ function checkAndUpdateProp(
|
||||
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
||||
}
|
||||
if (changed) {
|
||||
value = unwrapValue(value);
|
||||
if (def.flags & NodeFlags.HasComponent) {
|
||||
const compView = providerData.componentView;
|
||||
if (compView.def.flags & ViewFlags.OnPush) {
|
||||
compView.state |= ViewState.ChecksEnabled;
|
||||
}
|
||||
}
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const propName = binding.name;
|
||||
// Note: This is still safe with Closure Compiler as
|
||||
// the user passed in the property name as an object has to `providerDef`,
|
||||
// so Closure Compiler will have renamed the property correctly already.
|
||||
provider[propName] = value;
|
||||
providerData.instance[propName] = value;
|
||||
if (change) {
|
||||
changes = changes || {};
|
||||
changes[binding.nonMinifiedName] = change;
|
||||
@ -382,7 +414,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
|
||||
}
|
||||
const len = view.def.nodes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
// We use the provider post order to call providers of children first.
|
||||
// We use the reverse child oreder to call providers of children first.
|
||||
const nodeDef = view.def.reverseChildNodes[i];
|
||||
const nodeIndex = nodeDef.index;
|
||||
if (nodeDef.flags & lifecycles) {
|
||||
|
@ -7,24 +7,22 @@
|
||||
*/
|
||||
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionData, PureExpressionType, Services, ViewData, asPureExpressionData} from './types';
|
||||
import {checkAndUpdateBinding, tokenKey, unwrapValue} from './util';
|
||||
import {checkAndUpdateBinding, tokenKey} 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 purePipeDef(argCount: number): NodeDef {
|
||||
// argCount + 1 to include the pipe as first arg
|
||||
return _pureExpressionDef(PureExpressionType.Pipe, new Array(argCount + 1));
|
||||
}
|
||||
|
||||
export function pureArrayDef(argCount: number): NodeDef {
|
||||
return _pureExpressionDef(PureExpressionType.Array, new Array(argCount), undefined);
|
||||
return _pureExpressionDef(PureExpressionType.Array, new Array(argCount));
|
||||
}
|
||||
|
||||
export function pureObjectDef(propertyNames: string[]): NodeDef {
|
||||
return _pureExpressionDef(PureExpressionType.Object, propertyNames, undefined);
|
||||
return _pureExpressionDef(PureExpressionType.Object, propertyNames);
|
||||
}
|
||||
|
||||
function _pureExpressionDef(
|
||||
type: PureExpressionType, propertyNames: string[], pipeDep: DepDef): NodeDef {
|
||||
function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]): NodeDef {
|
||||
const bindings: BindingDef[] = new Array(propertyNames.length);
|
||||
for (let i = 0; i < propertyNames.length; i++) {
|
||||
const prop = propertyNames[i];
|
||||
@ -55,17 +53,14 @@ function _pureExpressionDef(
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: {type, pipeDep},
|
||||
pureExpression: {type},
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function createPureExpression(view: ViewData, def: NodeDef): PureExpressionData {
|
||||
const pipe = def.pureExpression.pipeDep ?
|
||||
Services.resolveDep(view, def.index, def.parent, def.pureExpression.pipeDep) :
|
||||
undefined;
|
||||
return {value: undefined, pipe};
|
||||
return {value: undefined};
|
||||
}
|
||||
|
||||
export function checkAndUpdatePureExpressionInline(
|
||||
@ -99,17 +94,6 @@ export function checkAndUpdatePureExpressionInline(
|
||||
|
||||
const data = asPureExpressionData(view, def.index);
|
||||
if (changed) {
|
||||
v0 = unwrapValue(v0);
|
||||
v1 = unwrapValue(v1);
|
||||
v2 = unwrapValue(v2);
|
||||
v3 = unwrapValue(v3);
|
||||
v4 = unwrapValue(v4);
|
||||
v5 = unwrapValue(v5);
|
||||
v6 = unwrapValue(v6);
|
||||
v7 = unwrapValue(v7);
|
||||
v8 = unwrapValue(v8);
|
||||
v9 = unwrapValue(v9);
|
||||
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
@ -165,36 +149,37 @@ export function checkAndUpdatePureExpressionInline(
|
||||
}
|
||||
break;
|
||||
case PureExpressionType.Pipe:
|
||||
const pipe = v0;
|
||||
switch (bindings.length) {
|
||||
case 10:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
value = pipe.transform(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);
|
||||
value = pipe.transform(v1, v2, v3, v4, v5, v6, v7, v8);
|
||||
break;
|
||||
case 8:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7);
|
||||
value = pipe.transform(v1, v2, v3, v4, v5, v6, v7);
|
||||
break;
|
||||
case 7:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6);
|
||||
value = pipe.transform(v1, v2, v3, v4, v5, v6);
|
||||
break;
|
||||
case 6:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4, v5);
|
||||
value = pipe.transform(v1, v2, v3, v4, v5);
|
||||
break;
|
||||
case 5:
|
||||
value = data.pipe.transform(v0, v1, v2, v3, v4);
|
||||
value = pipe.transform(v1, v2, v3, v4);
|
||||
break;
|
||||
case 4:
|
||||
value = data.pipe.transform(v0, v1, v2, v3);
|
||||
value = pipe.transform(v1, v2, v3);
|
||||
break;
|
||||
case 3:
|
||||
value = data.pipe.transform(v0, v1, v2);
|
||||
value = pipe.transform(v1, v2);
|
||||
break;
|
||||
case 2:
|
||||
value = data.pipe.transform(v0, v1);
|
||||
value = pipe.transform(v1);
|
||||
break;
|
||||
case 1:
|
||||
value = data.pipe.transform(v0);
|
||||
value = pipe.transform(v0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -219,23 +204,18 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
value = new Array(values.length);
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
value[i] = unwrapValue(values[i]);
|
||||
}
|
||||
value = values;
|
||||
break;
|
||||
case PureExpressionType.Object:
|
||||
value = {};
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
value[bindings[i].name] = unwrapValue(values[i]);
|
||||
value[bindings[i].name] = values[i];
|
||||
}
|
||||
break;
|
||||
case PureExpressionType.Pipe:
|
||||
const params = new Array(values.length);
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
params[i] = unwrapValue(values[i]);
|
||||
}
|
||||
value = (<any>data.pipe.transform)(...params);
|
||||
const pipe = values[0];
|
||||
const params = values.slice(1);
|
||||
value = (<any>pipe.transform)(...params);
|
||||
break;
|
||||
}
|
||||
data.value = value;
|
||||
|
@ -13,7 +13,7 @@ import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
|
||||
import {createTemplateRef, createViewContainerRef} from './refs';
|
||||
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
||||
import {declaredViewContainer} from './util';
|
||||
import {declaredViewContainer, viewParentElIndex} from './util';
|
||||
|
||||
export function queryDef(
|
||||
flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef {
|
||||
@ -54,16 +54,18 @@ export function createQuery(): QueryList<any> {
|
||||
}
|
||||
|
||||
export function dirtyParentQuery(queryId: string, view: ViewData) {
|
||||
let nodeIndex = view.parentIndex;
|
||||
let elIndex = viewParentElIndex(view);
|
||||
view = view.parent;
|
||||
let queryIdx: number;
|
||||
while (view) {
|
||||
const elementDef = view.def.nodes[nodeIndex];
|
||||
queryIdx = elementDef.element.providerIndices[queryId];
|
||||
if (queryIdx != null) {
|
||||
break;
|
||||
if (elIndex != null) {
|
||||
const elementDef = view.def.nodes[elIndex];
|
||||
queryIdx = elementDef.element.providerIndices[queryId];
|
||||
if (queryIdx != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
nodeIndex = view.parentIndex;
|
||||
elIndex = viewParentElIndex(view);
|
||||
view = view.parent;
|
||||
}
|
||||
if (!view) {
|
||||
|
@ -19,8 +19,8 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||
import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentDiIndex} from './util';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||
import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentElIndex} from './util';
|
||||
|
||||
const EMPTY_CONTEXT = new Object();
|
||||
|
||||
@ -53,7 +53,7 @@ class ComponentFactory_ implements ComponentFactory<any> {
|
||||
const len = viewDef.nodes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const nodeDef = viewDef.nodes[i];
|
||||
if (nodeDef.provider && nodeDef.provider.component) {
|
||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||
componentNodeIndex = i;
|
||||
break;
|
||||
}
|
||||
@ -99,7 +99,7 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||
let view = this._view;
|
||||
let elIndex = view.def.nodes[this._elIndex].parent;
|
||||
while (elIndex == null && view) {
|
||||
elIndex = viewParentDiIndex(view);
|
||||
elIndex = viewParentElIndex(view);
|
||||
view = view.parent;
|
||||
}
|
||||
return view ? new Injector_(view, elIndex) : this._view.root.injector;
|
||||
|
@ -19,8 +19,8 @@ import {resolveDep} from './provider';
|
||||
import {getQueryValue} from './query';
|
||||
import {createInjector} from './refs';
|
||||
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||
import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentDiIndex} from './util';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, ViewUpdateFn, asElementData, asProviderData} from './types';
|
||||
import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentElIndex} from './util';
|
||||
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||
|
||||
@ -44,7 +44,8 @@ export function initServicesIfNeeded() {
|
||||
Services.resolveDep = services.resolveDep;
|
||||
Services.createDebugContext = services.createDebugContext;
|
||||
Services.handleEvent = services.handleEvent;
|
||||
Services.updateView = services.updateView;
|
||||
Services.updateDirectives = services.updateDirectives;
|
||||
Services.updateRenderer = services.updateRenderer;
|
||||
}
|
||||
|
||||
function createProdServices() {
|
||||
@ -62,7 +63,9 @@ function createProdServices() {
|
||||
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
||||
handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) =>
|
||||
view.def.handleEvent(view, nodeIndex, eventName, event),
|
||||
updateView: (check: NodeCheckFn, view: ViewData) => view.def.update(check, view)
|
||||
updateDirectives: (check: NodeCheckFn, view: ViewData) =>
|
||||
view.def.updateDirectives(check, view),
|
||||
updateRenderer: (check: NodeCheckFn, view: ViewData) => view.def.updateRenderer(check, view),
|
||||
};
|
||||
}
|
||||
|
||||
@ -80,7 +83,8 @@ function createDebugServices() {
|
||||
resolveDep: resolveDep,
|
||||
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
||||
handleEvent: debugHandleEvent,
|
||||
updateView: debugUpdateView
|
||||
updateDirectives: debugUpdateDirectives,
|
||||
updateRenderer: debugUpdateRenderer
|
||||
};
|
||||
}
|
||||
|
||||
@ -152,37 +156,56 @@ function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string,
|
||||
'handleEvent', view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
|
||||
}
|
||||
|
||||
function debugUpdateView(check: NodeCheckFn, view: ViewData) {
|
||||
function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
|
||||
if (view.state & ViewState.Destroyed) {
|
||||
throw viewDestroyedError(_currentAction);
|
||||
}
|
||||
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, 0));
|
||||
return view.def.update(debugCheckFn, view);
|
||||
debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0));
|
||||
return view.def.updateDirectives(debugCheckDirectivesFn, view);
|
||||
|
||||
function debugCheckFn(
|
||||
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
||||
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any) {
|
||||
const values = argStyle === ArgumentType.Dynamic ? v0 : [].slice.call(arguments, 3);
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
for (let i = 0; i < nodeDef.bindings.length; i++) {
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if ((binding.type === BindingType.ElementProperty ||
|
||||
binding.type === BindingType.ProviderProperty) &&
|
||||
checkBinding(view, nodeDef, i, value)) {
|
||||
const elIndex = nodeDef.type === NodeType.Provider ? nodeDef.parent : nodeDef.index;
|
||||
setBindingDebugInfo(
|
||||
view.root.renderer, asElementData(view, elIndex).renderElement, binding.nonMinifiedName,
|
||||
value);
|
||||
}
|
||||
}
|
||||
const result = check(view, nodeIndex, <any>argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
|
||||
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, nodeIndex));
|
||||
function debugCheckDirectivesFn(
|
||||
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
|
||||
const result = debugCheckFn(check, view, nodeIndex, argStyle, values);
|
||||
debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
|
||||
if (view.state & ViewState.Destroyed) {
|
||||
throw viewDestroyedError(_currentAction);
|
||||
}
|
||||
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0));
|
||||
return view.def.updateRenderer(debugCheckRenderNodeFn, view);
|
||||
|
||||
function debugCheckRenderNodeFn(
|
||||
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
|
||||
const result = debugCheckFn(check, view, nodeIndex, argStyle, values);
|
||||
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function debugCheckFn(
|
||||
delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType,
|
||||
givenValues: any[]) {
|
||||
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
for (let i = 0; i < nodeDef.bindings.length; i++) {
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if ((binding.type === BindingType.ElementProperty ||
|
||||
binding.type === BindingType.DirectiveProperty) &&
|
||||
checkBinding(view, nodeDef, i, value)) {
|
||||
const elIndex = nodeDef.type === NodeType.Directive ? nodeDef.parent : nodeDef.index;
|
||||
setBindingDebugInfo(
|
||||
view.root.renderer, asElementData(view, elIndex).renderElement, binding.nonMinifiedName,
|
||||
value);
|
||||
}
|
||||
}
|
||||
return (<any>delegate)(view, nodeIndex, argStyle, ...givenValues);
|
||||
};
|
||||
|
||||
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
|
||||
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
|
||||
if (value) {
|
||||
@ -203,16 +226,26 @@ function camelCaseToDashCase(input: string): string {
|
||||
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||
}
|
||||
|
||||
function nextNodeIndexWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.bindings && nodeDef.bindings.length) {
|
||||
if (nodeDef.type === NodeType.Directive && nodeDef.bindings && nodeDef.bindings.length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if ((nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text) && nodeDef.bindings &&
|
||||
nodeDef.bindings.length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
class DebugRenderer implements RendererV2 {
|
||||
constructor(private _delegate: RendererV2) {}
|
||||
@ -282,7 +315,7 @@ class DebugContext_ implements DebugContext {
|
||||
}
|
||||
if (elIndex == null) {
|
||||
while (elIndex == null && elView) {
|
||||
elIndex = viewParentDiIndex(elView);
|
||||
elIndex = viewParentElIndex(elView);
|
||||
elView = elView.parent;
|
||||
}
|
||||
}
|
||||
@ -320,7 +353,7 @@ class DebugContext_ implements DebugContext {
|
||||
if (this.elDef) {
|
||||
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
||||
const childDef = this.elView.def.nodes[i];
|
||||
if (childDef.type === NodeType.Provider) {
|
||||
if (childDef.type === NodeType.Provider || childDef.type === NodeType.Directive) {
|
||||
tokens.push(childDef.provider.token);
|
||||
}
|
||||
i += childDef.childCount;
|
||||
@ -335,7 +368,7 @@ class DebugContext_ implements DebugContext {
|
||||
|
||||
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
||||
const childDef = this.elView.def.nodes[i];
|
||||
if (childDef.type === NodeType.Provider) {
|
||||
if (childDef.type === NodeType.Provider || childDef.type === NodeType.Directive) {
|
||||
collectReferences(this.elView, childDef, references);
|
||||
}
|
||||
i += childDef.childCount;
|
||||
@ -368,7 +401,7 @@ function findHostElement(view: ViewData): ElementData {
|
||||
view = view.parent;
|
||||
}
|
||||
if (view.parent) {
|
||||
const hostData = asElementData(view.parent, view.parentIndex);
|
||||
const hostData = asElementData(view.parent, viewParentElIndex(view));
|
||||
return hostData;
|
||||
}
|
||||
return undefined;
|
||||
@ -397,6 +430,7 @@ function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
|
||||
if (isViewDebugError(e) || !_currentView) {
|
||||
throw e;
|
||||
}
|
||||
_currentView.state |= ViewState.Errored;
|
||||
throw viewWrappedDebugError(e, getCurrentDebugContext());
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {isDevMode} from '../application_ref';
|
||||
import {looseIdentical} from '../facade/lang';
|
||||
|
||||
import {BindingDef, BindingType, DebugContext, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, TextData, ViewData, ViewFlags, asElementData, asTextData} from './types';
|
||||
import {checkAndUpdateBinding, sliceErrorStack, unwrapValue} from './util';
|
||||
import {checkAndUpdateBinding, sliceErrorStack} from './util';
|
||||
|
||||
export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
// skip the call to sliceErrorStack itself + the call to this function.
|
||||
@ -18,7 +18,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
const bindings: BindingDef[] = new Array(constants.length - 1);
|
||||
for (let i = 1; i < constants.length; i++) {
|
||||
bindings[i - 1] = {
|
||||
type: BindingType.Interpolation,
|
||||
type: BindingType.TextInterpolation,
|
||||
name: undefined,
|
||||
nonMinifiedName: undefined,
|
||||
securityContext: undefined,
|
||||
@ -143,7 +143,6 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
|
||||
}
|
||||
|
||||
function _addInterpolationPart(value: any, binding: BindingDef): string {
|
||||
value = unwrapValue(value);
|
||||
const valueStr = value != null ? value.toString() : '';
|
||||
return valueStr + binding.suffix;
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||
export interface ViewDefinition {
|
||||
flags: ViewFlags;
|
||||
component: ComponentDefinition;
|
||||
update: ViewUpdateFn;
|
||||
updateDirectives: ViewUpdateFn;
|
||||
updateRenderer: ViewUpdateFn;
|
||||
handleEvent: ViewHandleEventFn;
|
||||
/**
|
||||
* Order: Depth first.
|
||||
@ -124,7 +125,9 @@ export interface NodeDef {
|
||||
export enum NodeType {
|
||||
Element,
|
||||
Text,
|
||||
Directive,
|
||||
Provider,
|
||||
Pipe,
|
||||
PureExpression,
|
||||
Query,
|
||||
NgContent
|
||||
@ -163,8 +166,8 @@ export enum BindingType {
|
||||
ElementClass,
|
||||
ElementStyle,
|
||||
ElementProperty,
|
||||
ProviderProperty,
|
||||
Interpolation,
|
||||
DirectiveProperty,
|
||||
TextInterpolation,
|
||||
PureExpressionProperty
|
||||
}
|
||||
|
||||
@ -200,7 +203,7 @@ export interface ProviderDef {
|
||||
tokenKey: string;
|
||||
value: any;
|
||||
deps: DepDef[];
|
||||
outputs: ProviderOutputDef[];
|
||||
outputs: DirectiveOutputDef[];
|
||||
// closure to allow recursive components
|
||||
component: ViewDefinitionFactory;
|
||||
}
|
||||
@ -228,7 +231,7 @@ export enum DepFlags {
|
||||
Value = 2 << 2
|
||||
}
|
||||
|
||||
export interface ProviderOutputDef {
|
||||
export interface DirectiveOutputDef {
|
||||
propName: string;
|
||||
eventName: string;
|
||||
}
|
||||
@ -238,10 +241,7 @@ export interface TextDef {
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface PureExpressionDef {
|
||||
type: PureExpressionType;
|
||||
pipeDep: DepDef;
|
||||
}
|
||||
export interface PureExpressionDef { type: PureExpressionType; }
|
||||
|
||||
export enum PureExpressionType {
|
||||
Array,
|
||||
@ -285,8 +285,7 @@ export interface NgContentDef {
|
||||
export interface ViewData {
|
||||
def: ViewDefinition;
|
||||
root: RootData;
|
||||
// index of parent element / anchor. Not the index
|
||||
// of the provider with the component view.
|
||||
// index of component provider / anchor.
|
||||
parentIndex: number;
|
||||
parent: ViewData;
|
||||
component: any;
|
||||
@ -385,10 +384,7 @@ export function asProviderData(view: ViewData, index: number): ProviderData {
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface PureExpressionData {
|
||||
value: any;
|
||||
pipe: PipeTransform;
|
||||
}
|
||||
export interface PureExpressionData { value: any; }
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
@ -493,7 +489,8 @@ export interface Services {
|
||||
notFoundValue?: any): any;
|
||||
createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
|
||||
handleEvent: ViewHandleEventFn;
|
||||
updateView: ViewUpdateFn;
|
||||
updateDirectives: ViewUpdateFn;
|
||||
updateRenderer: ViewUpdateFn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -513,5 +510,6 @@ export const Services: Services = {
|
||||
resolveDep: undefined,
|
||||
createDebugContext: undefined,
|
||||
handleEvent: undefined,
|
||||
updateView: undefined,
|
||||
updateDirectives: undefined,
|
||||
updateRenderer: undefined,
|
||||
};
|
||||
|
@ -30,16 +30,28 @@ export function tokenKey(token: any): string {
|
||||
return key;
|
||||
}
|
||||
|
||||
let unwrapCounter = 0;
|
||||
|
||||
export function unwrapValue(value: any): any {
|
||||
if (value instanceof WrappedValue) {
|
||||
value = value.wrapped;
|
||||
unwrapCounter++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function checkBinding(
|
||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||
return !!(view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value);
|
||||
return unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) ||
|
||||
!devModeEqual(oldValue, value);
|
||||
}
|
||||
|
||||
export function checkBindingNoChanges(
|
||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
|
||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||
if ((view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
|
||||
if (unwrapCounter || (view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
|
||||
unwrapCounter = 0;
|
||||
throw expressionChangedAfterItHasBeenCheckedError(
|
||||
Services.createDebugContext(view, def.index), oldValue, value,
|
||||
(view.state & ViewState.FirstCheck) !== 0);
|
||||
@ -49,15 +61,10 @@ export function checkBindingNoChanges(
|
||||
export function checkAndUpdateBinding(
|
||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||
const oldValues = view.oldValues;
|
||||
if ((view.state & ViewState.FirstCheck) ||
|
||||
if (unwrapCounter || (view.state & ViewState.FirstCheck) ||
|
||||
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
|
||||
unwrapCounter = 0;
|
||||
oldValues[def.bindingIndex + bindingIdx] = value;
|
||||
if (def.flags & NodeFlags.HasComponent) {
|
||||
const compView = asProviderData(view, def.index).componentView;
|
||||
if (compView.def.flags & ViewFlags.OnPush) {
|
||||
compView.state |= ViewState.ChecksEnabled;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -75,13 +82,6 @@ export function dispatchEvent(
|
||||
return Services.handleEvent(view, nodeIndex, eventName, event);
|
||||
}
|
||||
|
||||
export function unwrapValue(value: any): any {
|
||||
if (value instanceof WrappedValue) {
|
||||
value = value.wrapped;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function declaredViewContainer(view: ViewData): ElementData {
|
||||
if (view.parent) {
|
||||
const parentView = view.parent;
|
||||
@ -91,16 +91,17 @@ export function declaredViewContainer(view: ViewData): ElementData {
|
||||
}
|
||||
|
||||
/**
|
||||
* for component views, this is the same as parentIndex.
|
||||
* for component views, this is the host element.
|
||||
* for embedded views, this is the index of the parent node
|
||||
* that contains the view container.
|
||||
*/
|
||||
export function viewParentDiIndex(view: ViewData): number {
|
||||
if (view.parent && view.context !== view.component) {
|
||||
const parentNodeDef = view.parent.def.nodes[view.parentIndex];
|
||||
return parentNodeDef.parent;
|
||||
export function viewParentElIndex(view: ViewData): number {
|
||||
const parentView = view.parent;
|
||||
if (parentView) {
|
||||
return parentView.def.nodes[view.parentIndex].parent;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return view.parentIndex;
|
||||
}
|
||||
|
||||
export function renderNode(view: ViewData, def: NodeDef): any {
|
||||
@ -119,6 +120,8 @@ export function nodeValue(view: ViewData, index: number): any {
|
||||
return asElementData(view, def.index).renderElement;
|
||||
case NodeType.Text:
|
||||
return asTextData(view, def.index).renderText;
|
||||
case NodeType.Directive:
|
||||
case NodeType.Pipe:
|
||||
case NodeType.Provider:
|
||||
return asProviderData(view, def.index).instance;
|
||||
}
|
||||
@ -183,7 +186,10 @@ export function visitRootRenderNodes(
|
||||
const len = view.def.nodes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
visitRenderNode(view, nodeDef, action, parentNode, nextSibling, target);
|
||||
if (nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text ||
|
||||
nodeDef.type === NodeType.NgContent) {
|
||||
visitRenderNode(view, nodeDef, action, parentNode, nextSibling, target);
|
||||
}
|
||||
// jump to next sibling
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
@ -197,7 +203,7 @@ export function visitProjectedRenderNodes(
|
||||
compView = compView.parent;
|
||||
}
|
||||
const hostView = compView.parent;
|
||||
const hostElDef = hostView.def.nodes[compView.parentIndex];
|
||||
const hostElDef = hostView.def.nodes[viewParentElIndex(compView)];
|
||||
const startIndex = hostElDef.index + 1;
|
||||
const endIndex = hostElDef.index + hostElDef.childCount;
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
|
@ -11,19 +11,19 @@ import {ViewEncapsulation} from '../metadata/view';
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||
import {appendNgContent} from './ng_content';
|
||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProviderInstance} from './provider';
|
||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAndUpdateDirectiveInline, createDirectiveInstance, createPipeInstance, createProviderInstance} from './provider';
|
||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, ComponentDefinition, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
|
||||
import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition} from './util';
|
||||
import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition, viewParentElIndex} from './util';
|
||||
|
||||
const NOOP = (): any => undefined;
|
||||
|
||||
export function viewDef(
|
||||
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, compId?: string, encapsulation?: ViewEncapsulation,
|
||||
styles?: string[]): ViewDefinition {
|
||||
flags: ViewFlags, nodesWithoutIndices: NodeDef[], updateDirectives?: ViewUpdateFn,
|
||||
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn, compId?: string,
|
||||
encapsulation?: ViewEncapsulation, styles?: string[]): ViewDefinition {
|
||||
// clone nodes and set auto calculated values
|
||||
if (nodesWithoutIndices.length === 0) {
|
||||
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
||||
@ -83,7 +83,7 @@ export function viewDef(
|
||||
if (!currentParent) {
|
||||
lastRootNode = node;
|
||||
}
|
||||
if (node.provider) {
|
||||
if (node.type === NodeType.Provider || node.type === NodeType.Directive) {
|
||||
currentParent.element.providerIndices[node.provider.tokenKey] = i;
|
||||
}
|
||||
if (node.query) {
|
||||
@ -108,7 +108,8 @@ export function viewDef(
|
||||
nodeFlags: viewNodeFlags,
|
||||
nodeMatchedQueries: viewMatchedQueries, flags,
|
||||
nodes: nodes, reverseChildNodes,
|
||||
update: update || NOOP,
|
||||
updateDirectives: updateDirectives || NOOP,
|
||||
updateRenderer: updateRenderer || NOOP,
|
||||
handleEvent: handleEvent || NOOP,
|
||||
component: componentDef,
|
||||
bindingCount: viewBindingCount,
|
||||
@ -172,18 +173,18 @@ function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
|
||||
`Illegal State: Last root node of a template can't have embedded views, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.provider) {
|
||||
if (node.type === NodeType.Provider || node.type === NodeType.Directive) {
|
||||
const parentType = parent ? parent.type : null;
|
||||
if (parentType !== NodeType.Element) {
|
||||
throw new Error(
|
||||
`Illegal State: Provider nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
`Illegal State: Provider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.query) {
|
||||
const parentType = parent ? parent.type : null;
|
||||
if (parentType !== NodeType.Provider) {
|
||||
if (parentType !== NodeType.Directive) {
|
||||
throw new Error(
|
||||
`Illegal State: Query nodes need to be children of providers, at index ${node.index}!`);
|
||||
`Illegal State: Query nodes need to be children of directives, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.childCount) {
|
||||
@ -282,7 +283,7 @@ function initView(view: ViewData, component: any, context: any) {
|
||||
function createViewNodes(view: ViewData) {
|
||||
let renderHost: any;
|
||||
if (isComponentView(view)) {
|
||||
renderHost = asElementData(view.parent, view.parentIndex).renderElement;
|
||||
renderHost = asElementData(view.parent, viewParentElIndex(view)).renderElement;
|
||||
}
|
||||
|
||||
const def = view.def;
|
||||
@ -297,23 +298,36 @@ function createViewNodes(view: ViewData) {
|
||||
case NodeType.Text:
|
||||
nodes[i] = createText(view, renderHost, nodeDef) as any;
|
||||
break;
|
||||
case NodeType.Provider:
|
||||
if (nodeDef.provider.component) {
|
||||
case NodeType.Provider: {
|
||||
const instance = createProviderInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
break;
|
||||
}
|
||||
case NodeType.Pipe: {
|
||||
const instance = createPipeInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
break;
|
||||
}
|
||||
case NodeType.Directive: {
|
||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||
// Components can inject a ChangeDetectorRef that needs a references to
|
||||
// the component view. Therefore, we create the component view first
|
||||
// and set the ProviderData in ViewData, and then instantiate the provider.
|
||||
const componentView = createView(
|
||||
view.root, view, nodeDef.parent, resolveViewDefinition(nodeDef.provider.component));
|
||||
view.root, view, nodeDef.index, resolveViewDefinition(nodeDef.provider.component));
|
||||
const providerData = <ProviderData>{componentView, instance: undefined};
|
||||
nodes[i] = providerData as any;
|
||||
const instance = providerData.instance = createProviderInstance(view, nodeDef);
|
||||
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
|
||||
initView(componentView, instance, instance);
|
||||
} else {
|
||||
const instance = createProviderInstance(view, nodeDef);
|
||||
const instance = createDirectiveInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeType.PureExpression:
|
||||
nodes[i] = createPureExpression(view, nodeDef) as any;
|
||||
break;
|
||||
@ -333,21 +347,25 @@ function createViewNodes(view: ViewData) {
|
||||
}
|
||||
|
||||
export function checkNoChangesView(view: ViewData) {
|
||||
Services.updateView(checkNoChangesNode, view);
|
||||
Services.updateDirectives(checkNoChangesNode, view);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
|
||||
Services.updateRenderer(checkNoChangesNode, view);
|
||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
|
||||
}
|
||||
|
||||
export function checkAndUpdateView(view: ViewData) {
|
||||
Services.updateView(checkAndUpdateNode, view);
|
||||
Services.updateDirectives(checkAndUpdateNode, view);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterContentChecked |
|
||||
(view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
|
||||
|
||||
Services.updateRenderer(checkAndUpdateNode, view);
|
||||
|
||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
|
||||
|
||||
@ -380,8 +398,8 @@ function checkAndUpdateNodeInline(
|
||||
return checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
case NodeType.Text:
|
||||
return checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
case NodeType.Provider:
|
||||
return checkAndUpdateProviderInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
case NodeType.Directive:
|
||||
return checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
case NodeType.PureExpression:
|
||||
return checkAndUpdatePureExpressionInline(
|
||||
view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
@ -395,8 +413,8 @@ function checkAndUpdateNodeDynamic(view: ViewData, nodeIndex: number, values: an
|
||||
return checkAndUpdateElementDynamic(view, nodeDef, values);
|
||||
case NodeType.Text:
|
||||
return checkAndUpdateTextDynamic(view, nodeDef, values);
|
||||
case NodeType.Provider:
|
||||
return checkAndUpdateProviderDynamic(view, nodeDef, values);
|
||||
case NodeType.Directive:
|
||||
return checkAndUpdateDirectiveDynamic(view, nodeDef, values);
|
||||
case NodeType.PureExpression:
|
||||
return checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
|
||||
}
|
||||
@ -462,14 +480,14 @@ function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
|
||||
}
|
||||
|
||||
export function destroyView(view: ViewData) {
|
||||
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
||||
execComponentViewsAction(view, ViewAction.Destroy);
|
||||
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
||||
if (view.disposables) {
|
||||
for (let i = 0; i < view.disposables.length; i++) {
|
||||
view.disposables[i]();
|
||||
}
|
||||
}
|
||||
execComponentViewsAction(view, ViewAction.Destroy);
|
||||
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
||||
view.state |= ViewState.Destroyed;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
|
||||
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
|
||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
@ -19,6 +20,19 @@ import {MockSchemaRegistry} from '../../../compiler/testing/index';
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
|
||||
export function main() {
|
||||
describe('Current compiler', () => { createTests({viewEngine: false}); });
|
||||
|
||||
describe('View Engine compiler', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(
|
||||
{useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]});
|
||||
});
|
||||
|
||||
createTests({viewEngine: true});
|
||||
});
|
||||
}
|
||||
|
||||
function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
let elSchema: MockSchemaRegistry;
|
||||
let renderLog: RenderLog;
|
||||
let directiveLog: DirectiveLog;
|
||||
@ -1080,7 +1094,8 @@ export function main() {
|
||||
|
||||
ctx.destroy();
|
||||
|
||||
expect(directiveLog.filter(['ngOnDestroy'])).toEqual([
|
||||
// We don't care about the exact order in this test.
|
||||
expect(directiveLog.filter(['ngOnDestroy']).sort()).toEqual([
|
||||
'dir.ngOnDestroy', 'injectable.ngOnDestroy'
|
||||
]);
|
||||
}));
|
||||
@ -1092,8 +1107,9 @@ export function main() {
|
||||
it('should throw when a record gets changed after it has been checked', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div [someProp]="a"></div>', TestData);
|
||||
ctx.componentInstance.a = 1;
|
||||
|
||||
expect(() => ctx.checkNoChanges())
|
||||
.toThrowError(/:0:5[\s\S]*Expression has changed after it was checked./g);
|
||||
.toThrowError(/Expression has changed after it was checked./g);
|
||||
}));
|
||||
|
||||
it('should warn when the view has been created in a cd hook', fakeAsync(() => {
|
||||
@ -1216,26 +1232,28 @@ export function main() {
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
// TODO(tbosch): ViewQueries don't work yet with the view engine...
|
||||
viewEngine ||
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
});
|
||||
|
||||
describe('pipes', () => {
|
||||
viewEngine || it('should support pipes in bindings', () => {
|
||||
it('should support pipes in bindings', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
|
||||
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
@ -545,7 +545,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
});
|
||||
|
||||
describe('variables', () => {
|
||||
viewEngine || it('should allow to use variables in a for loop', () => {
|
||||
it('should allow to use variables in a for loop', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]});
|
||||
const template =
|
||||
'<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>';
|
||||
@ -670,31 +670,29 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
viewEngine ||
|
||||
it('should be checked when an async pipe requests a check', fakeAsync(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
|
||||
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
it('should be checked when an async pipe requests a check', fakeAsync(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
|
||||
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
tick();
|
||||
tick();
|
||||
|
||||
const cmp: PushCmpWithAsyncPipe =
|
||||
fixture.debugElement.children[0].references['cmp'];
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references['cmp'];
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
fixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
|
||||
cmp.resolve(2);
|
||||
tick();
|
||||
cmp.resolve(2);
|
||||
tick();
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(2);
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(2);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@ -897,26 +895,24 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
expect(tc.nativeElement.id).toEqual('newId');
|
||||
});
|
||||
|
||||
viewEngine ||
|
||||
it('should not use template variables for expressions in hostProperties', () => {
|
||||
@Directive(
|
||||
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
|
||||
class DirectiveWithHostProps {
|
||||
id = 'one';
|
||||
}
|
||||
it('should not use template variables for expressions in hostProperties', () => {
|
||||
@Directive({selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
|
||||
class DirectiveWithHostProps {
|
||||
id = 'one';
|
||||
}
|
||||
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
|
||||
.overrideComponent(MyComp, {
|
||||
set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}
|
||||
})
|
||||
.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
|
||||
.overrideComponent(
|
||||
MyComp,
|
||||
{set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}})
|
||||
.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const tc = fixture.debugElement.children[0];
|
||||
expect(tc.properties['id']).toBe('one');
|
||||
expect(tc.properties['title']).toBe(undefined);
|
||||
});
|
||||
const tc = fixture.debugElement.children[0];
|
||||
expect(tc.properties['id']).toBe('one');
|
||||
expect(tc.properties['title']).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not allow pipes in hostProperties', () => {
|
||||
@Directive({selector: '[host-properties]', host: {'[id]': 'id | uppercase'}})
|
||||
@ -930,8 +926,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
.toThrowError(/Host binding expression cannot contain pipes/);
|
||||
});
|
||||
|
||||
// TODO: literal array
|
||||
viewEngine || it('should not use template variables for expressions in hostListeners', () => {
|
||||
it('should not use template variables for expressions in hostListeners', () => {
|
||||
@Directive({selector: '[host-listener]', host: {'(click)': 'doIt(id, unknownProp)'}})
|
||||
class DirectiveWithHostListener {
|
||||
id = 'one';
|
||||
|
@ -16,8 +16,9 @@ import {createRootView, isBrowser} from './helper';
|
||||
export function main() {
|
||||
describe(`View Anchor`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
|
@ -16,9 +16,9 @@ import {createRootView, isBrowser, removeNodes} from './helper';
|
||||
export function main() {
|
||||
describe(`Component Views`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
@ -119,7 +119,7 @@ export function main() {
|
||||
directiveDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||
], update
|
||||
], null, update
|
||||
)),
|
||||
]));
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
@ -194,7 +194,7 @@ export function main() {
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']),
|
||||
],
|
||||
update, null, ViewFlags.OnPush)),
|
||||
update, null, null, ViewFlags.OnPush)),
|
||||
],
|
||||
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
|
||||
|
||||
@ -246,7 +246,7 @@ export function main() {
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||
],
|
||||
update)),
|
||||
null, update)),
|
||||
]));
|
||||
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
@ -17,9 +17,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, re
|
||||
export function main() {
|
||||
describe(`View Elements`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
@ -85,7 +85,7 @@ export function main() {
|
||||
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
||||
]),
|
||||
],
|
||||
(check, view) => {
|
||||
null, (check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||
}));
|
||||
|
||||
@ -112,7 +112,7 @@ export function main() {
|
||||
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
||||
]),
|
||||
],
|
||||
(check, view) => {
|
||||
null, (check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||
}));
|
||||
|
||||
@ -159,7 +159,7 @@ export function main() {
|
||||
[BindingType.ElementStyle, 'color', null]
|
||||
]),
|
||||
],
|
||||
(check, view) => {
|
||||
null, (check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [10, 'red']);
|
||||
}));
|
||||
|
||||
@ -172,42 +172,6 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('general binding behavior', () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(
|
||||
NodeFlags.None, null, null, 0, 'input', null,
|
||||
[
|
||||
[BindingType.ElementProperty, 'someProp', SecurityContext.NONE],
|
||||
]),
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const setterSpy = jasmine.createSpy('set');
|
||||
Object.defineProperty(rootNodes[0], 'someProp', {set: setterSpy});
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (isBrowser()) {
|
||||
describe('listen to DOM events', () => {
|
||||
function createAndAttachAndGetRootNodes(viewDef: ViewDefinition):
|
||||
@ -228,7 +192,7 @@ export function main() {
|
||||
spyOn(HTMLElement.prototype, 'removeEventListener').and.callThrough();
|
||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
|
||||
handleEventSpy));
|
||||
null, handleEventSpy));
|
||||
|
||||
rootNodes[0].click();
|
||||
|
||||
@ -252,7 +216,7 @@ export function main() {
|
||||
[elementDef(
|
||||
NodeFlags.None, null, null, 0, 'button', null, null,
|
||||
[['window', 'windowClick']])],
|
||||
null, handleEventSpy));
|
||||
null, null, handleEventSpy));
|
||||
|
||||
expect(addListenerSpy).toHaveBeenCalled();
|
||||
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('windowClick');
|
||||
@ -278,7 +242,7 @@ export function main() {
|
||||
[elementDef(
|
||||
NodeFlags.None, null, null, 0, 'button', null, null,
|
||||
[['document', 'documentClick']])],
|
||||
null, handleEventSpy));
|
||||
null, null, handleEventSpy));
|
||||
|
||||
expect(addListenerSpy).toHaveBeenCalled();
|
||||
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('documentClick');
|
||||
@ -302,7 +266,7 @@ export function main() {
|
||||
|
||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
|
||||
(view, index, eventName, event) => {
|
||||
null, (view, index, eventName, event) => {
|
||||
preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough();
|
||||
return eventHandlerResult;
|
||||
}));
|
||||
@ -328,7 +292,7 @@ export function main() {
|
||||
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
||||
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
|
||||
() => { throw new Error('Test'); }));
|
||||
null, () => { throw new Error('Test'); }));
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
|
@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
|
||||
export function main() {
|
||||
describe(`Embedded Views`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
|
||||
|
@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
|
||||
export function main() {
|
||||
describe(`View NgContent`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
|
||||
|
@ -17,10 +17,11 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} fr
|
||||
export function main() {
|
||||
describe(`View Providers`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(
|
||||
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
|
||||
viewFlags, nodes, updateDirectives, updateRenderer, handleEvent, 'someCompId',
|
||||
ViewEncapsulation.None, []);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
|
||||
@ -59,7 +60,8 @@ export function main() {
|
||||
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 2, 'span'),
|
||||
directiveDef(NodeFlags.LazyProvider, null, 0, LazyService, []),
|
||||
providerDef(
|
||||
NodeFlags.LazyProvider, null, ProviderType.Class, LazyService, LazyService, []),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Injector])
|
||||
]));
|
||||
|
||||
@ -330,37 +332,6 @@ export function main() {
|
||||
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
let setterSpy = jasmine.createSpy('set');
|
||||
|
||||
class SomeService {
|
||||
set a(value: any) { setterSpy(value); }
|
||||
}
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -388,7 +359,7 @@ export function main() {
|
||||
directiveDef(
|
||||
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
|
||||
],
|
||||
null, handleEvent));
|
||||
null, null, handleEvent));
|
||||
|
||||
emitter.emit('someEventInstance');
|
||||
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
|
||||
@ -410,7 +381,7 @@ export function main() {
|
||||
directiveDef(
|
||||
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
|
||||
],
|
||||
null, () => { throw new Error('Test'); }));
|
||||
null, null, () => { throw new Error('Test'); }));
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
||||
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, nodeValue, pipeDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helper';
|
||||
@ -15,9 +15,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helpe
|
||||
export function main() {
|
||||
describe(`View Pure Expressions`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
@ -64,32 +64,6 @@ export function main() {
|
||||
expect(arr1).toEqual([3, 2]);
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
pureArrayDef(1),
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const exprData = asPureExpressionData(view, 1);
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
const v1Arr = exprData.value;
|
||||
expect(v1Arr).toEqual(['v1']);
|
||||
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).toBe(v1Arr);
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).not.toBe(v1Arr);
|
||||
expect(exprData.value).toEqual(['v1']);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -127,32 +101,6 @@ export function main() {
|
||||
expect(obj1).toEqual({a: 3, b: 2});
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
pureObjectDef(['a']),
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const exprData = asPureExpressionData(view, 1);
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
const v1Obj = exprData.value;
|
||||
expect(v1Obj).toEqual({'a': 'v1'});
|
||||
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).toBe(v1Obj);
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).not.toBe(v1Obj);
|
||||
expect(exprData.value).toEqual({'a': 'v1'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -168,11 +116,12 @@ export function main() {
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 3, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
|
||||
pipeDef(NodeFlags.None, SomePipe, []), purePipeDef(2),
|
||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(check, view) => {
|
||||
const pureValue = checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, values);
|
||||
const pureValue = checkNodeInlineOrDynamic(
|
||||
check, view, 2, inlineDynamic, [nodeValue(view, 1)].concat(values));
|
||||
checkNodeInlineOrDynamic(check, view, 3, inlineDynamic, [pureValue]);
|
||||
}));
|
||||
const service = asProviderData(view, 3).instance;
|
||||
@ -194,36 +143,6 @@ export function main() {
|
||||
expect(obj1).toEqual([13, 22]);
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
let transformSpy = jasmine.createSpy('transform');
|
||||
|
||||
class SomePipe implements PipeTransform {
|
||||
transform = transformSpy;
|
||||
}
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 2, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []),
|
||||
purePipeDef(SomePipe, 1),
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
transformSpy.calls.reset();
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).not.toHaveBeenCalled();
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -17,9 +17,9 @@ import {createRootView} from './helper';
|
||||
export function main() {
|
||||
describe(`Query Views`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
|
||||
|
@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
|
||||
export function main() {
|
||||
describe('View Services', () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
|
@ -16,9 +16,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} fr
|
||||
export function main() {
|
||||
describe(`View Text`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
@ -67,7 +67,7 @@ export function main() {
|
||||
[
|
||||
textDef(null, ['0', '1', '2']),
|
||||
],
|
||||
(check, view) => {
|
||||
null, (check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['a', 'b']);
|
||||
}));
|
||||
|
||||
@ -77,41 +77,6 @@ export function main() {
|
||||
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
|
||||
});
|
||||
|
||||
if (isBrowser()) {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const setterSpy = jasmine.createSpy('set');
|
||||
|
||||
class FakeTextNode {
|
||||
set nodeValue(value: any) { setterSpy(value); }
|
||||
}
|
||||
|
||||
spyOn(document, 'createTextNode').and.returnValue(new FakeTextNode());
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
textDef(null, ['', '']),
|
||||
],
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
Object.defineProperty(rootNodes[0], 'nodeValue', {set: setterSpy});
|
||||
|
||||
bindingValue = 'v1';
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user