feat(compiler): integrate compiler with view engine (#14487)

Aspects: di, query, content projection

Included refactoring:
- use a number as query id
- use a bloom filter for aggregating matched queries of nested elements
- separate static vs dynamic queries

Part of #14013
This commit is contained in:
Tobias Bosch
2017-02-15 08:36:49 -08:00
committed by Igor Minar
parent e9ba7aa4f8
commit 4e7752a12a
29 changed files with 810 additions and 558 deletions
+32 -13
View File
@@ -33,6 +33,8 @@ export function main() {
return {rootNodes, view};
}
const someQueryId = 1;
class AService {}
class QueryService {
@@ -42,19 +44,24 @@ export function main() {
function contentQueryProviders() {
return [
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All})
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.All})
];
}
function viewQueryProviders(compView: ViewDefinition) {
return [
directiveDef(NodeFlags.None, null, 1, QueryService, [], null, null, () => compView),
queryDef(NodeFlags.HasViewQuery, 'query1', {'a': QueryBindingType.All})
queryDef(
NodeFlags.HasViewQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.All})
];
}
function aServiceProvider() {
return directiveDef(NodeFlags.None, [['query1', QueryValueType.Provider]], 0, AService, []);
return directiveDef(
NodeFlags.None, [[someQueryId, QueryValueType.Provider]], 0, AService, []);
}
describe('content queries', () => {
@@ -251,7 +258,9 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 4, 'div'),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.All}),
aServiceProvider(),
aServiceProvider(),
]));
@@ -274,7 +283,9 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 4, 'div'),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.First}),
aServiceProvider(),
aServiceProvider(),
]));
@@ -293,9 +304,11 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], null, 2, 'div'),
elementDef(NodeFlags.None, [[someQueryId, QueryValueType.ElementRef]], null, 2, 'div'),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.First}),
]));
Services.checkAndUpdateView(view);
@@ -311,10 +324,12 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
anchorDef(
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], null, 2,
NodeFlags.None, [[someQueryId, QueryValueType.TemplateRef]], null, 2,
embeddedViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.First}),
]));
Services.checkAndUpdateView(view);
@@ -329,9 +344,11 @@ export function main() {
}
const {view} = createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], null, 2),
anchorDef(NodeFlags.None, [[someQueryId, QueryValueType.ViewContainerRef]], null, 2),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.First}),
]));
Services.checkAndUpdateView(view);
@@ -367,7 +384,7 @@ export function main() {
expect(err).toBeTruthy();
expect(err.message)
.toBe(
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Query 1 not dirty'. Current value: 'Query 1 dirty'.`);
const debugCtx = getDebugContext(err);
expect(debugCtx.view).toBe(view);
expect(debugCtx.nodeIndex).toBe(2);
@@ -381,7 +398,9 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'),
directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All}),
queryDef(
NodeFlags.HasContentQuery | NodeFlags.HasDynamicQuery, someQueryId,
{'a': QueryBindingType.All}),
aServiceProvider(),
]));
@@ -39,7 +39,7 @@ export function main() {
directiveDef(
NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, [['#ref', QueryValueType.ElementRef]], null, 2, 'span'),
elementDef(NodeFlags.None, [['ref', QueryValueType.ElementRef]], null, 2, 'span'),
directiveDef(NodeFlags.None, null, 0, AService, []), textDef(null, ['a'])
])),
]));
@@ -7,6 +7,7 @@
*/
import {NodeFlags, QueryValueType, ViewData, ViewDefinition, ViewFlags, anchorDef, directiveDef, elementDef, textDef, viewDef} from '@angular/core/src/view/index';
import {filterQueryId} from '@angular/core/src/view/util';
export function main() {
describe('viewDef', () => {
@@ -76,7 +77,7 @@ export function main() {
describe('parent', () => {
function parents(viewDef: ViewDefinition): number[] {
return viewDef.nodes.map(node => node.parent);
return viewDef.nodes.map(node => node.parent ? node.parent.index : null);
}
it('should calculate parents for one level', () => {
@@ -86,7 +87,7 @@ export function main() {
textDef(null, ['a']),
]);
expect(parents(vd)).toEqual([undefined, 0, 0]);
expect(parents(vd)).toEqual([null, 0, 0]);
});
it('should calculate parents for one level, multiple roots', () => {
@@ -98,7 +99,7 @@ export function main() {
textDef(null, ['a']),
]);
expect(parents(vd)).toEqual([undefined, 0, undefined, 2, undefined]);
expect(parents(vd)).toEqual([null, 0, null, 2, null]);
});
it('should calculate parents for multiple levels', () => {
@@ -111,7 +112,7 @@ export function main() {
textDef(null, ['a']),
]);
expect(parents(vd)).toEqual([undefined, 0, 1, undefined, 3, undefined]);
expect(parents(vd)).toEqual([null, 0, 1, null, 3, null]);
});
});
@@ -175,52 +176,56 @@ export function main() {
});
describe('childMatchedQueries', () => {
function childMatchedQueries(viewDef: ViewDefinition): string[][] {
return viewDef.nodes.map(node => Object.keys(node.childMatchedQueries).sort());
function childMatchedQueries(viewDef: ViewDefinition): number[] {
return viewDef.nodes.map(node => node.childMatchedQueries);
}
it('should calculate childMatchedQueries for one level', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
directiveDef(NodeFlags.None, [[1, QueryValueType.Provider]], 0, AService, [])
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], []]);
expect(childMatchedQueries(vd)).toEqual([filterQueryId(1), 0]);
});
it('should calculate childMatchedQueries for two levels', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, null, 2, 'span'),
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
directiveDef(NodeFlags.None, [[1, QueryValueType.Provider]], 0, AService, [])
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], []]);
expect(childMatchedQueries(vd)).toEqual([filterQueryId(1), filterQueryId(1), 0]);
});
it('should calculate childMatchedQueries for one level, multiple roots', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[1, QueryValueType.Provider]], 0, AService, []),
elementDef(NodeFlags.None, null, null, 2, 'span'),
directiveDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[2, QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[3, QueryValueType.Provider]], 0, AService, []),
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], [], ['q2', 'q3'], [], []]);
expect(childMatchedQueries(vd)).toEqual([
filterQueryId(1), 0, filterQueryId(2) | filterQueryId(3), 0, 0
]);
});
it('should calculate childMatchedQueries for multiple levels', () => {
const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, null, 2, 'span'),
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[1, QueryValueType.Provider]], 0, AService, []),
elementDef(NodeFlags.None, null, null, 2, 'span'),
directiveDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[2, QueryValueType.Provider]], 0, AService, []),
directiveDef(NodeFlags.None, [[3, QueryValueType.Provider]], 0, AService, []),
]);
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], [], ['q2', 'q3'], [], []]);
expect(childMatchedQueries(vd)).toEqual([
filterQueryId(1), filterQueryId(1), 0, filterQueryId(2) | filterQueryId(3), 0, 0
]);
});
it('should included embedded views into childMatchedQueries', () => {
@@ -231,12 +236,12 @@ export function main() {
() => viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, [['q1', QueryValueType.Provider]], null, 0, 'span'),
elementDef(NodeFlags.None, [[1, QueryValueType.Provider]], null, 0, 'span'),
]))
]);
// Note: the template will become a sibling to the anchor once stamped out,
expect(childMatchedQueries(vd)).toEqual([['q1'], []]);
expect(childMatchedQueries(vd)).toEqual([filterQueryId(1), 0]);
});
});
});