From f54f3856cbe082ef1468906c68de88dc4d842a66 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Fri, 17 Aug 2018 12:49:02 -0700 Subject: [PATCH] feat(ivy): add query inheritance (#25556) Adds inheritance handling for the following: - viewQuery - contentQueries - contentQueriesRefresh PR Close #25556 --- .../features/inherit_definition_feature.ts | 48 ++++++- .../core/src/render3/interfaces/definition.ts | 2 +- .../Inherit_definition_feature_spec.ts | 118 +++++++++++++++--- 3 files changed, 152 insertions(+), 16 deletions(-) diff --git a/packages/core/src/render3/features/inherit_definition_feature.ts b/packages/core/src/render3/features/inherit_definition_feature.ts index 72bcec9784..f28d1bc200 100644 --- a/packages/core/src/render3/features/inherit_definition_feature.ts +++ b/packages/core/src/render3/features/inherit_definition_feature.ts @@ -8,7 +8,8 @@ import {Type} from '../../type'; 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 = (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 fillProperties(definition.inputs, superDef.inputs); fillProperties(definition.declaredInputs, superDef.declaredInputs); diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index c8ff1dd1f2..ba752e032c 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -221,7 +221,7 @@ export interface ComponentDef extends DirectiveDef|null; + viewQuery: ComponentQuery|null; /** * The view encapsulation type, which determines how styles are applied to diff --git a/packages/core/test/render3/Inherit_definition_feature_spec.ts b/packages/core/test/render3/Inherit_definition_feature_spec.ts index b93baf8043..3cdf5fe902 100644 --- a/packages/core/test/render3/Inherit_definition_feature_spec.ts +++ b/packages/core/test/render3/Inherit_definition_feature_spec.ts @@ -8,7 +8,7 @@ import {EventEmitter, Output} from '../../src/core'; 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', () => { it('should inherit lifecycle hooks', () => { @@ -48,7 +48,6 @@ describe('InheritDefinitionFeature', () => { }); it('should inherit inputs', () => { - // tslint:disable-next-line:class-as-namespace class SuperDirective { static ngDirectiveDef = defineDirective({ inputs: { @@ -62,7 +61,6 @@ describe('InheritDefinitionFeature', () => { }); } - // tslint:disable-next-line:class-as-namespace class SubDirective extends SuperDirective { static ngDirectiveDef = defineDirective({ type: SubDirective, @@ -93,7 +91,6 @@ describe('InheritDefinitionFeature', () => { }); it('should inherit outputs', () => { - // tslint:disable-next-line:class-as-namespace class SuperDirective { static ngDirectiveDef = defineDirective({ outputs: { @@ -107,7 +104,6 @@ describe('InheritDefinitionFeature', () => { }); } - // tslint:disable-next-line:class-as-namespace class SubDirective extends SuperDirective { static ngDirectiveDef = defineDirective({ type: SubDirective, @@ -143,7 +139,6 @@ describe('InheritDefinitionFeature', () => { }); } - // tslint:disable-next-line:class-as-namespace class Class4 extends Class5 { input4 = 'hehe'; @@ -170,7 +165,6 @@ describe('InheritDefinitionFeature', () => { }) as any; } - // tslint:disable-next-line:class-as-namespace class Class1 extends Class2 { input1 = 'test'; input2 = 'whatever'; @@ -217,7 +211,6 @@ describe('InheritDefinitionFeature', () => { }); } - // tslint:disable-next-line:class-as-namespace class Class4 extends Class5 { output4 = 'hehe'; @@ -244,7 +237,6 @@ describe('InheritDefinitionFeature', () => { }) as any; } - // tslint:disable-next-line:class-as-namespace class Class1 extends Class2 { output1 = 'test'; output2 = 'whatever'; @@ -275,7 +267,6 @@ describe('InheritDefinitionFeature', () => { it('should compose hostBindings', () => { const log: Array<[string, number, number]> = []; - // tslint:disable-next-line:class-as-namespace class SuperDirective { static ngDirectiveDef = defineDirective({ type: SuperDirective, @@ -287,7 +278,6 @@ describe('InheritDefinitionFeature', () => { }); } - // tslint:disable-next-line:class-as-namespace class SubDirective extends SuperDirective { static ngDirectiveDef = defineDirective({ type: SubDirective, @@ -307,8 +297,110 @@ describe('InheritDefinitionFeature', () => { 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: (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; + + 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; + + 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; + + subDef.contentQueriesRefresh !(1, 2); + + expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]); + }); + it('should throw if inheriting a component from a directive', () => { - // tslint:disable-next-line:class-as-namespace class SuperComponent { static ngComponentDef = defineComponent({ type: SuperComponent, @@ -321,7 +413,6 @@ describe('InheritDefinitionFeature', () => { } expect(() => { - // tslint:disable-next-line:class-as-namespace class SubDirective extends SuperComponent{static ngDirectiveDef = defineDirective({ type: SubDirective, selectors: [['', 'subDir', '']], @@ -334,7 +425,6 @@ describe('InheritDefinitionFeature', () => { it('should run inherited features', () => { const log: any[] = []; - // tslint:disable-next-line:class-as-namespace class SuperDirective { static ngDirectiveDef = defineDirective({ type: SuperDirective,