refactor(core): add error code for required query results (#54103)

This commit introduces a dedicated error code for queries that require
results but none are available.

PR Close #54103
This commit is contained in:
Pawel Kozlowski 2024-02-04 12:00:06 -08:00 committed by Jessica Janiuk
parent 20008a6b56
commit 3a2ce9e0a2
6 changed files with 56 additions and 6 deletions

View file

@ -0,0 +1,23 @@
@name Child query result is required but no value is available.
@category runtime
@shortDescription Required child query result was accessed before query results were calculated or query has no matches.
@description
Required child query (`contentChild.required` or `viewChild.required`) result was accessed before query results were calculated or query has no matches.
This can happen in two distinct situations:
* query results were accessed before a given query could collect results;
* a query was executed but didn't match any nodes and has no results as a consequence.
Content queries and view queries each calculate their results at different points in time:
* `contentChild` results are available after a _host_ view (template where a directive declaring a query is used) is created;
* `viewChild` results are available after a template of a component declaring a query is created.
Accessing query results before they're available results in the error described on this page. Most notably, query results are _never_ available in a constructor of the component or directive declaring a query.
## Fixing the error
`contentChild` query results can be accessed in the `AfterContentChecked` lifecycle hook, or later.
`viewChild` query results can be accessed in the `AfterViewChecked` lifecycle hook, or later.
Make sure that a required query matches at least one node and has results at all. You can verify this by accessing query results in the lifecycle hooks listed above.

View file

@ -0,0 +1,23 @@
@name Child query result is required but no value is available.
@category runtime
@shortDescription Required child query result was accessed before query results were calculated or query has no matches.
@description
Required child query (`contentChild.required` or `viewChild.required`) result was accessed before query results were calculated or query has no matches.
This can happen in two distinct situations:
* query results were accessed before a given query could collect results;
* a query was executed but didn't match any nodes and has no results as a consequence.
Content queries and view queries each calculate their results at different points in time:
* `contentChild` results are available after a _host_ view (template where a directive declaring a query is used) is created;
* `viewChild` results are available after a template of a component declaring a query is created.
Accessing query results before they're available results in the error described on this page. Most notably, query results are _never_ available in a constructor of the component or directive declaring a query.
## Fixing the error
`contentChild` query results can be accessed in the `AfterContentChecked` lifecycle hook, or later.
`viewChild` query results can be accessed in the `AfterViewChecked` lifecycle hook, or later.
Make sure that a required query matches at least one node and has results at all. You can verify this by accessing query results in the lifecycle hooks listed above.

View file

@ -123,6 +123,8 @@ export const enum RuntimeErrorCode {
// (undocumented)
REQUIRED_INPUT_NO_VALUE = -950,
// (undocumented)
REQUIRED_QUERY_NO_VALUE = -951,
// (undocumented)
RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 1000,
// (undocumented)
RUNTIME_DEPS_ORPHAN_COMPONENT = 1001,

View file

@ -116,8 +116,9 @@ export const enum RuntimeErrorCode {
COMPONENT_ID_COLLISION = -912,
IMAGE_PERFORMANCE_WARNING = -913,
// Signal inputs
// Signal integration errors
REQUIRED_INPUT_NO_VALUE = -950,
REQUIRED_QUERY_NO_VALUE = -951,
// Runtime dependency tracker errors
RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 1000,

View file

@ -8,7 +8,7 @@
import {ComputedNode, createComputed, SIGNAL} from '@angular/core/primitives/signals';
import {RuntimeError} from '../errors';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {unwrapElementRef} from '../linker/element_ref';
import {QueryList} from '../linker/query_list';
import {EMPTY_ARRAY} from '../util/empty';
@ -48,9 +48,10 @@ function createQuerySignalFn<V>(firstOnly: boolean, required: boolean) {
const value = refreshSignalQuery<V>(node, firstOnly);
if (value === undefined && required) {
// TODO: add error code add proper message
throw new RuntimeError(0, 'no query results yet!');
if (required && value === undefined) {
throw new RuntimeError(
RuntimeErrorCode.REQUIRED_QUERY_NO_VALUE,
ngDevMode && 'Child query result is required but no value is available.');
}
return value;

View file

@ -66,7 +66,7 @@ describe('queries as signals', () => {
const appCmpt = new AppComponent();
expect(() => {
appCmpt.divEl();
}).toThrowError('NG00: no query results yet!');
}).toThrowError(/NG0951: Child query result is required but no value is available/);
});
it('should query for multiple elements in a template', () => {