From 62b87b4551d77815f58af152d1921de3733621ba Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 8 Feb 2024 21:50:19 +0100 Subject: [PATCH] fix(core): do not crash for signal query that does not have any matches (#54353) The newly introduced signal queries would error if no match exists, due to an invalid read within the query internals. This commit addresses the crash by allowing there to be no matches. PR Close #54353 --- packages/core/src/render3/query.ts | 6 +-- .../authoring/signal_queries_spec.ts | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index e05cb8b016f2b..7b44a51428fac 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -354,9 +354,9 @@ function materializeViewResults( const lQuery = lView[QUERIES]!.queries![queryIndex]; if (lQuery.matches === null) { const tViewData = tView.data; - const tQueryMatches = tQuery.matches!; - const result: T|null[] = []; - for (let i = 0; i < tQueryMatches.length; i += 2) { + const tQueryMatches = tQuery.matches; + const result: Array = []; + for (let i = 0; tQueryMatches !== null && i < tQueryMatches.length; i += 2) { const matchedNodeIdx = tQueryMatches[i]; if (matchedNodeIdx < 0) { // we at the marker which might have results in views created based on this diff --git a/packages/core/test/acceptance/authoring/signal_queries_spec.ts b/packages/core/test/acceptance/authoring/signal_queries_spec.ts index 7163a9633c5f3..1021b75286288 100644 --- a/packages/core/test/acceptance/authoring/signal_queries_spec.ts +++ b/packages/core/test/acceptance/authoring/signal_queries_spec.ts @@ -183,6 +183,23 @@ describe('queries as signals', () => { expect(result2.length).toBe(1); expect(result2).toBe(result1); }); + + it('should be empty when no query matches exist', () => { + @Component({ + standalone: true, + template: ``, + }) + class AppComponent { + result = viewChild('unknown'); + results = viewChildren('unknown'); + } + + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + + expect(fixture.componentInstance.result()).toBeUndefined(); + expect(fixture.componentInstance.results().length).toBe(0); + }); }); describe('content queries', () => { @@ -319,5 +336,32 @@ describe('queries as signals', () => { expect(queryDir.results().length).toBe(2); }); + + it('should be empty when no query matches exist', () => { + @Directive({ + selector: '[declare]', + standalone: true, + }) + class DeclareQuery { + result = contentChild('unknown'); + results = contentChildren('unknown'); + } + + @Component({ + standalone: true, + imports: [DeclareQuery], + template: `
`, + }) + class AppComponent { + } + + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const queryDir = + fixture.debugElement.query(By.directive(DeclareQuery)).injector.get(DeclareQuery); + + expect(queryDir.result()).toBeUndefined(); + expect(queryDir.results().length).toBe(0); + }); }); });