refactor(core): have different data types for each node. (#14120)

Also have a new node type for queries.

This leads to less memory usage and better performance.

Deep Tree Benchmark results (depth 11):
- createAndDestroy (view engine vs current codegen):
  * pureScriptTime: 78.80+-4% vs 72.34+-4%
  * scriptTime: 78.80+-4% vs 90.71+-9%
  * gc: 5371.66+-108% vs 9717.53+-174%
  * i.e. faster when gc is also considered and about 2x less memory usage!
- update unchanged

Part of #14013
PR Close #14120
This commit is contained in:
Tobias Bosch
2017-01-25 13:45:07 -08:00
committed by Miško Hevery
parent 7ad616a177
commit f802194c18
18 changed files with 668 additions and 559 deletions

View File

@ -7,7 +7,7 @@
*/
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -54,13 +54,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(
NodeFlags.None, null, AComp, [], null, null, null,
NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, null, 0, 'span'),
])),
]));
const compView = view.nodes[1].provider.componentView;
const compView = asProviderData(view, 1).componentView;
expect(compView.context).toBe(instance);
expect(compView.component).toBe(instance);
@ -81,13 +81,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(
compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(NodeFlags.None, null, AComp, [], null, null, null, () => compViewDef(
providerDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
[
elementDef(NodeFlags.None, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
], update
)),
], jasmine.createSpy('parentUpdater')));
const compView = view.nodes[1].provider.componentView;
const compView = asProviderData(view, 1).componentView;
checkAndUpdateView(view);
@ -118,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(
NodeFlags.None, null, AComp, [], null, null, null,
NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
])),
]));

View File

@ -7,7 +7,7 @@
*/
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -83,16 +83,16 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
const rootChildren = getDOM().childNodes(rootNodes[0]);
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(parentView.nodes[1], 1, childView1);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 1, childView1);
// 2 anchors + 2 elements
expect(rootChildren.length).toBe(4);
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
detachEmbeddedView(parentView.nodes[1], 1);
detachEmbeddedView(parentView.nodes[1], 0);
detachEmbeddedView(asElementData(parentView, 1), 1);
detachEmbeddedView(asElementData(parentView, 1), 0);
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2);
});
@ -106,7 +106,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
]));
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
attachEmbeddedView(parentView.nodes[0], 0, childView0);
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
const rootNodes = rootRenderNodes(parentView);
expect(rootNodes.length).toBe(3);
@ -133,7 +133,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
const rootEl = rootNodes[0];
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
checkAndUpdateView(parentView);
@ -163,13 +163,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
elementDef(NodeFlags.None, null, 1, 'div'),
anchorDef(NodeFlags.HasEmbeddedViews, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
]))
]));
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
destroyView(parentView);
expect(log).toEqual(['ngOnDestroy']);

View File

@ -58,7 +58,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [])
providerDef(NodeFlags.None, null, 0, SomeService, [])
]));
expect(instances.length).toBe(1);
@ -76,8 +76,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject deps from the same element', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'span'), providerDef(NodeFlags.None, null, Dep, []),
providerDef(NodeFlags.None, null, SomeService, [Dep])
elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, null, 0, Dep, []),
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
]));
expect(instance.dep instanceof Dep).toBeTruthy();
@ -85,9 +86,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject deps from a parent element', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'span'), providerDef(NodeFlags.None, null, Dep, []),
elementDef(NodeFlags.None, null, 3, 'span'),
providerDef(NodeFlags.None, null, 0, Dep, []),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Dep])
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
]));
expect(instance.dep instanceof Dep).toBeTruthy();
@ -95,9 +97,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should not inject deps from sibling root elements', () => {
const nodes = [
elementDef(NodeFlags.None, null, 1, 'span'), providerDef(NodeFlags.None, null, Dep, []),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Dep])
providerDef(NodeFlags.None, null, 0, Dep, []),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
];
// root elements
@ -115,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(
NodeFlags.None, null, Dep, [], null, null, null,
NodeFlags.None, null, 0, Dep, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Dep])
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
])),
]));
@ -129,7 +132,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject ViewContainerRef', () => {
createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.HasEmbeddedViews, null, 1),
providerDef(NodeFlags.None, null, SomeService, [ViewContainerRef])
providerDef(NodeFlags.None, null, 0, SomeService, [ViewContainerRef])
]));
expect(instance.dep.createEmbeddedView).toBeTruthy();
@ -139,7 +142,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([
anchorDef(
NodeFlags.None, null, 1, embeddedViewDef([anchorDef(NodeFlags.None, null, 0)])),
providerDef(NodeFlags.None, null, SomeService, [TemplateRef])
providerDef(NodeFlags.None, null, 0, SomeService, [TemplateRef])
]));
expect(instance.dep.createEmbeddedView).toBeTruthy();
@ -148,7 +151,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject ElementRef', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [ElementRef])
providerDef(NodeFlags.None, null, 0, SomeService, [ElementRef])
]));
expect(getDOM().nodeName(instance.dep.nativeElement).toLowerCase()).toBe('span');
@ -158,7 +161,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should not inject Renderer when using directDom', () => {
expect(() => createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Renderer])
providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
])))
.toThrowError('No provider for Renderer!');
});
@ -166,7 +169,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject Renderer when not using directDom', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Renderer])
providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
]));
expect(instance.dep.createElement).toBeTruthy();
@ -199,7 +202,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
providerDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
],
config.update));
@ -219,7 +222,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [], {a: [0, 'a']})
providerDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
],
(updater, view) => updater.checkInline(view, 1, propValue)));
@ -254,7 +257,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [], null, {emitter: 'someEventName'})
providerDef(
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
],
null, handleEvent));
@ -292,9 +296,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 3, 'span'),
providerDef(allFlags, null, SomeService, [], {a: [0, 'a']}),
providerDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']}),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(allFlags, null, SomeService, [], {a: [0, 'a']})
providerDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
],
(updater) => {
updater.checkInline(view, 1, 'someValue');
@ -351,7 +355,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnChanges, null, SomeService, [], {a: [0, 'nonMinifiedA']})
providerDef(NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
],
(updater) => updater.checkInline(view, 1, currValue)));

View File

@ -7,7 +7,7 @@
*/
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper';
@ -46,14 +46,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 2, 'span'), pureArrayDef(2),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]);
}));
const service = view.nodes[2].provider.instance;
const service = asProviderData(view, 2).instance;
values = [1, 2];
checkAndUpdateView(view);
@ -80,14 +80,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 2, 'span'), pureObjectDef(['a', 'b']),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]);
}));
const service = view.nodes[2].provider.instance;
const service = asProviderData(view, 2).instance;
values = [1, 2];
checkAndUpdateView(view);
@ -118,15 +118,15 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 3, 'span'),
providerDef(NodeFlags.None, null, SomePipe, []), purePipeDef(SomePipe, 2),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 3,
[callUpdater(updater, inlineDynamic, view, 2, values)]);
}));
const service = view.nodes[3].provider.instance;
const service = asProviderData(view, 3).instance;
values = [1, 2];
checkAndUpdateView(view);

View File

@ -7,7 +7,7 @@
*/
import {ElementRef, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, QueryBindingType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, QueryBindingType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -45,57 +45,59 @@ export function main() {
a: QueryList<AService>;
}
function contentQueryProvider() {
return providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.All]});
function contentQueryProviders() {
return [
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All})
];
}
function viewQueryProviders(compView: ViewDefinition) {
return [
providerDef(NodeFlags.None, null, 1, QueryService, [], null, null, () => compView),
queryDef(NodeFlags.HasViewQuery, 'query1', {'a': QueryBindingType.All})
];
}
function aServiceProvider() {
return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], AService, []);
}
function viewQueryProvider(compView: ViewDefinition) {
return providerDef(
NodeFlags.None, null, QueryService, [], null, null, null, () => compView,
{'a': ['query1', QueryBindingType.All]});
return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], 0, AService, []);
}
describe('content queries', () => {
it('should query providers on the same element and child elements', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 4, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 5, 'div'),
...contentQueryProviders(),
aServiceProvider(),
elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(),
]));
const qs: QueryService = view.nodes[1].provider.instance;
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a).toBeUndefined();
checkAndUpdateView(view);
const as = qs.a.toArray();
expect(as.length).toBe(2);
expect(as[0]).toBe(view.nodes[2].provider.instance);
expect(as[1]).toBe(view.nodes[4].provider.instance);
expect(as[0]).toBe(asProviderData(view, 3).instance);
expect(as[1]).toBe(asProviderData(view, 5).instance);
});
it('should not query providers on sibling or parent elements', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 5, 'div'),
elementDef(NodeFlags.None, null, 6, 'div'),
aServiceProvider(),
elementDef(NodeFlags.None, null, 1, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 2, 'div'),
...contentQueryProviders(),
elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[3].provider.instance;
const qs: QueryService = asProviderData(view, 3).instance;
expect(qs.a.length).toBe(0);
});
});
@ -103,8 +105,8 @@ export function main() {
describe('view queries', () => {
it('should query providers in the view', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
viewQueryProvider(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'),
...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
aServiceProvider(),
])),
@ -112,23 +114,23 @@ export function main() {
checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance;
const compView = view.nodes[1].provider.componentView;
const comp: QueryService = asProviderData(view, 1).instance;
const compView = asProviderData(view, 1).componentView;
expect(comp.a.length).toBe(1);
expect(comp.a.first).toBe(compView.nodes[1].provider.instance);
expect(comp.a.first).toBe(asProviderData(compView, 1).instance);
});
it('should not query providers on the host element', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'),
viewQueryProvider(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'),
...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
])),
aServiceProvider(),
]));
checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance;
const comp: QueryService = asProviderData(view, 1).instance;
expect(comp.a.length).toBe(0);
});
});
@ -136,37 +138,37 @@ export function main() {
describe('embedded views', () => {
it('should query providers in embedded views', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 5, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, 1, viewDef(
NodeFlags.HasEmbeddedViews, null, 2, viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(),
])),
contentQueryProvider(),
...contentQueryProviders(),
]));
const childView = createEmbeddedView(view, view.def.nodes[2]);
attachEmbeddedView(view.nodes[2], 0, childView);
const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView);
checkAndUpdateView(view);
// queries on parent elements of anchors
const qs1: QueryService = view.nodes[1].provider.instance;
const qs1: QueryService = asProviderData(view, 1).instance;
expect(qs1.a.length).toBe(1);
expect(qs1.a.first instanceof AService).toBe(true);
// queries on the anchor
const qs2: QueryService = view.nodes[3].provider.instance;
const qs2: QueryService = asProviderData(view, 4).instance;
expect(qs2.a.length).toBe(1);
expect(qs2.a.first instanceof AService).toBe(true);
});
it('should query providers in embedded views only at the template declaration', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, 0, viewDef(
ViewFlags.None,
@ -174,31 +176,31 @@ export function main() {
elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(),
])),
elementDef(NodeFlags.None, null, 2, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(NodeFlags.HasEmbeddedViews, null, 0),
]));
const childView = createEmbeddedView(view, view.def.nodes[2]);
const childView = createEmbeddedView(view, view.def.nodes[3]);
// attach at a different place than the one where the template was defined
attachEmbeddedView(view.nodes[5], 0, childView);
attachEmbeddedView(asElementData(view, 7), 0, childView);
checkAndUpdateView(view);
// query on the declaration place
const qs1: QueryService = view.nodes[1].provider.instance;
const qs1: QueryService = asProviderData(view, 1).instance;
expect(qs1.a.length).toBe(1);
expect(qs1.a.first instanceof AService).toBe(true);
// query on the attach place
const qs2: QueryService = view.nodes[4].provider.instance;
const qs2: QueryService = asProviderData(view, 5).instance;
expect(qs2.a.length).toBe(0);
});
it('should checkNoChanges', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 4, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, 1, viewDef(
ViewFlags.None,
@ -211,18 +213,18 @@ export function main() {
checkAndUpdateView(view);
checkNoChangesView(view);
const childView = createEmbeddedView(view, view.def.nodes[2]);
attachEmbeddedView(view.nodes[2], 0, childView);
const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView);
expect(() => checkNoChangesView(view))
.toThrowError(
`Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'.`);
`Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.`);
});
it('should update content queries if embedded views are added or removed', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'),
contentQueryProvider(),
elementDef(NodeFlags.None, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, 0, viewDef(
ViewFlags.None,
@ -234,16 +236,16 @@ export function main() {
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.length).toBe(0);
const childView = createEmbeddedView(view, view.def.nodes[2]);
attachEmbeddedView(view.nodes[2], 0, childView);
const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(asElementData(view, 3), 0, childView);
checkAndUpdateView(view);
expect(qs.a.length).toBe(1);
detachEmbeddedView(view.nodes[2], 0);
detachEmbeddedView(asElementData(view, 3), 0);
checkAndUpdateView(view);
expect(qs.a.length).toBe(0);
@ -251,8 +253,8 @@ export function main() {
it('should update view queries if embedded views are added or removed', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
viewQueryProvider(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'),
...viewQueryProviders(compViewDef([
anchorDef(
NodeFlags.HasEmbeddedViews, null, 0,
viewDef(
@ -266,17 +268,17 @@ export function main() {
checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance;
const comp: QueryService = asProviderData(view, 1).instance;
expect(comp.a.length).toBe(0);
const compView = view.nodes[1].provider.componentView;
const compView = asProviderData(view, 1).componentView;
const childView = createEmbeddedView(compView, compView.def.nodes[0]);
attachEmbeddedView(compView.nodes[0], 0, childView);
attachEmbeddedView(asElementData(compView, 0), 0, childView);
checkAndUpdateView(view);
expect(comp.a.length).toBe(1);
detachEmbeddedView(compView.nodes[0], 0);
detachEmbeddedView(asElementData(compView, 0), 0);
checkAndUpdateView(view);
expect(comp.a.length).toBe(0);
@ -290,21 +292,20 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'),
providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.All]}),
elementDef(NodeFlags.None, null, 4, 'div'),
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All}),
aServiceProvider(),
aServiceProvider(),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a instanceof QueryList).toBeTruthy();
expect(qs.a.toArray()).toEqual([
view.nodes[2].provider.instance,
view.nodes[3].provider.instance,
asProviderData(view, 3).instance,
asProviderData(view, 4).instance,
]);
});
@ -314,18 +315,17 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'),
providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.First]}),
elementDef(NodeFlags.None, null, 4, 'div'),
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
aServiceProvider(),
aServiceProvider(),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
expect(qs.a).toBe(view.nodes[2].provider.instance);
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a).toBe(asProviderData(view, 3).instance);
});
});
@ -336,16 +336,15 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 1, 'div'),
providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.First]}),
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 2, 'div'),
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
expect(qs.a.nativeElement).toBe(view.nodes[0].elementOrText.node);
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
});
it('should query TemplateRef', () => {
@ -355,16 +354,15 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
anchorDef(
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 1,
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 2,
viewDef(ViewFlags.None, [anchorDef(NodeFlags.None, null, 0)])),
providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.First]}),
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.createEmbeddedView).toBeTruthy();
});
@ -374,15 +372,14 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 1),
providerDef(
NodeFlags.None, null, QueryService, [], null, null,
{'a': ['query1', QueryBindingType.First]}),
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 2),
providerDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
]));
checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance;
const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.createEmbeddedView).toBeTruthy();
});
});

View File

@ -124,7 +124,7 @@ export function main() {
it('should calculate childFlags for one level', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, [])
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, [])
]);
expect(childFlags(vd)).toEqual([NodeFlags.AfterContentChecked, NodeFlags.None]);
@ -133,7 +133,7 @@ export function main() {
it('should calculate childFlags for two levels', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, [])
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, [])
]);
expect(childFlags(vd)).toEqual([
@ -144,10 +144,10 @@ export function main() {
it('should calculate childFlags for one level, multiple roots', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, []),
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.AfterContentInit, null, AService, []),
providerDef(NodeFlags.AfterViewChecked, null, AService, []),
providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
providerDef(NodeFlags.AfterViewChecked, null, 0, AService, []),
]);
expect(childFlags(vd)).toEqual([
@ -160,10 +160,10 @@ export function main() {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, []),
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.AfterContentInit, null, AService, []),
providerDef(NodeFlags.AfterViewInit, null, AService, []),
providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
providerDef(NodeFlags.AfterViewInit, null, 0, AService, []),
]);
expect(childFlags(vd)).toEqual([
@ -181,7 +181,7 @@ export function main() {
it('should calculate childMatchedQueries for one level', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, [])
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], []]);
@ -190,7 +190,7 @@ export function main() {
it('should calculate childMatchedQueries for two levels', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, [])
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], []]);
@ -199,10 +199,10 @@ export function main() {
it('should calculate childMatchedQueries for one level, multiple roots', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], [], ['q2', 'q3'], [], []]);
@ -212,10 +212,10 @@ export function main() {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'),
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], [], ['q2', 'q3'], [], []]);