feat(ivy): add query inheritance (#25556)

Adds inheritance handling for the following:

- viewQuery
- contentQueries
- contentQueriesRefresh

PR Close #25556
This commit is contained in:
Ben Lesh 2018-08-17 12:49:02 -07:00 committed by Jason Aden
parent c8b70ae8e4
commit f54f3856cb
3 changed files with 152 additions and 16 deletions

View File

@ -8,7 +8,8 @@
import {Type} from '../../type'; import {Type} from '../../type';
import {fillProperties} from '../../util/property'; import {fillProperties} from '../../util/property';
import {ComponentDefInternal, DirectiveDefFeature, DirectiveDefInternal} from '../interfaces/definition'; import {ComponentDefInternal, ComponentTemplate, DirectiveDefFeature, DirectiveDefInternal, RenderFlags} from '../interfaces/definition';
/** /**
@ -68,6 +69,51 @@ export function InheritDefinitionFeature(
} }
} }
// Merge View Queries
if (isComponentDef(definition) && isComponentDef(superDef)) {
const prevViewQuery = definition.viewQuery;
const superViewQuery = superDef.viewQuery;
if (superViewQuery) {
if (prevViewQuery) {
definition.viewQuery = <T>(rf: RenderFlags, ctx: T): void => {
superViewQuery(rf, ctx);
prevViewQuery(rf, ctx);
};
} else {
definition.viewQuery = superViewQuery;
}
}
}
// Merge Content Queries
const prevContentQueries = definition.contentQueries;
const superContentQueries = superDef.contentQueries;
if (superContentQueries) {
if (prevContentQueries) {
definition.contentQueries = () => {
superContentQueries();
prevContentQueries();
};
} else {
definition.contentQueries = superContentQueries;
}
}
// Merge Content Queries Refresh
const prevContentQueriesRefresh = definition.contentQueriesRefresh;
const superContentQueriesRefresh = superDef.contentQueriesRefresh;
if (superContentQueriesRefresh) {
if (prevContentQueriesRefresh) {
definition.contentQueriesRefresh = (directiveIndex: number, queryIndex: number) => {
superContentQueriesRefresh(directiveIndex, queryIndex);
prevContentQueriesRefresh(directiveIndex, queryIndex);
};
} else {
definition.contentQueriesRefresh = superContentQueriesRefresh;
}
}
// Merge inputs and outputs // Merge inputs and outputs
fillProperties(definition.inputs, superDef.inputs); fillProperties(definition.inputs, superDef.inputs);
fillProperties(definition.declaredInputs, superDef.declaredInputs); fillProperties(definition.declaredInputs, superDef.declaredInputs);

View File

@ -221,7 +221,7 @@ export interface ComponentDef<T, Selector extends string> extends DirectiveDef<T
/** /**
* Query-related instructions for a component. * Query-related instructions for a component.
*/ */
readonly viewQuery: ComponentQuery<T>|null; viewQuery: ComponentQuery<T>|null;
/** /**
* The view encapsulation type, which determines how styles are applied to * The view encapsulation type, which determines how styles are applied to

View File

@ -8,7 +8,7 @@
import {EventEmitter, Output} from '../../src/core'; import {EventEmitter, Output} from '../../src/core';
import {InheritDefinitionFeature} from '../../src/render3/features/inherit_definition_feature'; import {InheritDefinitionFeature} from '../../src/render3/features/inherit_definition_feature';
import {DirectiveDefInternal, defineBase, defineComponent, defineDirective} from '../../src/render3/index'; import {ComponentDefInternal, DirectiveDefInternal, RenderFlags, defineBase, defineComponent, defineDirective} from '../../src/render3/index';
describe('InheritDefinitionFeature', () => { describe('InheritDefinitionFeature', () => {
it('should inherit lifecycle hooks', () => { it('should inherit lifecycle hooks', () => {
@ -48,7 +48,6 @@ describe('InheritDefinitionFeature', () => {
}); });
it('should inherit inputs', () => { it('should inherit inputs', () => {
// tslint:disable-next-line:class-as-namespace
class SuperDirective { class SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
inputs: { inputs: {
@ -62,7 +61,6 @@ describe('InheritDefinitionFeature', () => {
}); });
} }
// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective { class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SubDirective, type: SubDirective,
@ -93,7 +91,6 @@ describe('InheritDefinitionFeature', () => {
}); });
it('should inherit outputs', () => { it('should inherit outputs', () => {
// tslint:disable-next-line:class-as-namespace
class SuperDirective { class SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
outputs: { outputs: {
@ -107,7 +104,6 @@ describe('InheritDefinitionFeature', () => {
}); });
} }
// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective { class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SubDirective, type: SubDirective,
@ -143,7 +139,6 @@ describe('InheritDefinitionFeature', () => {
}); });
} }
// tslint:disable-next-line:class-as-namespace
class Class4 extends Class5 { class Class4 extends Class5 {
input4 = 'hehe'; input4 = 'hehe';
@ -170,7 +165,6 @@ describe('InheritDefinitionFeature', () => {
}) as any; }) as any;
} }
// tslint:disable-next-line:class-as-namespace
class Class1 extends Class2 { class Class1 extends Class2 {
input1 = 'test'; input1 = 'test';
input2 = 'whatever'; input2 = 'whatever';
@ -217,7 +211,6 @@ describe('InheritDefinitionFeature', () => {
}); });
} }
// tslint:disable-next-line:class-as-namespace
class Class4 extends Class5 { class Class4 extends Class5 {
output4 = 'hehe'; output4 = 'hehe';
@ -244,7 +237,6 @@ describe('InheritDefinitionFeature', () => {
}) as any; }) as any;
} }
// tslint:disable-next-line:class-as-namespace
class Class1 extends Class2 { class Class1 extends Class2 {
output1 = 'test'; output1 = 'test';
output2 = 'whatever'; output2 = 'whatever';
@ -275,7 +267,6 @@ describe('InheritDefinitionFeature', () => {
it('should compose hostBindings', () => { it('should compose hostBindings', () => {
const log: Array<[string, number, number]> = []; const log: Array<[string, number, number]> = [];
// tslint:disable-next-line:class-as-namespace
class SuperDirective { class SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SuperDirective, type: SuperDirective,
@ -287,7 +278,6 @@ describe('InheritDefinitionFeature', () => {
}); });
} }
// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective { class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SubDirective, type: SubDirective,
@ -307,8 +297,110 @@ describe('InheritDefinitionFeature', () => {
expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]); expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]);
}); });
it('should compose viewQuery', () => {
const log: Array<[string, RenderFlags, any]> = [];
class SuperComponent {
static ngComponentDef = defineComponent({
type: SuperComponent,
template: () => {},
consts: 0,
vars: 0,
selectors: [['', 'superDir', '']],
viewQuery: <T>(rf: RenderFlags, ctx: T) => {
log.push(['super', rf, ctx]);
},
factory: () => new SuperComponent(),
});
}
class SubComponent extends SuperComponent {
static ngComponentDef = defineComponent({
type: SubComponent,
template: () => {},
consts: 0,
vars: 0,
selectors: [['', 'subDir', '']],
viewQuery: (directiveIndex: number, elementIndex: number) => {
log.push(['sub', directiveIndex, elementIndex]);
},
factory: () => new SubComponent(),
features: [InheritDefinitionFeature]
});
}
const subDef = SubComponent.ngComponentDef as ComponentDefInternal<any>;
const context = {foo: 'bar'};
subDef.viewQuery !(1, context);
expect(log).toEqual([['super', 1, context], ['sub', 1, context]]);
});
it('should compose contentQueries', () => {
const log: string[] = [];
class SuperDirective {
static ngDirectiveDef = defineDirective({
type: SuperDirective,
selectors: [['', 'superDir', '']],
contentQueries: () => { log.push('super'); },
factory: () => new SuperDirective(),
});
}
class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({
type: SubDirective,
selectors: [['', 'subDir', '']],
contentQueries: () => { log.push('sub'); },
factory: () => new SubDirective(),
features: [InheritDefinitionFeature]
});
}
const subDef = SubDirective.ngDirectiveDef as DirectiveDefInternal<any>;
subDef.contentQueries !();
expect(log).toEqual(['super', 'sub']);
});
it('should compose contentQueriesRefresh', () => {
const log: Array<[string, number, number]> = [];
class SuperDirective {
static ngDirectiveDef = defineDirective({
type: SuperDirective,
selectors: [['', 'superDir', '']],
contentQueriesRefresh: (directiveIndex: number, queryIndex: number) => {
log.push(['super', directiveIndex, queryIndex]);
},
factory: () => new SuperDirective(),
});
}
class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({
type: SubDirective,
selectors: [['', 'subDir', '']],
contentQueriesRefresh: (directiveIndex: number, queryIndex: number) => {
log.push(['sub', directiveIndex, queryIndex]);
},
factory: () => new SubDirective(),
features: [InheritDefinitionFeature]
});
}
const subDef = SubDirective.ngDirectiveDef as DirectiveDefInternal<any>;
subDef.contentQueriesRefresh !(1, 2);
expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]);
});
it('should throw if inheriting a component from a directive', () => { it('should throw if inheriting a component from a directive', () => {
// tslint:disable-next-line:class-as-namespace
class SuperComponent { class SuperComponent {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: SuperComponent, type: SuperComponent,
@ -321,7 +413,6 @@ describe('InheritDefinitionFeature', () => {
} }
expect(() => { expect(() => {
// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperComponent{static ngDirectiveDef = defineDirective({ class SubDirective extends SuperComponent{static ngDirectiveDef = defineDirective({
type: SubDirective, type: SubDirective,
selectors: [['', 'subDir', '']], selectors: [['', 'subDir', '']],
@ -334,7 +425,6 @@ describe('InheritDefinitionFeature', () => {
it('should run inherited features', () => { it('should run inherited features', () => {
const log: any[] = []; const log: any[] = [];
// tslint:disable-next-line:class-as-namespace
class SuperDirective { class SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SuperDirective, type: SuperDirective,