fix(ivy): unable to inherit view queries into component from directive (#29203)
Fixes components not being able to inherit their view queries from a directive. This PR resolves FW-1146. PR Close #29203
This commit is contained in:
parent
a5a35ff54a
commit
0ffa2f2e73
|
@ -145,7 +145,7 @@ export class ComponentDecoratorHandler implements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, read the `@Component`-specific fields.
|
// Next, read the `@Component`-specific fields.
|
||||||
const {decoratedElements, decorator: component, metadata} = directiveResult;
|
const {decorator: component, metadata} = directiveResult;
|
||||||
|
|
||||||
// Go through the root directories for this project, and select the one with the smallest
|
// Go through the root directories for this project, and select the one with the smallest
|
||||||
// relative path representation.
|
// relative path representation.
|
||||||
|
@ -223,22 +223,6 @@ export class ComponentDecoratorHandler implements
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the list of view queries.
|
|
||||||
const coreModule = this.isCore ? undefined : '@angular/core';
|
|
||||||
const viewChildFromFields = queriesFromFields(
|
|
||||||
filterToMembersWithDecorator(decoratedElements, 'ViewChild', coreModule), this.reflector,
|
|
||||||
this.evaluator);
|
|
||||||
const viewChildrenFromFields = queriesFromFields(
|
|
||||||
filterToMembersWithDecorator(decoratedElements, 'ViewChildren', coreModule), this.reflector,
|
|
||||||
this.evaluator);
|
|
||||||
const viewQueries = [...viewChildFromFields, ...viewChildrenFromFields];
|
|
||||||
|
|
||||||
if (component.has('queries')) {
|
|
||||||
const queriesFromDecorator = extractQueriesFromDecorator(
|
|
||||||
component.get('queries') !, this.reflector, this.evaluator, this.isCore);
|
|
||||||
viewQueries.push(...queriesFromDecorator.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out the set of styles. The ordering here is important: external resources (styleUrls)
|
// Figure out the set of styles. The ordering here is important: external resources (styleUrls)
|
||||||
// precede inline styles, and styles defined in the template override styles defined in the
|
// precede inline styles, and styles defined in the template override styles defined in the
|
||||||
// component.
|
// component.
|
||||||
|
@ -288,7 +272,6 @@ export class ComponentDecoratorHandler implements
|
||||||
meta: {
|
meta: {
|
||||||
...metadata,
|
...metadata,
|
||||||
template,
|
template,
|
||||||
viewQueries,
|
|
||||||
encapsulation,
|
encapsulation,
|
||||||
interpolation: template.interpolation,
|
interpolation: template.interpolation,
|
||||||
styles: styles || [],
|
styles: styles || [],
|
||||||
|
|
|
@ -160,10 +160,20 @@ export function extractDirectiveMetadata(
|
||||||
|
|
||||||
const queries = [...contentChildFromFields, ...contentChildrenFromFields];
|
const queries = [...contentChildFromFields, ...contentChildrenFromFields];
|
||||||
|
|
||||||
|
// Construct the list of view queries.
|
||||||
|
const viewChildFromFields = queriesFromFields(
|
||||||
|
filterToMembersWithDecorator(decoratedElements, 'ViewChild', coreModule), reflector,
|
||||||
|
evaluator);
|
||||||
|
const viewChildrenFromFields = queriesFromFields(
|
||||||
|
filterToMembersWithDecorator(decoratedElements, 'ViewChildren', coreModule), reflector,
|
||||||
|
evaluator);
|
||||||
|
const viewQueries = [...viewChildFromFields, ...viewChildrenFromFields];
|
||||||
|
|
||||||
if (directive.has('queries')) {
|
if (directive.has('queries')) {
|
||||||
const queriesFromDecorator =
|
const queriesFromDecorator =
|
||||||
extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore);
|
extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore);
|
||||||
queries.push(...queriesFromDecorator.content);
|
queries.push(...queriesFromDecorator.content);
|
||||||
|
viewQueries.push(...queriesFromDecorator.view);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the selector.
|
// Parse the selector.
|
||||||
|
@ -213,7 +223,7 @@ export function extractDirectiveMetadata(
|
||||||
usesOnChanges,
|
usesOnChanges,
|
||||||
},
|
},
|
||||||
inputs: {...inputsFromMeta, ...inputsFromFields},
|
inputs: {...inputsFromMeta, ...inputsFromFields},
|
||||||
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
|
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector,
|
||||||
type: new WrappedNodeExpr(clazz.name !),
|
type: new WrappedNodeExpr(clazz.name !),
|
||||||
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
|
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
|
||||||
typeSourceSpan: null !, usesInheritance, exportAs, providers
|
typeSourceSpan: null !, usesInheritance, exportAs, providers
|
||||||
|
|
|
@ -983,7 +983,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
env.tsconfig({strictInjectionParameters: true});
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Test {
|
export class Test {
|
||||||
constructor(private notInjectable: string) {}
|
constructor(private notInjectable: string) {}
|
||||||
|
@ -1000,7 +1000,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
env.tsconfig({strictInjectionParameters: true});
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Test {
|
export class Test {
|
||||||
constructor(private notInjectable: string) {}
|
constructor(private notInjectable: string) {}
|
||||||
|
@ -1017,7 +1017,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
env.tsconfig({strictInjectionParameters: true});
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
useValue: '42',
|
useValue: '42',
|
||||||
|
@ -1358,6 +1358,41 @@ describe('ngtsc behavioral tests', () => {
|
||||||
expect(jsContents).toMatch(viewQueryRegExp(true));
|
expect(jsContents).toMatch(viewQueryRegExp(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate queries for directives', () => {
|
||||||
|
env.tsconfig();
|
||||||
|
env.write(`test.ts`, `
|
||||||
|
import {Directive, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[test]',
|
||||||
|
queries: {
|
||||||
|
'mview': new ViewChild('test1'),
|
||||||
|
'mcontent': new ContentChild('test2'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
class FooCmp {
|
||||||
|
@ContentChild('bar', {read: TemplateRef}) child: any;
|
||||||
|
@ContentChildren(TemplateRef) children: any;
|
||||||
|
get aview(): any { return null; }
|
||||||
|
@ViewChild('accessor') set aview(value: any) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents).toMatch(varRegExp('bar'));
|
||||||
|
expect(jsContents).toMatch(varRegExp('test1'));
|
||||||
|
expect(jsContents).toMatch(varRegExp('test2'));
|
||||||
|
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||||
|
// match `i0.ɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
|
||||||
|
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
|
||||||
|
|
||||||
|
// match `i0.ɵviewQuery(_c2, true, null)`
|
||||||
|
// Note that while ViewQuery doesn't necessarily make sense on a directive, because it doesn't
|
||||||
|
// have a view, we still need to handle it because a component could extend the directive.
|
||||||
|
expect(jsContents).toMatch(viewQueryRegExp(true));
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle queries that use forwardRef', () => {
|
it('should handle queries that use forwardRef', () => {
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
env.write(`test.ts`, `
|
env.write(`test.ts`, `
|
||||||
|
@ -1931,7 +1966,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
it('should be able to compile an app using the factory shim', () => {
|
it('should be able to compile an app using the factory shim', () => {
|
||||||
env.tsconfig({'allowEmptyCodegenFiles': true});
|
env.tsconfig({'allowEmptyCodegenFiles': true});
|
||||||
|
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
export {MyModuleNgFactory} from './my-module.ngfactory';
|
export {MyModuleNgFactory} from './my-module.ngfactory';
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
@ -2217,7 +2252,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {Other} from './types';
|
import {Other} from './types';
|
||||||
import Default from './types';
|
import Default from './types';
|
||||||
|
|
||||||
@Component({selector: 'test', template: 'test'})
|
@Component({selector: 'test', template: 'test'})
|
||||||
export class SomeCmp {
|
export class SomeCmp {
|
||||||
constructor(arg: Default, other: Other) {}
|
constructor(arg: Default, other: Other) {}
|
||||||
|
|
|
@ -129,13 +129,13 @@ export interface R3DirectiveMetadataFacade {
|
||||||
usesInheritance: boolean;
|
usesInheritance: boolean;
|
||||||
exportAs: string[]|null;
|
exportAs: string[]|null;
|
||||||
providers: Provider[]|null;
|
providers: Provider[]|null;
|
||||||
|
viewQueries: R3QueryMetadataFacade[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
template: string;
|
template: string;
|
||||||
preserveWhitespaces: boolean;
|
preserveWhitespaces: boolean;
|
||||||
animations: any[]|undefined;
|
animations: any[]|undefined;
|
||||||
viewQueries: R3QueryMetadataFacade[];
|
|
||||||
pipes: Map<string, any>;
|
pipes: Map<string, any>;
|
||||||
directives: {selector: string, expression: any}[];
|
directives: {selector: string, expression: any}[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
|
|
|
@ -133,7 +133,6 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
||||||
...convertDirectiveFacadeToMetadata(facade),
|
...convertDirectiveFacadeToMetadata(facade),
|
||||||
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
||||||
template,
|
template,
|
||||||
viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
|
|
||||||
wrapDirectivesAndPipesInClosure: false,
|
wrapDirectivesAndPipesInClosure: false,
|
||||||
styles: facade.styles || [],
|
styles: facade.styles || [],
|
||||||
encapsulation: facade.encapsulation as any,
|
encapsulation: facade.encapsulation as any,
|
||||||
|
@ -235,6 +234,7 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3
|
||||||
outputs: {...outputsFromMetadata, ...outputsFromType},
|
outputs: {...outputsFromMetadata, ...outputsFromType},
|
||||||
queries: facade.queries.map(convertToR3QueryMetadata),
|
queries: facade.queries.map(convertToR3QueryMetadata),
|
||||||
providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null,
|
providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null,
|
||||||
|
viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,11 @@ export interface R3DirectiveMetadata {
|
||||||
*/
|
*/
|
||||||
queries: R3QueryMetadata[];
|
queries: R3QueryMetadata[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the view queries made by the directive.
|
||||||
|
*/
|
||||||
|
viewQueries: R3QueryMetadata[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mappings indicating how the directive interacts with its host element (host bindings,
|
* Mappings indicating how the directive interacts with its host element (host bindings,
|
||||||
* listeners, etc).
|
* listeners, etc).
|
||||||
|
@ -128,11 +133,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||||
nodes: t.Node[];
|
nodes: t.Node[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Information about the view queries made by the component.
|
|
||||||
*/
|
|
||||||
viewQueries: R3QueryMetadata[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of pipe names to an expression referencing the pipe type which are in the scope of the
|
* A map of pipe names to an expression referencing the pipe type which are in the scope of the
|
||||||
* compilation.
|
* compilation.
|
||||||
|
|
|
@ -69,6 +69,10 @@ function baseDirectiveFields(
|
||||||
definitionMap.set('contentQueries', createContentQueriesFunction(meta, constantPool));
|
definitionMap.set('contentQueries', createContentQueriesFunction(meta, constantPool));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (meta.viewQueries.length) {
|
||||||
|
definitionMap.set('viewQuery', createViewQueriesFunction(meta, constantPool));
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize hostVarsCount to number of bound host properties (interpolations illegal),
|
// Initialize hostVarsCount to number of bound host properties (interpolations illegal),
|
||||||
// except 'style' and 'class' properties, since they should *not* allocate host var slots
|
// except 'style' and 'class' properties, since they should *not* allocate host var slots
|
||||||
const hostVarsCount = Object.keys(meta.host.properties)
|
const hostVarsCount = Object.keys(meta.host.properties)
|
||||||
|
@ -228,10 +232,6 @@ export function compileComponentFromMetadata(
|
||||||
directiveMatcher = matcher;
|
directiveMatcher = matcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.viewQueries.length) {
|
|
||||||
definitionMap.set('viewQuery', createViewQueriesFunction(meta, constantPool));
|
|
||||||
}
|
|
||||||
|
|
||||||
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
||||||
const templateTypeName = meta.name;
|
const templateTypeName = meta.name;
|
||||||
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
||||||
|
@ -549,7 +549,7 @@ function createTypeForDef(meta: R3DirectiveMetadata, typeBase: o.ExternalReferen
|
||||||
|
|
||||||
// Define and update any view queries
|
// Define and update any view queries
|
||||||
function createViewQueriesFunction(
|
function createViewQueriesFunction(
|
||||||
meta: R3ComponentMetadata, constantPool: ConstantPool): o.Expression {
|
meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression {
|
||||||
const createStatements: o.Statement[] = [];
|
const createStatements: o.Statement[] = [];
|
||||||
const updateStatements: o.Statement[] = [];
|
const updateStatements: o.Statement[] = [];
|
||||||
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
||||||
|
|
|
@ -129,13 +129,13 @@ export interface R3DirectiveMetadataFacade {
|
||||||
usesInheritance: boolean;
|
usesInheritance: boolean;
|
||||||
exportAs: string[]|null;
|
exportAs: string[]|null;
|
||||||
providers: Provider[]|null;
|
providers: Provider[]|null;
|
||||||
|
viewQueries: R3QueryMetadataFacade[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
|
||||||
template: string;
|
template: string;
|
||||||
preserveWhitespaces: boolean;
|
preserveWhitespaces: boolean;
|
||||||
animations: any[]|undefined;
|
animations: any[]|undefined;
|
||||||
viewQueries: R3QueryMetadataFacade[];
|
|
||||||
pipes: Map<string, any>;
|
pipes: Map<string, any>;
|
||||||
directives: {selector: string, expression: any}[];
|
directives: {selector: string, expression: any}[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
|
|
|
@ -597,6 +597,12 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
|
||||||
*/
|
*/
|
||||||
contentQueries?: ContentQueriesFunction<T>;
|
contentQueries?: ContentQueriesFunction<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional set of instructions specific to view query processing. This could be seen as a
|
||||||
|
* set of instructions to be inserted into the template function.
|
||||||
|
*/
|
||||||
|
viewQuery?: ViewQueriesFunction<T>| null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the name that can be used in the template to assign this directive to a variable.
|
* Defines the name that can be used in the template to assign this directive to a variable.
|
||||||
*
|
*
|
||||||
|
|
|
@ -73,18 +73,17 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge View Queries
|
// Merge View Queries
|
||||||
if (isComponentDef(definition) && isComponentDef(superDef)) {
|
const prevViewQuery = definition.viewQuery;
|
||||||
const prevViewQuery = definition.viewQuery;
|
const superViewQuery = superDef.viewQuery;
|
||||||
const superViewQuery = superDef.viewQuery;
|
|
||||||
if (superViewQuery) {
|
if (superViewQuery) {
|
||||||
if (prevViewQuery) {
|
if (prevViewQuery) {
|
||||||
definition.viewQuery = <T>(rf: RenderFlags, ctx: T): void => {
|
definition.viewQuery = <T>(rf: RenderFlags, ctx: T): void => {
|
||||||
superViewQuery(rf, ctx);
|
superViewQuery(rf, ctx);
|
||||||
prevViewQuery(rf, ctx);
|
prevViewQuery(rf, ctx);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
definition.viewQuery = superViewQuery;
|
definition.viewQuery = superViewQuery;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,13 @@ export interface DirectiveDef<T> extends BaseDef<T> {
|
||||||
*/
|
*/
|
||||||
contentQueries: ContentQueriesFunction<T>|null;
|
contentQueries: ContentQueriesFunction<T>|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query-related instructions for a directive. Note that while directives don't have a
|
||||||
|
* view and as such view queries won't necessarily do anything, there might be
|
||||||
|
* components that extend the directive.
|
||||||
|
*/
|
||||||
|
viewQuery: ViewQueriesFunction<T>|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes host bindings on the associated directive.
|
* Refreshes host bindings on the associated directive.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -63,8 +63,6 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
||||||
preserveWhitespaces: metadata.preserveWhitespaces || false,
|
preserveWhitespaces: metadata.preserveWhitespaces || false,
|
||||||
styles: metadata.styles || EMPTY_ARRAY,
|
styles: metadata.styles || EMPTY_ARRAY,
|
||||||
animations: metadata.animations,
|
animations: metadata.animations,
|
||||||
viewQueries:
|
|
||||||
extractQueriesMetadata(type, getReflect().ownPropMetadata(type), isViewQuery),
|
|
||||||
directives: [],
|
directives: [],
|
||||||
changeDetection: metadata.changeDetection,
|
changeDetection: metadata.changeDetection,
|
||||||
pipes: new Map(),
|
pipes: new Map(),
|
||||||
|
@ -157,6 +155,7 @@ export function directiveMetadata(type: Type<any>, metadata: Directive): R3Direc
|
||||||
usesInheritance: !extendsDirectlyFromObject(type),
|
usesInheritance: !extendsDirectlyFromObject(type),
|
||||||
exportAs: extractExportAs(metadata.exportAs),
|
exportAs: extractExportAs(metadata.exportAs),
|
||||||
providers: metadata.providers || null,
|
providers: metadata.providers || null,
|
||||||
|
viewQueries: extractQueriesMetadata(type, propMetadata, isViewQuery),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ describe('query logic', () => {
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComp, QueryComp, SimpleCompA, SimpleCompB, StaticViewQueryComp, TextDirective,
|
AppComp, QueryComp, SimpleCompA, SimpleCompB, StaticViewQueryComp, TextDirective,
|
||||||
SubclassStaticViewQueryComp, StaticContentQueryComp, SubclassStaticContentQueryComp,
|
SubclassStaticViewQueryComp, StaticContentQueryComp, SubclassStaticContentQueryComp,
|
||||||
QueryCompWithChanges, StaticContentQueryDir
|
QueryCompWithChanges, StaticContentQueryDir, SuperDirectiveQueryTarget, SuperDirective,
|
||||||
|
SubComponent
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -161,6 +162,17 @@ describe('query logic', () => {
|
||||||
expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
|
expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow for view queries to be inherited from a directive', () => {
|
||||||
|
const fixture = TestBed.createComponent(SubComponent);
|
||||||
|
const comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(comp.headers).toBeTruthy();
|
||||||
|
expect(comp.headers.length).toBe(2);
|
||||||
|
expect(comp.headers.toArray().every(result => result instanceof SuperDirectiveQueryTarget))
|
||||||
|
.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('content queries', () => {
|
describe('content queries', () => {
|
||||||
|
@ -484,7 +496,7 @@ class StaticViewQueryComp {
|
||||||
template: `
|
template: `
|
||||||
<div [text]="text"></div>
|
<div [text]="text"></div>
|
||||||
<span #foo></span>
|
<span #foo></span>
|
||||||
|
|
||||||
<div #bar></div>
|
<div #bar></div>
|
||||||
<span #baz></span>
|
<span #baz></span>
|
||||||
`
|
`
|
||||||
|
@ -564,3 +576,21 @@ export class QueryCompWithChanges {
|
||||||
|
|
||||||
showing = false;
|
showing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'query-target', template: '<ng-content></ng-content>'})
|
||||||
|
class SuperDirectiveQueryTarget {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[super-directive]'})
|
||||||
|
class SuperDirective {
|
||||||
|
@ViewChildren(SuperDirectiveQueryTarget) headers !: QueryList<SuperDirectiveQueryTarget>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<query-target>One</query-target>
|
||||||
|
<query-target>Two</query-target>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class SubComponent extends SuperDirective {
|
||||||
|
}
|
||||||
|
|
|
@ -349,16 +349,6 @@ ivyEnabled && describe('render3 jit', () => {
|
||||||
expect((TestDirective as any).ngDirectiveDef.contentQueries).not.toBeNull();
|
expect((TestDirective as any).ngDirectiveDef.contentQueries).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not pick up view queries from directives', () => {
|
|
||||||
@Directive({selector: '[test]'})
|
|
||||||
class TestDirective {
|
|
||||||
@ViewChildren('foo') foos: QueryList<ElementRef>|undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect((TestDirective as any).ngDirectiveDef.contentQueries).toBeNull();
|
|
||||||
expect((TestDirective as any).ngDirectiveDef.viewQuery).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should compile ViewChild query on a component', () => {
|
it('should compile ViewChild query on a component', () => {
|
||||||
@Component({selector: 'test', template: ''})
|
@Component({selector: 'test', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
|
|
Loading…
Reference in New Issue