diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts index 6793ccd3fd6..f6e0669e376 100644 --- a/packages/core/src/render3/jit/directive.ts +++ b/packages/core/src/render3/jit/directive.ts @@ -28,7 +28,7 @@ import {ViewEncapsulation} from '../../metadata/view'; import {flatten} from '../../util/array_utils'; import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty'; import {initNgDevMode} from '../../util/ng_dev_mode'; -import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../def_getters'; +import {getComponentDef, getDirectiveDef, getPipeDef} from '../def_getters'; import {depsTracker} from '../deps_tracker/deps_tracker'; import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF} from '../fields'; import {ComponentDef, ComponentType, DirectiveDefList, PipeDefList} from '../interfaces/definition'; @@ -442,7 +442,8 @@ function extractQueriesMetadata( propMetadata: {[key: string]: any[]}, isQueryAnn: (ann: any) => ann is Query, ): R3QueryMetadataFacade[] { - const queriesMeta: R3QueryMetadataFacade[] = []; + const signalQueriesMeta: R3QueryMetadataFacade[] = []; + const decoratorQueriesMeta: R3QueryMetadataFacade[] = []; for (const field in propMetadata) { if (propMetadata.hasOwnProperty(field)) { const annotations = propMetadata[field]; @@ -457,12 +458,19 @@ function extractQueriesMetadata( if (annotations.some(isInputAnnotation)) { throw new Error(`Cannot combine @Input decorators with query decorators`); } - queriesMeta.push(convertToR3QueryMetadata(field, ann)); + const queryMeta = convertToR3QueryMetadata(field, ann); + if (queryMeta.isSignal) { + signalQueriesMeta.push(queryMeta); + } else { + decoratorQueriesMeta.push(queryMeta); + } } }); } } - return queriesMeta; + + // We match the behavior of AOT here by having signal queries first, then the decorator queries. + return [...signalQueriesMeta, ...decoratorQueriesMeta]; } function extractExportAs(exportAs: string | undefined): string[] | null { diff --git a/packages/core/test/acceptance/authoring/signal_queries_spec.ts b/packages/core/test/acceptance/authoring/signal_queries_spec.ts index 57d3636fede..9b76cdd4774 100644 --- a/packages/core/test/acceptance/authoring/signal_queries_spec.ts +++ b/packages/core/test/acceptance/authoring/signal_queries_spec.ts @@ -16,6 +16,7 @@ import { ElementRef, EnvironmentInjector, QueryList, + ViewChild, viewChild, ViewChildren, viewChildren, @@ -660,4 +661,42 @@ describe('queries as signals', () => { expect(fixture.componentInstance.divElsDecorator.length).toBe(1); }); }); + + it('should resolve static decorator queries when mixed with signal queries', () => { + @Directive({ + selector: 'div', + }) + class DivDirective {} + + @Component({ + imports: [DivDirective], + template: ` +
Content A
+
Content B
+
Content C
+ `, + }) + class App { + @ViewChildren(DivDirective) divs!: QueryList>; + @ViewChild('templateA') elRefA!: ElementRef; + readonly elRefB = viewChild>('templateB'); + @ViewChild('templateC') elRefC!: ElementRef; + } + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const componentInstance = fixture.componentInstance; + + expect(fixture.componentInstance.divs).withContext('divs').toBeDefined(); + expect(componentInstance.divs.length).toBe(3); + + expect(componentInstance.elRefA).withContext('A').toBeDefined(); + expect(componentInstance.elRefA.nativeElement.textContent).toBe('Content A'); + + expect(fixture.componentInstance.elRefB()).withContext('B').toBeDefined(); + expect(componentInstance.elRefB()?.nativeElement.textContent).toBe('Content B'); + + expect(fixture.componentInstance.elRefC).withContext('C').toBeDefined(); + expect(componentInstance.elRefC.nativeElement.textContent).toBe('Content C'); + }); });