feat(compiler): integrate compiler with view engine - main integration tests work (#14284)

Part of #14013

PR Close #14284
This commit is contained in:
Tobias Bosch
2017-02-02 15:01:35 -08:00
committed by Miško Hevery
parent dfe29934b6
commit baa654a234
35 changed files with 1232 additions and 277 deletions

View File

@ -7,7 +7,7 @@
*/
import {AnimationQueue} from './animation/animation_queue';
import {ApplicationInitStatus} from './application_init';
import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
import {ApplicationRef, ApplicationRef_} from './application_ref';
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
@ -16,6 +16,7 @@ import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler';
import {ViewUtils} from './linker/view_utils';
import {NgModule} from './metadata';
import {initServicesIfNeeded} from './view/index';
export function _iterableDiffersFactory() {
return defaultIterableDiffers;
@ -29,6 +30,10 @@ export function _localeFactory(locale?: string): string {
return locale || 'en-US';
}
export function _initViewEngine() {
initServicesIfNeeded();
}
/**
* This module includes the providers of @angular/core that are needed
* to bootstrap components via `ApplicationRef`.
@ -51,6 +56,7 @@ export function _localeFactory(locale?: string): string {
useFactory: _localeFactory,
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
},
{provide: APP_INITIALIZER, useValue: _initViewEngine, multi: true},
]
})
export class ApplicationModule {

View File

@ -42,6 +42,7 @@ import * as reflection_types from './reflection/types';
import * as api from './render/api';
import * as decorators from './util/decorators';
import {isObservable, isPromise} from './util/lang';
import * as viewEngine from './view/index';
export const __core_private__: {
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
@ -109,6 +110,7 @@ export const __core_private__: {
AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils,
ERROR_COMPONENT_TYPE: typeof ERROR_COMPONENT_TYPE,
viewEngine: typeof viewEngine,
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -125,6 +127,7 @@ export const __core_private__: {
registerModuleFactory: ng_module_factory_loader.registerModuleFactory,
ViewType: view_type.ViewType,
view_utils: view_utils,
viewEngine: viewEngine,
ViewMetadata: metadata_view.ViewMetadata,
DebugContext: debug_context.DebugContext,
StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo,

View File

@ -9,18 +9,19 @@
import {isDevMode} from '../application_ref';
import {SecurityContext} from '../security';
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
import {checkAndUpdateBinding, dispatchEvent, sliceErrorStack, unwrapValue} from './util';
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';
export function anchorDef(
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
childCount: number, template?: ViewDefinition): NodeDef {
childCount: number, templateFactory?: ViewDefinitionFactory): NodeDef {
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
if (matchedQueries) {
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
}
// skip the call to sliceErrorStack itself + the call to this function.
const source = isDevMode() ? sliceErrorStack(2, 3) : '';
const template = templateFactory ? resolveViewDefinition(templateFactory) : null;
return {
type: NodeType.Element,
// will bet set by the view definition
@ -130,10 +131,10 @@ export function elementDef(
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
const elDef = def.element;
const rootElement = view.root.element;
const rootSelectorOrNode = view.root.selectorOrNode;
const renderer = view.root.renderer;
let el: any;
if (view.parent || !rootElement) {
if (view.parent || !rootSelectorOrNode) {
const parentNode =
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
el = elDef.name ? renderer.createElement(elDef.name) : renderer.createComment('');
@ -141,7 +142,7 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
renderer.appendChild(parentNode, el);
}
} else {
el = rootElement;
el = renderer.selectRootElement(rootSelectorOrNode);
}
if (elDef.attrs) {
for (let attrName in elDef.attrs) {
@ -151,7 +152,8 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
if (elDef.outputs.length) {
for (let i = 0; i < elDef.outputs.length; i++) {
const output = elDef.outputs[i];
const handleEventClosure = renderEventHandlerClosure(view, def.index, output.eventName);
const handleEventClosure = renderEventHandlerClosure(
view, def.index, elementEventFullName(output.target, output.eventName));
const disposable =
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
view.disposables[def.disposableIndex + i] = disposable;

View File

@ -14,7 +14,7 @@ export {queryDef} from './query';
export {createComponentFactory} from './refs';
export {initServicesIfNeeded} from './services';
export {textDef} from './text';
export {rootRenderNodes} from './util';
export {elementEventFullName, nodeValue, rootRenderNodes} from './util';
export {viewDef} from './view';
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';

View File

@ -16,7 +16,7 @@ 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, findElementDef, isComponentView, parentDiIndex, tokenKey, unwrapValue} from './util';
import {checkAndUpdateBinding, dispatchEvent, isComponentView, tokenKey, unwrapValue, viewParentDiIndex} from './util';
const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
const ElementRefTokenKey = tokenKey(ElementRef);
@ -278,6 +278,9 @@ function callFactory(
export function resolveDep(
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef,
notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
if (depDef.flags & DepFlags.Value) {
return depDef.token;
}
const startView = view;
if (depDef.flags & DepFlags.Optional) {
notFoundValue = null;
@ -288,7 +291,7 @@ export function resolveDep(
requestNodeIndex = null;
elIndex = view.def.nodes[elIndex].parent;
while (elIndex == null && view) {
elIndex = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
}
@ -337,7 +340,7 @@ export function resolveDep(
}
}
requestNodeIndex = null;
elIndex = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
return startView.root.injector.get(depDef.token, notFoundValue);

View File

@ -20,7 +20,7 @@ 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 {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey} from './util';
import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentDiIndex} from './util';
const EMPTY_CONTEXT = new Object();
@ -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 = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
return view ? new Injector_(view, elIndex) : this._view.root.injector;
@ -119,7 +119,7 @@ class ViewContainerRef_ implements ViewContainerRef {
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
EmbeddedViewRef<C> {
const viewRef = templateRef.createEmbeddedView(context);
const viewRef = templateRef.createEmbeddedView(context || <any>{});
this.insert(viewRef, index);
return viewRef;
}

View File

@ -40,6 +40,8 @@ export class DirectDomRenderer implements RendererV2 {
nextSibling(node: any): any { return node.nextSiblibng; }
setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); }
removeAttribute(el: any, name: string): void { el.removeAttribute(name); }
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {}
removeBindingDebugInfo(el: any, propertyName: string): void {}
addClass(el: any, name: string): void { el.classList.add(name); }
removeClass(el: any, name: string): void { el.classList.remove(name); }
setStyle(el: any, style: string, value: any): void { el.style[style] = value; }
@ -97,8 +99,11 @@ export class LegacyRendererAdapter implements RendererV2 {
}
appendChild(parent: any, newChild: any): void { this._delegate.projectNodes(parent, [newChild]); }
insertBefore(parent: any, newChild: any, refChild: any): void {
const beforeSibling = refChild.nextSiblingOf ? refChild.nextSiblingOf : refChild;
this._delegate.attachViewAfter(beforeSibling, [newChild]);
if (refChild) {
this._delegate.attachViewAfter(refChild.previousSibling, [newChild]);
} else {
this.appendChild(parent, newChild);
}
}
removeChild(parent: any, oldChild: any): void {
if (parent) {
@ -108,14 +113,20 @@ export class LegacyRendererAdapter implements RendererV2 {
selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any {
return this._delegate.selectRootElement(selectorOrNode, debugInfo);
}
parentNode(node: any): any { return {parentOf: node}; }
nextSibling(node: any): any { return {nextSiblingOf: node}; }
parentNode(node: any): any { return node.parentNode; }
nextSibling(node: any): any { return node.nextSibling; }
setAttribute(el: any, name: string, value: string): void {
this._delegate.setElementAttribute(el, name, value);
}
removeAttribute(el: any, name: string): void {
this._delegate.setElementAttribute(el, name, null);
}
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, null);
}
addClass(el: any, name: string): void { this._delegate.setElementClass(el, name, true); }
removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); }
setStyle(el: any, style: string, value: any): void {

View File

@ -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, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
import {checkBinding, findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes} 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 {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentDiIndex} from './util';
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
@ -98,7 +98,7 @@ function debugCreateRootView(
const debugRoot: RootData = {
injector: root.injector,
projectableNodes: root.projectableNodes,
element: root.element,
selectorOrNode: root.selectorOrNode,
renderer: new DebugRenderer(root.renderer),
sanitizer: root.sanitizer
};
@ -114,7 +114,7 @@ function createRootData(
new DirectDomRenderer();
const rootElement =
rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined;
return {injector, projectableNodes, element: rootElement, sanitizer, renderer};
return {injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer};
}
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
@ -184,13 +184,16 @@ function debugUpdateView(check: NodeCheckFn, view: ViewData) {
}
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
try {
renderer.setAttribute(
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
} catch (e) {
renderer.setAttribute(
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`,
'[ERROR] Exception while trying to serialize the value');
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
if (value) {
try {
renderer.setBindingDebugInfo(renderNode, renderName, value.toString());
} catch (e) {
renderer.setBindingDebugInfo(
renderNode, renderName, '[ERROR] Exception while trying to serialize the value');
}
} else {
renderer.removeBindingDebugInfo(renderNode, renderName);
}
}
@ -240,6 +243,12 @@ class DebugRenderer implements RendererV2 {
return this._delegate.setAttribute(el, name, value);
}
removeAttribute(el: any, name: string): void { return this._delegate.removeAttribute(el, name); }
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.removeBindingDebugInfo(el, propertyName);
}
addClass(el: any, name: string): void { return this._delegate.addClass(el, name); }
removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); }
setStyle(el: any, style: string, value: any): void {
@ -258,27 +267,63 @@ class DebugRenderer implements RendererV2 {
class DebugContext_ implements DebugContext {
private nodeDef: NodeDef;
private elView: ViewData;
private elDef: NodeDef;
private compProviderIndex: number;
constructor(public view: ViewData, public nodeIndex: number) {
if (nodeIndex == null) {
this.nodeIndex = nodeIndex = view.parentIndex;
this.view = view = view.parent;
this.nodeIndex = 0;
}
this.nodeDef = view.def.nodes[nodeIndex];
this.elDef = findElementDef(view, nodeIndex);
let elIndex = nodeIndex;
let elView = view;
while (elIndex != null && view.def.nodes[elIndex].type !== NodeType.Element) {
elIndex = view.def.nodes[elIndex].parent;
}
if (elIndex == null) {
while (elIndex == null && elView) {
elIndex = viewParentDiIndex(elView);
elView = elView.parent;
}
}
this.elView = elView;
if (elView) {
this.elDef = elView.def.nodes[elIndex];
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.flags & NodeFlags.HasComponent) {
this.compProviderIndex = i;
break;
}
i += childDef.childCount;
}
} else {
this.elDef = null;
}
}
get injector(): Injector { return createInjector(this.elView, this.elDef.index); }
get component(): any {
if (this.compProviderIndex != null) {
return asProviderData(this.elView, this.compProviderIndex).instance;
}
return this.view.component;
}
get context(): any {
if (this.compProviderIndex != null) {
return asProviderData(this.elView, this.compProviderIndex).instance;
}
return this.view.context;
}
get injector(): Injector { return createInjector(this.view, this.elDef.index); }
get component(): any { return this.view.component; }
get providerTokens(): any[] {
const tokens: any[] = [];
if (this.elDef) {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.view.def.nodes[i];
const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) {
tokens.push(childDef.provider.token);
} else {
i += childDef.childCount;
}
i += childDef.childCount;
}
}
return tokens;
@ -286,20 +331,18 @@ class DebugContext_ implements DebugContext {
get references(): {[key: string]: any} {
const references: {[key: string]: any} = {};
if (this.elDef) {
collectReferences(this.view, this.elDef, references);
collectReferences(this.elView, this.elDef, references);
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.view.def.nodes[i];
const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) {
collectReferences(this.view, childDef, references);
} else {
i += childDef.childCount;
collectReferences(this.elView, childDef, references);
}
i += childDef.childCount;
}
}
return references;
}
get context(): any { return this.view.context; }
get source(): string {
if (this.nodeDef.type === NodeType.Text) {
return this.nodeDef.text.source;
@ -308,12 +351,15 @@ class DebugContext_ implements DebugContext {
}
}
get componentRenderElement() {
const elData = findHostElement(this.view);
const view = this.compProviderIndex != null ?
asProviderData(this.elView, this.compProviderIndex).componentView :
this.view;
const elData = findHostElement(view);
return elData ? elData.renderElement : undefined;
}
get renderNode(): any {
let nodeDef = this.nodeDef.type === NodeType.Text ? this.nodeDef : this.elDef;
return renderNode(this.view, nodeDef);
return this.nodeDef.type === NodeType.Text ? renderNode(this.view, this.nodeDef) :
renderNode(this.elView, this.elDef);
}
}
@ -330,7 +376,7 @@ function findHostElement(view: ViewData): ElementData {
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
for (let queryId in nodeDef.matchedQueries) {
if (queryId.startsWith('#')) {
if (queryIdIsReference(queryId)) {
references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId);
}
}

View File

@ -72,8 +72,7 @@ export enum ArgumentType {
*/
export enum ViewFlags {
None = 0,
DirectDom = 1 << 1,
OnPush = 1 << 2
OnPush = 1 << 1
}
export interface ComponentDefinition {
@ -225,7 +224,8 @@ export interface DepDef {
export enum DepFlags {
None = 0,
SkipSelf = 1 << 0,
Optional = 1 << 1
Optional = 1 << 1,
Value = 2 << 2
}
export interface ProviderOutputDef {
@ -407,7 +407,7 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
export interface RootData {
injector: Injector;
projectableNodes: any[][];
element: any;
selectorOrNode: any;
renderer: RendererV2;
sanitizer: Sanitizer;
}
@ -436,6 +436,14 @@ export interface RendererV2 {
* the caller can't rely on checking whether this is null or not.
*/
nextSibling(node: any): any;
/**
* Used only in debug mode to serialize property changes to dom nodes as attributes.
*/
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void;
/**
* Used only in debug mode to serialize property changes to dom nodes as attributes.
*/
removeBindingDebugInfo(el: any, propertyName: string): void;
setAttribute(el: any, name: string, value: string): void;
removeAttribute(el: any, name: string): void;
addClass(el: any, name: string): void;

View File

@ -95,27 +95,14 @@ export function declaredViewContainer(view: ViewData): ElementData {
* for embedded views, this is the index of the parent node
* that contains the view container.
*/
export function parentDiIndex(view: ViewData): number {
if (view.parent) {
const parentNodeDef = view.def.nodes[view.parentIndex];
return parentNodeDef.element && parentNodeDef.element.template ? parentNodeDef.parent :
parentNodeDef.index;
export function viewParentDiIndex(view: ViewData): number {
if (view.parent && view.context !== view.component) {
const parentNodeDef = view.parent.def.nodes[view.parentIndex];
return parentNodeDef.parent;
}
return view.parentIndex;
}
export function findElementDef(view: ViewData, nodeIndex: number): NodeDef {
const viewDef = view.def;
let nodeDef = viewDef.nodes[nodeIndex];
while (nodeDef) {
if (nodeDef.type === NodeType.Element) {
return nodeDef;
}
nodeDef = nodeDef.parent != null ? viewDef.nodes[nodeDef.parent] : undefined;
}
return undefined;
}
export function renderNode(view: ViewData, def: NodeDef): any {
switch (def.type) {
case NodeType.Element:
@ -125,6 +112,27 @@ export function renderNode(view: ViewData, def: NodeDef): any {
}
}
export function nodeValue(view: ViewData, index: number): any {
const def = view.def.nodes[index];
switch (def.type) {
case NodeType.Element:
return asElementData(view, def.index).renderElement;
case NodeType.Text:
return asTextData(view, def.index).renderText;
case NodeType.Provider:
return asProviderData(view, def.index).instance;
}
return undefined;
}
export function queryIdIsReference(queryId: string): boolean {
return queryId.startsWith('#');
}
export function elementEventFullName(target: string, name: string): string {
return target ? `${target}:${name}` : name;
}
export function isComponentView(view: ViewData): boolean {
return view.component === view.context && !!view.parent;
}

View File

@ -7,6 +7,7 @@
*/
import {ViewEncapsulation} from '../metadata/view';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content';
@ -15,7 +16,7 @@ import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline,
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, resolveViewDefinition} from './util';
import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition} from './util';
const NOOP = (): any => undefined;
@ -41,7 +42,7 @@ export function viewDef(
const newParent = nodes[currentParent.parent];
if (newParent) {
newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
}
currentParent = newParent;
}
@ -64,17 +65,18 @@ export function viewDef(
}
nodes[i] = node;
reverseChildNodes[reverseChildIndex] = node;
validateNode(currentParent, node);
validateNode(currentParent, node, nodesWithoutIndices.length);
viewNodeFlags |= node.flags;
copyInto(node.matchedQueries, viewMatchedQueries);
copyQueryMatchesInto(node.matchedQueries, viewMatchedQueries);
viewBindingCount += node.bindings.length;
viewDisposableCount += node.disposableCount;
if (currentParent) {
currentParent.childFlags |= node.flags;
copyInto(node.matchedQueries, currentParent.childMatchedQueries);
copyQueryMatchesInto(node.matchedQueries, currentParent.childMatchedQueries);
if (node.element && node.element.template) {
copyInto(node.element.template.nodeMatchedQueries, currentParent.childMatchedQueries);
copyQueryMatchesInto(
node.element.template.nodeMatchedQueries, currentParent.childMatchedQueries);
}
}
@ -96,7 +98,7 @@ export function viewDef(
const newParent = nodes[currentParent.parent];
if (newParent) {
newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
}
currentParent = newParent;
}
@ -114,9 +116,12 @@ export function viewDef(
};
}
function copyInto(source: any, target: any) {
function copyQueryMatchesInto(
source: {[queryId: string]: any}, target: {[queryId: string]: boolean}) {
for (let prop in source) {
target[prop] = source[prop];
if (!queryIdIsReference(prop)) {
target[prop] = true;
}
}
}
@ -159,7 +164,7 @@ function calculateReverseChildIndex(
return parentEndIndexInReverseChildOrder - lastChildOffsetRelativeToParentInDfsOrder;
}
function validateNode(parent: NodeDef, node: NodeDef) {
function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
const template = node.element && node.element.template;
if (template) {
if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) {
@ -182,12 +187,10 @@ function validateNode(parent: NodeDef, node: NodeDef) {
}
}
if (node.childCount) {
if (parent) {
const parentEnd = parent.index + parent.childCount;
if (node.index <= parentEnd && node.index + node.childCount > parentEnd) {
throw new Error(
`Illegal State: childCount of node leads outside of parent, at index ${node.index}!`);
}
const parentEnd = parent ? parent.index + parent.childCount : nodeCount - 1;
if (node.index <= parentEnd && node.index + node.childCount > parentEnd) {
throw new Error(
`Illegal State: childCount of node leads outside of parent, at index ${node.index}!`);
}
}
}