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:

committed by
Matias Niemelä

parent
a5a35ff54a
commit
0ffa2f2e73
@ -145,7 +145,7 @@ export class ComponentDecoratorHandler implements
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
// precede inline styles, and styles defined in the template override styles defined in the
|
||||
// component.
|
||||
@ -288,7 +272,6 @@ export class ComponentDecoratorHandler implements
|
||||
meta: {
|
||||
...metadata,
|
||||
template,
|
||||
viewQueries,
|
||||
encapsulation,
|
||||
interpolation: template.interpolation,
|
||||
styles: styles || [],
|
||||
|
@ -160,10 +160,20 @@ export function extractDirectiveMetadata(
|
||||
|
||||
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')) {
|
||||
const queriesFromDecorator =
|
||||
extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore);
|
||||
queries.push(...queriesFromDecorator.content);
|
||||
viewQueries.push(...queriesFromDecorator.view);
|
||||
}
|
||||
|
||||
// Parse the selector.
|
||||
@ -213,7 +223,7 @@ export function extractDirectiveMetadata(
|
||||
usesOnChanges,
|
||||
},
|
||||
inputs: {...inputsFromMeta, ...inputsFromFields},
|
||||
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
|
||||
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector,
|
||||
type: new WrappedNodeExpr(clazz.name !),
|
||||
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
|
||||
typeSourceSpan: null !, usesInheritance, exportAs, providers
|
||||
|
@ -983,7 +983,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
env.tsconfig({strictInjectionParameters: true});
|
||||
env.write('test.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class Test {
|
||||
constructor(private notInjectable: string) {}
|
||||
@ -1000,7 +1000,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
env.tsconfig({strictInjectionParameters: true});
|
||||
env.write('test.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class Test {
|
||||
constructor(private notInjectable: string) {}
|
||||
@ -1017,7 +1017,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
env.tsconfig({strictInjectionParameters: true});
|
||||
env.write('test.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
useValue: '42',
|
||||
@ -1358,6 +1358,41 @@ describe('ngtsc behavioral tests', () => {
|
||||
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', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
@ -1931,7 +1966,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
it('should be able to compile an app using the factory shim', () => {
|
||||
env.tsconfig({'allowEmptyCodegenFiles': true});
|
||||
|
||||
env.write('test.ts', `
|
||||
env.write('test.ts', `
|
||||
export {MyModuleNgFactory} from './my-module.ngfactory';
|
||||
`);
|
||||
|
||||
@ -2217,7 +2252,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
import {Component} from '@angular/core';
|
||||
import {Other} from './types';
|
||||
import Default from './types';
|
||||
|
||||
|
||||
@Component({selector: 'test', template: 'test'})
|
||||
export class SomeCmp {
|
||||
constructor(arg: Default, other: Other) {}
|
||||
|
Reference in New Issue
Block a user