Skip to content

Commit

Permalink
feat(ivy): add query inheritance (angular#25556)
Browse files Browse the repository at this point in the history
Adds inheritance handling for the following:

- viewQuery
- contentQueries
- contentQueriesRefresh

PR Close angular#25556
  • Loading branch information
benlesh authored and FrederikSchlemmer committed Jan 3, 2019
1 parent 063ca80 commit eb1e965
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 16 deletions.
48 changes: 47 additions & 1 deletion packages/core/src/render3/features/inherit_definition_feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';



/**
Expand Down Expand Up @@ -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
fillProperties(definition.inputs, superDef.inputs);
fillProperties(definition.declaredInputs, superDef.declaredInputs);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/render3/interfaces/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export interface ComponentDef<T, Selector extends string> extends DirectiveDef<T
/**
* 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
Expand Down
118 changes: 104 additions & 14 deletions packages/core/test/render3/Inherit_definition_feature_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -48,7 +48,6 @@ describe('InheritDefinitionFeature', () => {
});

it('should inherit inputs', () => {
// tslint:disable-next-line:class-as-namespace
class SuperDirective {
static ngDirectiveDef = defineDirective({
inputs: {
Expand All @@ -62,7 +61,6 @@ describe('InheritDefinitionFeature', () => {
});
}

// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({
type: SubDirective,
Expand Down Expand Up @@ -93,7 +91,6 @@ describe('InheritDefinitionFeature', () => {
});

it('should inherit outputs', () => {
// tslint:disable-next-line:class-as-namespace
class SuperDirective {
static ngDirectiveDef = defineDirective({
outputs: {
Expand All @@ -107,7 +104,6 @@ describe('InheritDefinitionFeature', () => {
});
}

// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({
type: SubDirective,
Expand Down Expand Up @@ -143,7 +139,6 @@ describe('InheritDefinitionFeature', () => {
});
}

// tslint:disable-next-line:class-as-namespace
class Class4 extends Class5 {
input4 = 'hehe';

Expand All @@ -170,7 +165,6 @@ describe('InheritDefinitionFeature', () => {
}) as any;
}

// tslint:disable-next-line:class-as-namespace
class Class1 extends Class2 {
input1 = 'test';
input2 = 'whatever';
Expand Down Expand Up @@ -217,7 +211,6 @@ describe('InheritDefinitionFeature', () => {
});
}

// tslint:disable-next-line:class-as-namespace
class Class4 extends Class5 {
output4 = 'hehe';

Expand All @@ -244,7 +237,6 @@ describe('InheritDefinitionFeature', () => {
}) as any;
}

// tslint:disable-next-line:class-as-namespace
class Class1 extends Class2 {
output1 = 'test';
output2 = 'whatever';
Expand Down Expand Up @@ -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,
Expand All @@ -287,7 +278,6 @@ describe('InheritDefinitionFeature', () => {
});
}

// tslint:disable-next-line:class-as-namespace
class SubDirective extends SuperDirective {
static ngDirectiveDef = defineDirective({
type: SubDirective,
Expand All @@ -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: <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', () => {
// tslint:disable-next-line:class-as-namespace
class SuperComponent {
static ngComponentDef = defineComponent({
type: SuperComponent,
Expand All @@ -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', '']],
Expand All @@ -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,
Expand Down

0 comments on commit eb1e965

Please sign in to comment.