mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(core): fix ordering of view queries metadata in JIT mode
AOT was generating an array that was ordered as signal queries first, then the decorator queries.
Aligning JIT with AOT fixes the issue illustrated by the test.
fixes #68404
(cherry picked from commit 8c11816490)
This commit is contained in:
parent
9ed1b6c045
commit
a40e2cebc8
2 changed files with 51 additions and 4 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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: `
|
||||
<div #templateA>Content A</div>
|
||||
<div #templateB>Content B</div>
|
||||
<div #templateC>Content C</div>
|
||||
`,
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(DivDirective) divs!: QueryList<ElementRef<HTMLDivElement>>;
|
||||
@ViewChild('templateA') elRefA!: ElementRef<HTMLDivElement>;
|
||||
readonly elRefB = viewChild<ElementRef<HTMLDivElement>>('templateB');
|
||||
@ViewChild('templateC') elRefC!: ElementRef<HTMLDivElement>;
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue