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:
@@ -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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user