refactor(core): view engine - misc

- fix bug when detaching view from `ApplicationRef`
- fix integration of adding `ng-reflect` attributes
  in debug mode.
This commit is contained in:
Tobias Bosch 2017-02-22 10:05:56 -08:00 committed by Igor Minar
parent 5049a50bf6
commit ab3527c99b
18 changed files with 307 additions and 223 deletions

View File

@ -255,7 +255,7 @@ export class Identifiers {
static pureArrayDef: static pureArrayDef:
IdentifierSpec = {name: 'ɵpureArrayDef', moduleUrl: CORE, runtime: ɵpureArrayDef}; IdentifierSpec = {name: 'ɵpureArrayDef', moduleUrl: CORE, runtime: ɵpureArrayDef};
static pureObjectDef: static pureObjectDef:
IdentifierSpec = {name: 'ɵpureObjectRef', moduleUrl: CORE, runtime: ɵpureObjectDef}; IdentifierSpec = {name: 'ɵpureObjectDef', moduleUrl: CORE, runtime: ɵpureObjectDef};
static purePipeDef: static purePipeDef:
IdentifierSpec = {name: 'ɵpurePipeDef', moduleUrl: CORE, runtime: ɵpurePipeDef}; IdentifierSpec = {name: 'ɵpurePipeDef', moduleUrl: CORE, runtime: ɵpurePipeDef};
static pipeDef: IdentifierSpec = {name: 'ɵpipeDef', moduleUrl: CORE, runtime: ɵpipeDef}; static pipeDef: IdentifierSpec = {name: 'ɵpipeDef', moduleUrl: CORE, runtime: ɵpipeDef};

View File

@ -447,7 +447,9 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
dirAst.directive.queries.forEach((query, queryIndex) => { dirAst.directive.queries.forEach((query, queryIndex) => {
let flags = NodeFlags.HasContentQuery; let flags = NodeFlags.HasContentQuery;
const queryId = dirAst.contentQueryStartId + queryIndex; const queryId = dirAst.contentQueryStartId + queryIndex;
if (queryIds.staticQueryIds.has(queryId)) { // Note: We only make queries static that query for a single item.
// This is because of backwards compatibility with the old view compiler...
if (queryIds.staticQueryIds.has(queryId) && query.first) {
flags |= NodeFlags.HasStaticQuery; flags |= NodeFlags.HasStaticQuery;
} else { } else {
flags |= NodeFlags.HasDynamicQuery; flags |= NodeFlags.HasDynamicQuery;

View File

@ -493,7 +493,7 @@ export class ApplicationRef_ extends ApplicationRef {
detachView(viewRef: ViewRef): void { detachView(viewRef: ViewRef): void {
const view = (viewRef as InternalViewRef); const view = (viewRef as InternalViewRef);
ListWrapper.remove(this._views, view); ListWrapper.remove(this._views, view);
view.detachFromContainer(); view.detachFromAppRef();
} }
bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>): ComponentRef<C> { bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>): ComponentRef<C> {

View File

@ -89,7 +89,7 @@ export abstract class EmbeddedViewRef<C> extends ViewRef {
} }
export interface InternalViewRef extends ViewRef { export interface InternalViewRef extends ViewRef {
detachFromContainer(): void; detachFromAppRef(): void;
attachToAppRef(appRef: ApplicationRef): void; attachToAppRef(appRef: ApplicationRef): void;
} }
@ -131,7 +131,7 @@ export class ViewRef_<C> implements EmbeddedViewRef<C>, ChangeDetectorRef, Inter
destroy() { this._view.detachAndDestroy(); } destroy() { this._view.detachAndDestroy(); }
detachFromContainer() { this._view.detach(); } detachFromAppRef() { this._view.detach(); }
attachToAppRef(appRef: ApplicationRef) { this._view.attachToAppRef(appRef); } attachToAppRef(appRef: ApplicationRef) { this._view.attachToAppRef(appRef); }
} }

View File

@ -213,29 +213,33 @@ function renderEventHandlerClosure(view: ViewData, index: number, eventName: str
export function checkAndUpdateElementInline( export function checkAndUpdateElementInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any) { v7: any, v8: any, v9: any): boolean {
const bindLen = def.bindings.length; const bindLen = def.bindings.length;
if (bindLen > 0) checkAndUpdateElementValue(view, def, 0, v0); let changed = false;
if (bindLen > 1) checkAndUpdateElementValue(view, def, 1, v1); if (bindLen > 0 && checkAndUpdateElementValue(view, def, 0, v0)) changed = true;
if (bindLen > 2) checkAndUpdateElementValue(view, def, 2, v2); if (bindLen > 1 && checkAndUpdateElementValue(view, def, 1, v1)) changed = true;
if (bindLen > 3) checkAndUpdateElementValue(view, def, 3, v3); if (bindLen > 2 && checkAndUpdateElementValue(view, def, 2, v2)) changed = true;
if (bindLen > 4) checkAndUpdateElementValue(view, def, 4, v4); if (bindLen > 3 && checkAndUpdateElementValue(view, def, 3, v3)) changed = true;
if (bindLen > 5) checkAndUpdateElementValue(view, def, 5, v5); if (bindLen > 4 && checkAndUpdateElementValue(view, def, 4, v4)) changed = true;
if (bindLen > 6) checkAndUpdateElementValue(view, def, 6, v6); if (bindLen > 5 && checkAndUpdateElementValue(view, def, 5, v5)) changed = true;
if (bindLen > 7) checkAndUpdateElementValue(view, def, 7, v7); if (bindLen > 6 && checkAndUpdateElementValue(view, def, 6, v6)) changed = true;
if (bindLen > 8) checkAndUpdateElementValue(view, def, 8, v8); if (bindLen > 7 && checkAndUpdateElementValue(view, def, 7, v7)) changed = true;
if (bindLen > 9) checkAndUpdateElementValue(view, def, 9, v9); if (bindLen > 8 && checkAndUpdateElementValue(view, def, 8, v8)) changed = true;
if (bindLen > 9 && checkAndUpdateElementValue(view, def, 9, v9)) changed = true;
return changed;
} }
export function checkAndUpdateElementDynamic(view: ViewData, def: NodeDef, values: any[]) { export function checkAndUpdateElementDynamic(view: ViewData, def: NodeDef, values: any[]): boolean {
let changed = false;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
checkAndUpdateElementValue(view, def, i, values[i]); if (checkAndUpdateElementValue(view, def, i, values[i])) changed = true;
} }
return changed;
} }
function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: number, value: any) { function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
if (!checkAndUpdateBinding(view, def, bindingIdx, value)) { if (!checkAndUpdateBinding(view, def, bindingIdx, value)) {
return; return false;
} }
const binding = def.bindings[bindingIdx]; const binding = def.bindings[bindingIdx];
const elData = asElementData(view, def.index); const elData = asElementData(view, def.index);
@ -258,6 +262,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
setElementProperty(elData.componentView, binding, renderNode, name, value); setElementProperty(elData.componentView, binding, renderNode, name, value);
break; break;
} }
return true;
} }
function setElementAttribute( function setElementAttribute(

View File

@ -16,7 +16,7 @@ import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} f
import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs'; import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs';
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types'; import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util'; import {checkBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
const RendererV1TokenKey = tokenKey(RendererV1); const RendererV1TokenKey = tokenKey(RendererV1);
const RendererV2TokenKey = tokenKey(RendererV2); const RendererV2TokenKey = tokenKey(RendererV2);
@ -156,21 +156,52 @@ function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
export function checkAndUpdateDirectiveInline( export function checkAndUpdateDirectiveInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any) { v7: any, v8: any, v9: any): boolean {
const providerData = asProviderData(view, def.index); const providerData = asProviderData(view, def.index);
const directive = providerData.instance; const directive = providerData.instance;
let changed = false;
let changes: SimpleChanges; let changes: SimpleChanges;
const bindLen = def.bindings.length; const bindLen = def.bindings.length;
if (bindLen > 0) changes = checkAndUpdateProp(view, providerData, def, 0, v0, changes); if (bindLen > 0 && checkBinding(view, def, 0, v0)) {
if (bindLen > 1) changes = checkAndUpdateProp(view, providerData, def, 1, v1, changes); changed = true;
if (bindLen > 2) changes = checkAndUpdateProp(view, providerData, def, 2, v2, changes); changes = updateProp(view, providerData, def, 0, v0, changes);
if (bindLen > 3) changes = checkAndUpdateProp(view, providerData, def, 3, v3, changes); };
if (bindLen > 4) changes = checkAndUpdateProp(view, providerData, def, 4, v4, changes); if (bindLen > 1 && checkBinding(view, def, 1, v1)) {
if (bindLen > 5) changes = checkAndUpdateProp(view, providerData, def, 5, v5, changes); changed = true;
if (bindLen > 6) changes = checkAndUpdateProp(view, providerData, def, 6, v6, changes); changes = updateProp(view, providerData, def, 1, v1, changes);
if (bindLen > 7) changes = checkAndUpdateProp(view, providerData, def, 7, v7, changes); };
if (bindLen > 8) changes = checkAndUpdateProp(view, providerData, def, 8, v8, changes); if (bindLen > 2 && checkBinding(view, def, 2, v2)) {
if (bindLen > 9) changes = checkAndUpdateProp(view, providerData, def, 9, v9, changes); changed = true;
changes = updateProp(view, providerData, def, 2, v2, changes);
};
if (bindLen > 3 && checkBinding(view, def, 3, v3)) {
changed = true;
changes = updateProp(view, providerData, def, 3, v3, changes);
};
if (bindLen > 4 && checkBinding(view, def, 4, v4)) {
changed = true;
changes = updateProp(view, providerData, def, 4, v4, changes);
};
if (bindLen > 5 && checkBinding(view, def, 5, v5)) {
changed = true;
changes = updateProp(view, providerData, def, 5, v5, changes);
};
if (bindLen > 6 && checkBinding(view, def, 6, v6)) {
changed = true;
changes = updateProp(view, providerData, def, 6, v6, changes);
};
if (bindLen > 7 && checkBinding(view, def, 7, v7)) {
changed = true;
changes = updateProp(view, providerData, def, 7, v7, changes);
};
if (bindLen > 8 && checkBinding(view, def, 8, v8)) {
changed = true;
changes = updateProp(view, providerData, def, 8, v8, changes);
};
if (bindLen > 9 && checkBinding(view, def, 9, v9)) {
changed = true;
changes = updateProp(view, providerData, def, 9, v9, changes);
};
if (changes) { if (changes) {
directive.ngOnChanges(changes); directive.ngOnChanges(changes);
} }
@ -180,14 +211,20 @@ export function checkAndUpdateDirectiveInline(
if (def.flags & NodeFlags.DoCheck) { if (def.flags & NodeFlags.DoCheck) {
directive.ngDoCheck(); directive.ngDoCheck();
} }
return changed;
} }
export function checkAndUpdateDirectiveDynamic(view: ViewData, def: NodeDef, values: any[]) { export function checkAndUpdateDirectiveDynamic(
view: ViewData, def: NodeDef, values: any[]): boolean {
const providerData = asProviderData(view, def.index); const providerData = asProviderData(view, def.index);
const directive = providerData.instance; const directive = providerData.instance;
let changed = false;
let changes: SimpleChanges; let changes: SimpleChanges;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
changes = checkAndUpdateProp(view, providerData, def, i, values[i], changes); if (checkBinding(view, def, i, values[i])) {
changed = true;
changes = updateProp(view, providerData, def, i, values[i], changes);
}
} }
if (changes) { if (changes) {
directive.ngOnChanges(changes); directive.ngOnChanges(changes);
@ -198,6 +235,7 @@ export function checkAndUpdateDirectiveDynamic(view: ViewData, def: NodeDef, val
if (def.flags & NodeFlags.DoCheck) { if (def.flags & NodeFlags.DoCheck) {
directive.ngDoCheck(); directive.ngDoCheck();
} }
return changed;
} }
function _createProviderInstance(view: ViewData, def: NodeDef): any { function _createProviderInstance(view: ViewData, def: NodeDef): any {
@ -366,21 +404,9 @@ function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: bool
return compView; return compView;
} }
function checkAndUpdateProp( function updateProp(
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any, view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
changes: SimpleChanges): SimpleChanges { changes: SimpleChanges): SimpleChanges {
let change: SimpleChange;
let changed: boolean;
if (def.flags & NodeFlags.OnChanges) {
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
change = changed ?
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0) :
null;
} else {
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
}
if (changed) {
if (def.flags & NodeFlags.IsComponent) { if (def.flags & NodeFlags.IsComponent) {
const compView = asElementData(view, def.parent.index).componentView; const compView = asElementData(view, def.parent.index).componentView;
if (compView.def.flags & ViewFlags.OnPush) { if (compView.def.flags & ViewFlags.OnPush) {
@ -393,11 +419,14 @@ function checkAndUpdateProp(
// the user passed in the property name as an object has to `providerDef`, // the user passed in the property name as an object has to `providerDef`,
// so Closure Compiler will have renamed the property correctly already. // so Closure Compiler will have renamed the property correctly already.
providerData.instance[propName] = value; providerData.instance[propName] = value;
if (change) { if (def.flags & NodeFlags.OnChanges) {
changes = changes || {}; changes = changes || {};
changes[binding.nonMinifiedName] = change; const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
} const binding = def.bindings[bindingIdx];
changes[binding.nonMinifiedName] =
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
} }
view.oldValues[def.bindingIndex + bindingIdx] = value;
return changes; return changes;
} }

View File

@ -69,7 +69,7 @@ export function createPureExpression(view: ViewData, def: NodeDef): PureExpressi
export function checkAndUpdatePureExpressionInline( export function checkAndUpdatePureExpressionInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any) { v7: any, v8: any, v9: any): boolean {
const bindings = def.bindings; const bindings = def.bindings;
let changed = false; let changed = false;
const bindLen = bindings.length; const bindLen = bindings.length;
@ -84,8 +84,8 @@ export function checkAndUpdatePureExpressionInline(
if (bindLen > 8 && checkAndUpdateBinding(view, def, 8, v8)) changed = true; if (bindLen > 8 && checkAndUpdateBinding(view, def, 8, v8)) changed = true;
if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true; if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true;
const data = asPureExpressionData(view, def.index);
if (changed) { if (changed) {
const data = asPureExpressionData(view, def.index);
let value: any; let value: any;
switch (def.pureExpression.type) { switch (def.pureExpression.type) {
case PureExpressionType.Array: case PureExpressionType.Array:
@ -152,10 +152,11 @@ export function checkAndUpdatePureExpressionInline(
} }
data.value = value; data.value = value;
} }
return data.value; return changed;
} }
export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef, values: any[]) { export function checkAndUpdatePureExpressionDynamic(
view: ViewData, def: NodeDef, values: any[]): boolean {
const bindings = def.bindings; const bindings = def.bindings;
let changed = false; let changed = false;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
@ -165,8 +166,8 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
changed = true; changed = true;
} }
} }
const data = asPureExpressionData(view, def.index);
if (changed) { if (changed) {
const data = asPureExpressionData(view, def.index);
let value: any; let value: any;
switch (def.pureExpression.type) { switch (def.pureExpression.type) {
case PureExpressionType.Array: case PureExpressionType.Array:
@ -186,5 +187,5 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
} }
data.value = value; data.value = value;
} }
return data.value; return changed;
} }

View File

@ -20,7 +20,8 @@ import {Type} from '../type';
import {VERSION} from '../version'; import {VERSION} from '../version';
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types'; import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util'; import {isComponentView, markParentViewsForCheck, renderNode, resolveViewDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';
const EMPTY_CONTEXT = new Object(); const EMPTY_CONTEXT = new Object();
@ -98,14 +99,13 @@ class ViewContainerRef_ implements ViewContainerRef {
clear(): void { clear(): void {
const len = this._data.embeddedViews.length; const len = this._data.embeddedViews.length;
for (let i = len - 1; i >= 0; i--) { for (let i = len - 1; i >= 0; i--) {
const view = Services.detachEmbeddedView(this._data, i); const view = detachEmbeddedView(this._data, i);
Services.destroyView(view); Services.destroyView(view);
} }
} }
get(index: number): ViewRef { return this._getViewRef(this._data.embeddedViews[index]); } get(index: number): ViewRef {
const view = this._data.embeddedViews[index];
private _getViewRef(view: ViewData) {
if (view) { if (view) {
const ref = new ViewRef_(view); const ref = new ViewRef_(view);
ref.attachToViewContainerRef(this); ref.attachToViewContainerRef(this);
@ -135,14 +135,14 @@ class ViewContainerRef_ implements ViewContainerRef {
insert(viewRef: ViewRef, index?: number): ViewRef { insert(viewRef: ViewRef, index?: number): ViewRef {
const viewRef_ = <ViewRef_>viewRef; const viewRef_ = <ViewRef_>viewRef;
const viewData = viewRef_._view; const viewData = viewRef_._view;
Services.attachEmbeddedView(this._data, index, viewData); attachEmbeddedView(this._view, this._data, index, viewData);
viewRef_.attachToViewContainerRef(this); viewRef_.attachToViewContainerRef(this);
return viewRef; return viewRef;
} }
move(viewRef: ViewRef_, currentIndex: number): ViewRef { move(viewRef: ViewRef_, currentIndex: number): ViewRef {
const previousIndex = this._data.embeddedViews.indexOf(viewRef._view); const previousIndex = this._data.embeddedViews.indexOf(viewRef._view);
Services.moveEmbeddedView(this._data, previousIndex, currentIndex); moveEmbeddedView(this._data, previousIndex, currentIndex);
return viewRef; return viewRef;
} }
@ -151,20 +151,15 @@ class ViewContainerRef_ implements ViewContainerRef {
} }
remove(index?: number): void { remove(index?: number): void {
const viewData = Services.detachEmbeddedView(this._data, index); const viewData = detachEmbeddedView(this._data, index);
if (viewData) { if (viewData) {
Services.destroyView(viewData); Services.destroyView(viewData);
} }
} }
detach(index?: number): ViewRef { detach(index?: number): ViewRef {
const view = Services.detachEmbeddedView(this._data, index); const view = detachEmbeddedView(this._data, index);
if (view) { return view ? new ViewRef_(view) : null;
const viewRef = this._getViewRef(view);
viewRef.detachFromContainer();
return viewRef;
}
return null;
} }
} }
@ -190,7 +185,7 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; } get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; }
markForCheck(): void { this.reattach(); } markForCheck(): void { markParentViewsForCheck(this._view); }
detach(): void { this._view.state &= ~ViewState.ChecksEnabled; } detach(): void { this._view.state &= ~ViewState.ChecksEnabled; }
detectChanges(): void { Services.checkAndUpdateView(this._view); } detectChanges(): void { Services.checkAndUpdateView(this._view); }
checkNoChanges(): void { Services.checkNoChangesView(this._view); } checkNoChanges(): void { Services.checkNoChangesView(this._view); }
@ -212,9 +207,10 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
Services.destroyView(this._view); Services.destroyView(this._view);
} }
detachFromContainer() { detachFromAppRef() {
this._appRef = null; this._appRef = null;
this._viewContainerRef = null; renderDetachView(this._view);
Services.dirtyParentQueries(this._view);
} }
attachToAppRef(appRef: ApplicationRef) { attachToAppRef(appRef: ApplicationRef) {

View File

@ -14,12 +14,11 @@ import {Sanitizer, SecurityContext} from '../security';
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors'; import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
import {resolveDep} from './provider'; import {resolveDep} from './provider';
import {getQueryValue} from './query'; import {dirtyParentQueries, getQueryValue} from './query';
import {createInjector} from './refs'; import {createInjector} from './refs';
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; import {ArgumentType, BindingType, CheckType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asPureExpressionData} from './types';
import {checkBinding, isComponentView, renderNode, viewParentEl} from './util'; import {checkBinding, isComponentView, renderNode, viewParentEl} from './util';
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view'; import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
let initialized = false; let initialized = false;
@ -35,14 +34,12 @@ export function initServicesIfNeeded() {
Services.checkAndUpdateView = services.checkAndUpdateView; Services.checkAndUpdateView = services.checkAndUpdateView;
Services.checkNoChangesView = services.checkNoChangesView; Services.checkNoChangesView = services.checkNoChangesView;
Services.destroyView = services.destroyView; Services.destroyView = services.destroyView;
Services.attachEmbeddedView = services.attachEmbeddedView, Services.resolveDep = resolveDep;
Services.detachEmbeddedView = services.detachEmbeddedView,
Services.moveEmbeddedView = services.moveEmbeddedView;
Services.resolveDep = services.resolveDep;
Services.createDebugContext = services.createDebugContext; Services.createDebugContext = services.createDebugContext;
Services.handleEvent = services.handleEvent; Services.handleEvent = services.handleEvent;
Services.updateDirectives = services.updateDirectives; Services.updateDirectives = services.updateDirectives;
Services.updateRenderer = services.updateRenderer; Services.updateRenderer = services.updateRenderer;
Services.dirtyParentQueries = dirtyParentQueries;
} }
function createProdServices() { function createProdServices() {
@ -53,16 +50,17 @@ function createProdServices() {
checkAndUpdateView: checkAndUpdateView, checkAndUpdateView: checkAndUpdateView,
checkNoChangesView: checkNoChangesView, checkNoChangesView: checkNoChangesView,
destroyView: destroyView, destroyView: destroyView,
attachEmbeddedView: attachEmbeddedView,
detachEmbeddedView: detachEmbeddedView,
moveEmbeddedView: moveEmbeddedView,
resolveDep: resolveDep,
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) => handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) =>
view.def.handleEvent(view, nodeIndex, eventName, event), view.def.handleEvent(view, nodeIndex, eventName, event),
updateDirectives: (check: NodeCheckFn, view: ViewData) => updateDirectives: (view: ViewData, checkType: CheckType) => view.def.updateDirectives(
view.def.updateDirectives(check, view), checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode :
updateRenderer: (check: NodeCheckFn, view: ViewData) => view.def.updateRenderer(check, view), prodCheckNoChangesNode,
view),
updateRenderer: (view: ViewData, checkType: CheckType) => view.def.updateRenderer(
checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode :
prodCheckNoChangesNode,
view),
}; };
} }
@ -74,10 +72,6 @@ function createDebugServices() {
checkAndUpdateView: debugCheckAndUpdateView, checkAndUpdateView: debugCheckAndUpdateView,
checkNoChangesView: debugCheckNoChangesView, checkNoChangesView: debugCheckNoChangesView,
destroyView: debugDestroyView, destroyView: debugDestroyView,
attachEmbeddedView: attachEmbeddedView,
detachEmbeddedView: detachEmbeddedView,
moveEmbeddedView: moveEmbeddedView,
resolveDep: resolveDep,
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: debugHandleEvent, handleEvent: debugHandleEvent,
updateDirectives: debugUpdateDirectives, updateDirectives: debugUpdateDirectives,
@ -115,6 +109,24 @@ function createRootData(
}; };
} }
function prodCheckAndUpdateNode(
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): any {
const nodeDef = view.def.nodes[nodeIndex];
checkAndUpdateNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
return (nodeDef.type === NodeType.PureExpression) ? asPureExpressionData(view, nodeIndex).value :
undefined;
}
function prodCheckNoChangesNode(
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): any {
const nodeDef = view.def.nodes[nodeIndex];
checkNoChangesNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
return (nodeDef.type === NodeType.PureExpression) ? asPureExpressionData(view, nodeIndex).value :
undefined;
}
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData { function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
return callWithDebugContext( return callWithDebugContext(
DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]); DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]);
@ -158,7 +170,7 @@ function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string,
DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]); DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
} }
function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) { function debugUpdateDirectives(view: ViewData, checkType: CheckType) {
if (view.state & ViewState.Destroyed) { if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(DebugAction[_currentAction]); throw viewDestroyedError(DebugAction[_currentAction]);
} }
@ -167,15 +179,22 @@ function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
function debugCheckDirectivesFn( function debugCheckDirectivesFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const result = debugCheckFn(check, view, nodeIndex, argStyle, values); const nodeDef = view.def.nodes[nodeIndex];
if (view.def.nodes[nodeIndex].type === NodeType.Directive) { if (checkType === CheckType.CheckAndUpdate) {
debugCheckAndUpdateNode(view, nodeDef, argStyle, values);
} else {
debugCheckNoChangesNode(view, nodeDef, argStyle, values);
}
if (nodeDef.type === NodeType.Directive) {
debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex)); debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex));
} }
return result; return (nodeDef.type === NodeType.PureExpression) ?
asPureExpressionData(view, nodeDef.index).value :
undefined;
}; };
} }
function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) { function debugUpdateRenderer(view: ViewData, checkType: CheckType) {
if (view.state & ViewState.Destroyed) { if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(DebugAction[_currentAction]); throw viewDestroyedError(DebugAction[_currentAction]);
} }
@ -184,21 +203,26 @@ function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
function debugCheckRenderNodeFn( function debugCheckRenderNodeFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const result = debugCheckFn(check, view, nodeIndex, argStyle, values);
const nodeDef = view.def.nodes[nodeIndex]; const nodeDef = view.def.nodes[nodeIndex];
if (checkType === CheckType.CheckAndUpdate) {
debugCheckAndUpdateNode(view, nodeDef, argStyle, values);
} else {
debugCheckNoChangesNode(view, nodeDef, argStyle, values);
}
if (nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text) { if (nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text) {
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex)); debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex));
} }
return result; return (nodeDef.type === NodeType.PureExpression) ?
asPureExpressionData(view, nodeDef.index).value :
undefined;
} }
} }
function debugCheckFn( function debugCheckAndUpdateNode(
delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType, view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, givenValues: any[]): void {
givenValues: any[]) { const changed = (<any>checkAndUpdateNode)(view, nodeDef, argStyle, ...givenValues);
if (_currentAction === DebugAction.detectChanges) { if (changed) {
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues; const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
const nodeDef = view.def.nodes[nodeIndex];
if (nodeDef.type === NodeType.Directive || nodeDef.type === NodeType.Element) { if (nodeDef.type === NodeType.Directive || nodeDef.type === NodeType.Element) {
const bindingValues: {[key: string]: string} = {}; const bindingValues: {[key: string]: string} = {};
for (let i = 0; i < nodeDef.bindings.length; i++) { for (let i = 0; i < nodeDef.bindings.length; i++) {
@ -206,8 +230,7 @@ function debugCheckFn(
const value = values[i]; const value = values[i];
if ((binding.type === BindingType.ElementProperty || if ((binding.type === BindingType.ElementProperty ||
binding.type === BindingType.ComponentHostProperty || binding.type === BindingType.ComponentHostProperty ||
binding.type === BindingType.DirectiveProperty) && binding.type === BindingType.DirectiveProperty)) {
checkBinding(view, nodeDef, i, value)) {
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] = bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
normalizeDebugBindingValue(value); normalizeDebugBindingValue(value);
} }
@ -225,8 +248,12 @@ function debugCheckFn(
} }
} }
} }
return (<any>delegate)(view, nodeIndex, argStyle, ...givenValues); }
};
function debugCheckNoChangesNode(
view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, values: any[]): void {
(<any>checkNoChangesNode)(view, nodeDef, argStyle, ...values);
}
function normalizeDebugBindingName(name: string) { function normalizeDebugBindingName(name: string) {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers

View File

@ -66,7 +66,7 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): TextD
export function checkAndUpdateTextInline( export function checkAndUpdateTextInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any) { v7: any, v8: any, v9: any): boolean {
let changed = false; let changed = false;
const bindings = def.bindings; const bindings = def.bindings;
const bindLen = bindings.length; const bindLen = bindings.length;
@ -96,9 +96,10 @@ export function checkAndUpdateTextInline(
const renderNode = asTextData(view, def.index).renderText; const renderNode = asTextData(view, def.index).renderText;
view.renderer.setValue(renderNode, value); view.renderer.setValue(renderNode, value);
} }
return changed;
} }
export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: any[]) { export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: any[]): boolean {
const bindings = def.bindings; const bindings = def.bindings;
let changed = false; let changed = false;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
@ -117,6 +118,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
const renderNode = asTextData(view, def.index).renderText; const renderNode = asTextData(view, def.index).renderText;
view.renderer.setValue(renderNode, value); view.renderer.setValue(renderNode, value);
} }
return changed;
} }
function _addInterpolationPart(value: any, binding: BindingDef): string { function _addInterpolationPart(value: any, binding: BindingDef): string {

View File

@ -312,6 +312,7 @@ export interface ViewData {
// index of component provider / anchor. // index of component provider / anchor.
parentNodeDef: NodeDef; parentNodeDef: NodeDef;
parent: ViewData; parent: ViewData;
viewContainerParent: ViewData;
component: any; component: any;
context: any; context: any;
// Attention: Never loop over this, as this will // Attention: Never loop over this, as this will
@ -448,6 +449,11 @@ export abstract class DebugContext {
// Other // Other
// ------------------------------------- // -------------------------------------
export enum CheckType {
CheckAndUpdate,
CheckNoChanges
}
export interface Services { export interface Services {
setCurrentNode(view: ViewData, nodeIndex: number): void; setCurrentNode(view: ViewData, nodeIndex: number): void;
createRootView( createRootView(
@ -456,17 +462,15 @@ export interface Services {
createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData; createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData;
checkAndUpdateView(view: ViewData): void; checkAndUpdateView(view: ViewData): void;
checkNoChangesView(view: ViewData): void; checkNoChangesView(view: ViewData): void;
attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData): void;
detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData;
moveEmbeddedView(elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData;
destroyView(view: ViewData): void; destroyView(view: ViewData): void;
resolveDep( resolveDep(
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
notFoundValue?: any): any; notFoundValue?: any): any;
createDebugContext(view: ViewData, nodeIndex: number): DebugContext; createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
handleEvent: ViewHandleEventFn; handleEvent: ViewHandleEventFn;
updateDirectives: ViewUpdateFn; updateDirectives: (view: ViewData, checkType: CheckType) => void;
updateRenderer: ViewUpdateFn; updateRenderer: (view: ViewData, checkType: CheckType) => void;
dirtyParentQueries: (view: ViewData) => void;
} }
/** /**
@ -480,12 +484,10 @@ export const Services: Services = {
checkAndUpdateView: undefined, checkAndUpdateView: undefined,
checkNoChangesView: undefined, checkNoChangesView: undefined,
destroyView: undefined, destroyView: undefined,
attachEmbeddedView: undefined,
detachEmbeddedView: undefined,
moveEmbeddedView: undefined,
resolveDep: undefined, resolveDep: undefined,
createDebugContext: undefined, createDebugContext: undefined,
handleEvent: undefined, handleEvent: undefined,
updateDirectives: undefined, updateDirectives: undefined,
updateRenderer: undefined, updateRenderer: undefined,
dirtyParentQueries: undefined,
}; };

View File

@ -60,9 +60,22 @@ export function createRendererTypeV2(values: {
export function checkBinding( export function checkBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean { view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
const oldValue = view.oldValues[def.bindingIndex + bindingIdx]; const oldValues = view.oldValues;
return unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) || if (unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) ||
!devModeEqual(oldValue, value); !looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
unwrapCounter = 0;
return true;
}
return false;
}
export function checkAndUpdateBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
if (checkBinding(view, def, bindingIdx, value)) {
view.oldValues[def.bindingIndex + bindingIdx] = value;
return true;
}
return false;
} }
export function checkBindingNoChanges( export function checkBindingNoChanges(
@ -76,27 +89,19 @@ export function checkBindingNoChanges(
} }
} }
export function checkAndUpdateBinding( export function markParentViewsForCheck(view: ViewData) {
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
const oldValues = view.oldValues;
if (unwrapCounter || (view.state & ViewState.FirstCheck) ||
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
unwrapCounter = 0;
oldValues[def.bindingIndex + bindingIdx] = value;
return true;
}
return false;
}
export function dispatchEvent(
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
let currView = view; let currView = view;
while (currView) { while (currView) {
if (currView.def.flags & ViewFlags.OnPush) { if (currView.def.flags & ViewFlags.OnPush) {
currView.state |= ViewState.ChecksEnabled; currView.state |= ViewState.ChecksEnabled;
} }
currView = currView.parent; currView = currView.viewContainerParent || currView.parent;
} }
}
export function dispatchEvent(
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
markParentViewsForCheck(view);
return Services.handleEvent(view, nodeIndex, eventName, event); return Services.handleEvent(view, nodeIndex, eventName, event);
} }

View File

@ -16,7 +16,7 @@ import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAn
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression'; import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
import {checkAndUpdateQuery, createQuery, queryDef} from './query'; import {checkAndUpdateQuery, createQuery, queryDef} from './query';
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
import {ArgumentType, ElementData, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types'; import {ArgumentType, CheckType, ElementData, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types';
import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util'; import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util';
const NOOP = (): any => undefined; const NOOP = (): any => undefined;
@ -246,7 +246,7 @@ function createView(
const view: ViewData = { const view: ViewData = {
def, def,
parent, parent,
parentNodeDef, viewContainerParent: undefined, parentNodeDef,
context: undefined, context: undefined,
component: undefined, nodes, component: undefined, nodes,
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer, state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer,
@ -339,35 +339,35 @@ function createViewNodes(view: ViewData) {
// fill static content and view queries // fill static content and view queries
execQueriesAction( execQueriesAction(
view, NodeFlags.HasContentQuery | NodeFlags.HasViewQuery, NodeFlags.HasStaticQuery, view, NodeFlags.HasContentQuery | NodeFlags.HasViewQuery, NodeFlags.HasStaticQuery,
QueryAction.CheckAndUpdate); CheckType.CheckAndUpdate);
} }
export function checkNoChangesView(view: ViewData) { export function checkNoChangesView(view: ViewData) {
Services.updateDirectives(checkNoChangesNode, view); Services.updateDirectives(view, CheckType.CheckNoChanges);
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges); execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
execQueriesAction( execQueriesAction(
view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckNoChanges); view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, CheckType.CheckNoChanges);
Services.updateRenderer(checkNoChangesNode, view); Services.updateRenderer(view, CheckType.CheckNoChanges);
execComponentViewsAction(view, ViewAction.CheckNoChanges); execComponentViewsAction(view, ViewAction.CheckNoChanges);
execQueriesAction( execQueriesAction(
view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckNoChanges); view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, CheckType.CheckNoChanges);
} }
export function checkAndUpdateView(view: ViewData) { export function checkAndUpdateView(view: ViewData) {
Services.updateDirectives(checkAndUpdateNode, view); Services.updateDirectives(view, CheckType.CheckAndUpdate);
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate); execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction( execQueriesAction(
view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckAndUpdate); view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, CheckType.CheckAndUpdate);
callLifecycleHooksChildrenFirst( callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterContentChecked | view, NodeFlags.AfterContentChecked |
(view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0)); (view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
Services.updateRenderer(checkAndUpdateNode, view); Services.updateRenderer(view, CheckType.CheckAndUpdate);
execComponentViewsAction(view, ViewAction.CheckAndUpdate); execComponentViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction( execQueriesAction(
view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckAndUpdate); view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, CheckType.CheckAndUpdate);
callLifecycleHooksChildrenFirst( callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterViewChecked | view, NodeFlags.AfterViewChecked |
@ -379,61 +379,83 @@ export function checkAndUpdateView(view: ViewData) {
view.state &= ~ViewState.FirstCheck; view.state &= ~ViewState.FirstCheck;
} }
function checkAndUpdateNode( export function checkAndUpdateNode(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): boolean {
if (argStyle === ArgumentType.Inline) { if (argStyle === ArgumentType.Inline) {
return checkAndUpdateNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); return checkAndUpdateNodeInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
} else { } else {
return checkAndUpdateNodeDynamic(view, nodeIndex, v0); return checkAndUpdateNodeDynamic(view, nodeDef, v0);
} }
} }
function checkAndUpdateNodeInline( function checkAndUpdateNodeInline(
view: ViewData, nodeIndex: number, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, view: ViewData, nodeDef: NodeDef, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
v6?: any, v7?: any, v8?: any, v9?: any): any { v6?: any, v7?: any, v8?: any, v9?: any): boolean {
const nodeDef = view.def.nodes[nodeIndex]; let changed = false;
switch (nodeDef.type) { switch (nodeDef.type) {
case NodeType.Element: case NodeType.Element:
return checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); changed = checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
break;
case NodeType.Text: case NodeType.Text:
return checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); changed = checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
break;
case NodeType.Directive: case NodeType.Directive:
return checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); changed =
checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
break;
case NodeType.PureExpression: case NodeType.PureExpression:
return checkAndUpdatePureExpressionInline( changed =
view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
break;
} }
return changed;
} }
function checkAndUpdateNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): any { function checkAndUpdateNodeDynamic(view: ViewData, nodeDef: NodeDef, values: any[]): boolean {
const nodeDef = view.def.nodes[nodeIndex]; let changed = false;
switch (nodeDef.type) { switch (nodeDef.type) {
case NodeType.Element: case NodeType.Element:
return checkAndUpdateElementDynamic(view, nodeDef, values); changed = checkAndUpdateElementDynamic(view, nodeDef, values);
break;
case NodeType.Text: case NodeType.Text:
return checkAndUpdateTextDynamic(view, nodeDef, values); changed = checkAndUpdateTextDynamic(view, nodeDef, values);
break;
case NodeType.Directive: case NodeType.Directive:
return checkAndUpdateDirectiveDynamic(view, nodeDef, values); changed = checkAndUpdateDirectiveDynamic(view, nodeDef, values);
break;
case NodeType.PureExpression: case NodeType.PureExpression:
return checkAndUpdatePureExpressionDynamic(view, nodeDef, values); changed = checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
break;
} }
if (changed) {
// Update oldValues after all bindings have been updated,
// as a setter for a property might update other properties.
const bindLen = nodeDef.bindings.length;
const bindingStart = nodeDef.bindingIndex;
const oldValues = view.oldValues;
for (let i = 0; i < bindLen; i++) {
oldValues[bindingStart + i] = values[i];
}
}
return changed;
} }
function checkNoChangesNode( export function checkNoChangesNode(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
if (argStyle === ArgumentType.Inline) { if (argStyle === ArgumentType.Inline) {
return checkNoChangesNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); checkNoChangesNodeInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
} else { } else {
return checkNoChangesNodeDynamic(view, nodeIndex, v0); checkNoChangesNodeDynamic(view, nodeDef, v0);
} }
// Returning false is ok here as we would have thrown in case of a change.
return false;
} }
function checkNoChangesNodeInline( function checkNoChangesNodeInline(
view: ViewData, nodeIndex: number, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, view: ViewData, nodeDef: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v6: any, v7: any, v8: any, v9: any): void { v7: any, v8: any, v9: any): void {
const nodeDef = view.def.nodes[nodeIndex];
const bindLen = nodeDef.bindings.length; const bindLen = nodeDef.bindings.length;
if (bindLen > 0) checkBindingNoChanges(view, nodeDef, 0, v0); if (bindLen > 0) checkBindingNoChanges(view, nodeDef, 0, v0);
if (bindLen > 1) checkBindingNoChanges(view, nodeDef, 1, v1); if (bindLen > 1) checkBindingNoChanges(view, nodeDef, 1, v1);
@ -445,17 +467,12 @@ function checkNoChangesNodeInline(
if (bindLen > 7) checkBindingNoChanges(view, nodeDef, 7, v7); if (bindLen > 7) checkBindingNoChanges(view, nodeDef, 7, v7);
if (bindLen > 8) checkBindingNoChanges(view, nodeDef, 8, v8); if (bindLen > 8) checkBindingNoChanges(view, nodeDef, 8, v8);
if (bindLen > 9) checkBindingNoChanges(view, nodeDef, 9, v9); if (bindLen > 9) checkBindingNoChanges(view, nodeDef, 9, v9);
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
undefined;
} }
function checkNoChangesNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): void { function checkNoChangesNodeDynamic(view: ViewData, nodeDef: NodeDef, values: any[]): void {
const nodeDef = view.def.nodes[nodeIndex];
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
checkBindingNoChanges(view, nodeDef, i, values[i]); checkBindingNoChanges(view, nodeDef, i, values[i]);
} }
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
undefined;
} }
function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) { function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
@ -574,13 +591,9 @@ function callViewAction(view: ViewData, action: ViewAction) {
} }
} }
enum QueryAction {
CheckAndUpdate,
CheckNoChanges
}
function execQueriesAction( function execQueriesAction(
view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags, action: QueryAction) { view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags,
checkType: CheckType) {
if (!(view.def.nodeFlags & queryFlags) || !(view.def.nodeFlags & staticDynamicQueryFlag)) { if (!(view.def.nodeFlags & queryFlags) || !(view.def.nodeFlags & staticDynamicQueryFlag)) {
return; return;
} }
@ -589,11 +602,11 @@ function execQueriesAction(
const nodeDef = view.def.nodes[i]; const nodeDef = view.def.nodes[i];
if ((nodeDef.flags & queryFlags) && (nodeDef.flags & staticDynamicQueryFlag)) { if ((nodeDef.flags & queryFlags) && (nodeDef.flags & staticDynamicQueryFlag)) {
Services.setCurrentNode(view, nodeDef.index); Services.setCurrentNode(view, nodeDef.index);
switch (action) { switch (checkType) {
case QueryAction.CheckAndUpdate: case CheckType.CheckAndUpdate:
checkAndUpdateQuery(view, nodeDef); checkAndUpdateQuery(view, nodeDef);
break; break;
case QueryAction.CheckNoChanges: case CheckType.CheckNoChanges:
checkNoChangesQuery(view, nodeDef); checkNoChangesQuery(view, nodeDef);
break; break;
} }

View File

@ -6,15 +6,16 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {dirtyParentQueries} from './query'; import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, Services, ViewData, asElementData, asProviderData, asTextData} from './types';
import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, ViewData, asElementData, asProviderData, asTextData} from './types';
import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, rootRenderNodes, visitProjectedRenderNodes, visitRootRenderNodes} from './util'; import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, rootRenderNodes, visitProjectedRenderNodes, visitRootRenderNodes} from './util';
export function attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData) { export function attachEmbeddedView(
parentView: ViewData, elementData: ElementData, viewIndex: number, view: ViewData) {
let embeddedViews = elementData.embeddedViews; let embeddedViews = elementData.embeddedViews;
if (viewIndex == null) { if (viewIndex == null) {
viewIndex = embeddedViews.length; viewIndex = embeddedViews.length;
} }
view.viewContainerParent = parentView;
addToArray(embeddedViews, viewIndex, view); addToArray(embeddedViews, viewIndex, view);
const dvcElementData = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) { if (dvcElementData && dvcElementData !== elementData) {
@ -25,7 +26,7 @@ export function attachEmbeddedView(elementData: ElementData, viewIndex: number,
projectedViews.push(view); projectedViews.push(view);
} }
dirtyParentQueries(view); Services.dirtyParentQueries(view);
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null; const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
renderAttachEmbeddedView(elementData, prevView, view); renderAttachEmbeddedView(elementData, prevView, view);
@ -40,6 +41,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
return null; return null;
} }
const view = embeddedViews[viewIndex]; const view = embeddedViews[viewIndex];
view.viewContainerParent = undefined;
removeFromArray(embeddedViews, viewIndex); removeFromArray(embeddedViews, viewIndex);
const dvcElementData = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
@ -48,9 +50,9 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
removeFromArray(projectedViews, projectedViews.indexOf(view)); removeFromArray(projectedViews, projectedViews.indexOf(view));
} }
dirtyParentQueries(view); Services.dirtyParentQueries(view);
renderDetachEmbeddedView(elementData, view); renderDetachView(view);
return view; return view;
} }
@ -68,9 +70,9 @@ export function moveEmbeddedView(
// Note: Don't need to change projectedViews as the order in there // Note: Don't need to change projectedViews as the order in there
// as always invalid... // as always invalid...
dirtyParentQueries(view); Services.dirtyParentQueries(view);
renderDetachEmbeddedView(elementData, view); renderDetachView(view);
const prevView = newViewIndex > 0 ? embeddedViews[newViewIndex - 1] : null; const prevView = newViewIndex > 0 ? embeddedViews[newViewIndex - 1] : null;
renderAttachEmbeddedView(elementData, prevView, view); renderAttachEmbeddedView(elementData, prevView, view);
@ -87,9 +89,8 @@ function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData,
visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined); visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined);
} }
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) { export function renderDetachView(view: ViewData) {
const parentNode = view.renderer.parentNode(elementData.renderElement); visitRootRenderNodes(view, RenderNodeAction.RemoveChild, null, null, undefined);
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
} }
function addToArray(arr: any[], index: number, value: any) { function addToArray(arr: any[], index: number, value: any) {

View File

@ -78,7 +78,7 @@ export function main() {
it('should throw when reentering tick', inject([ApplicationRef], (ref: ApplicationRef_) => { it('should throw when reentering tick', inject([ApplicationRef], (ref: ApplicationRef_) => {
const view = jasmine.createSpyObj('view', ['detach', 'attachToAppRef']); const view = jasmine.createSpyObj('view', ['detach', 'attachToAppRef']);
const viewRef = jasmine.createSpyObj( const viewRef = jasmine.createSpyObj(
'viewRef', ['detectChanges', 'detachFromContainer', 'attachToAppRef']); 'viewRef', ['detectChanges', 'detachFromAppRef', 'attachToAppRef']);
viewRef.internalView = view; viewRef.internalView = view;
view.ref = viewRef; view.ref = viewRef;
try { try {

View File

@ -66,8 +66,8 @@ export function main() {
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]); const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
attachEmbeddedView(viewContainerData, 0, childView0); attachEmbeddedView(parentView, viewContainerData, 0, childView0);
attachEmbeddedView(viewContainerData, 1, childView1); attachEmbeddedView(parentView, viewContainerData, 1, childView1);
// 2 anchors + 2 elements // 2 anchors + 2 elements
const rootChildren = getDOM().childNodes(rootNodes[0]); const rootChildren = getDOM().childNodes(rootNodes[0]);
@ -96,8 +96,8 @@ export function main() {
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]); const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
attachEmbeddedView(viewContainerData, 0, childView0); attachEmbeddedView(parentView, viewContainerData, 0, childView0);
attachEmbeddedView(viewContainerData, 1, childView1); attachEmbeddedView(parentView, viewContainerData, 1, childView1);
moveEmbeddedView(viewContainerData, 0, 1); moveEmbeddedView(viewContainerData, 0, 1);
@ -118,7 +118,7 @@ export function main() {
])); ]));
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]);
attachEmbeddedView(asElementData(parentView, 0), 0, childView0); attachEmbeddedView(parentView, asElementData(parentView, 0), 0, childView0);
const rootNodes = rootRenderNodes(parentView); const rootNodes = rootRenderNodes(parentView);
expect(rootNodes.length).toBe(3); expect(rootNodes.length).toBe(3);
@ -146,7 +146,7 @@ export function main() {
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0); attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
Services.checkAndUpdateView(parentView); Services.checkAndUpdateView(parentView);
@ -180,7 +180,7 @@ export function main() {
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0); attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
Services.destroyView(parentView); Services.destroyView(parentView);
expect(log).toEqual(['ngOnDestroy']); expect(log).toEqual(['ngOnDestroy']);

View File

@ -114,7 +114,7 @@ export function main() {
const componentView = asElementData(view, 0).componentView; const componentView = asElementData(view, 0).componentView;
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]); const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
attachEmbeddedView(asElementData(componentView, 1), 0, view0); attachEmbeddedView(view, asElementData(componentView, 1), 0, view0);
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3); expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3);
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0]))[1]) expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0]))[1])
.toBe(asTextData(view, 2).renderText); .toBe(asTextData(view, 2).renderText);

View File

@ -156,7 +156,7 @@ export function main() {
])); ]));
const childView = Services.createEmbeddedView(view, view.def.nodes[3]); const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView); attachEmbeddedView(view, asElementData(view, 3), 0, childView);
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
// queries on parent elements of anchors // queries on parent elements of anchors
@ -185,7 +185,7 @@ export function main() {
const childView = Services.createEmbeddedView(view, view.def.nodes[3]); const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
// attach at a different place than the one where the template was defined // attach at a different place than the one where the template was defined
attachEmbeddedView(asElementData(view, 7), 0, childView); attachEmbeddedView(view, asElementData(view, 7), 0, childView);
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
@ -215,12 +215,13 @@ export function main() {
expect(qs.a.length).toBe(0); expect(qs.a.length).toBe(0);
const childView = Services.createEmbeddedView(view, view.def.nodes[3]); const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView); attachEmbeddedView(view, asElementData(view, 3), 0, childView);
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
expect(qs.a.length).toBe(1); expect(qs.a.length).toBe(1);
detachEmbeddedView(asElementData(view, 3), 0); detachEmbeddedView(asElementData(view, 3), 0);
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
expect(qs.a.length).toBe(0); expect(qs.a.length).toBe(0);
@ -245,7 +246,7 @@ export function main() {
const compView = asElementData(view, 0).componentView; const compView = asElementData(view, 0).componentView;
const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]); const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]);
attachEmbeddedView(asElementData(compView, 1), 0, childView); attachEmbeddedView(view, asElementData(compView, 1), 0, childView);
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
expect(comp.a.length).toBe(1); expect(comp.a.length).toBe(1);
@ -381,7 +382,7 @@ export function main() {
Services.checkNoChangesView(view); Services.checkNoChangesView(view);
const childView = Services.createEmbeddedView(view, view.def.nodes[3]); const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView); attachEmbeddedView(view, asElementData(view, 3), 0, childView);
let err: any; let err: any;
try { try {