feat(core): expose queries as signals (#54283)

This commit exposes authoring functions for queries as signals
thus making those generally available.

PR Close #54283
This commit is contained in:
Pawel Kozlowski 2024-02-04 13:00:21 -08:00 committed by Jessica Janiuk
parent 0d4e983c24
commit e95ef2cbc6
20 changed files with 86 additions and 79 deletions

View file

@ -20,11 +20,6 @@
"packages/core/src/change_detection/change_detector_ref.ts",
"packages/core/src/render3/view_ref.ts"
],
[
"packages/core/src/change_detection/change_detector_ref.ts",
"packages/core/src/render3/view_ref.ts",
"packages/core/src/linker/view_ref.ts"
],
[
"packages/core/src/change_detection/differs/default_iterable_differ.ts",
"packages/core/src/change_detection/differs/iterable_differs.ts"

View file

@ -322,6 +322,9 @@ export type ContentChild = Query;
// @public
export const ContentChild: ContentChildDecorator;
// @public
export const contentChild: ContentChildFunction;
// @public
export interface ContentChildDecorator {
(selector: ProviderToken<unknown> | Function | string, opts?: {
@ -337,12 +340,44 @@ export interface ContentChildDecorator {
}): ContentChild;
}
// @public
export interface ContentChildFunction {
<LocatorT>(locator: ProviderToken<LocatorT> | string, opts?: {
descendants?: boolean;
}): Signal<LocatorT | undefined>;
// (undocumented)
<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
descendants?: boolean;
read: ProviderToken<ReadT>;
}): Signal<ReadT | undefined>;
required: {
<LocatorT>(locator: ProviderToken<LocatorT> | string, opts?: {
descendants?: boolean;
}): Signal<LocatorT>;
<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
descendants?: boolean;
read: ProviderToken<ReadT>;
}): Signal<ReadT>;
};
}
// @public
export type ContentChildren = Query;
// @public
export const ContentChildren: ContentChildrenDecorator;
// @public (undocumented)
export function contentChildren<LocatorT>(locator: ProviderToken<LocatorT> | string, opts?: {
descendants?: boolean;
}): Signal<ReadonlyArray<LocatorT>>;
// @public (undocumented)
export function contentChildren<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
descendants?: boolean;
read: ProviderToken<ReadT>;
}): Signal<ReadonlyArray<ReadT>>;
// @public
export interface ContentChildrenDecorator {
(selector: ProviderToken<unknown> | Function | string, opts?: {
@ -1598,6 +1633,9 @@ export type ViewChild = Query;
// @public
export const ViewChild: ViewChildDecorator;
// @public
export const viewChild: ViewChildFunction;
// @public
export interface ViewChildDecorator {
(selector: ProviderToken<unknown> | Function | string, opts?: {
@ -1611,12 +1649,35 @@ export interface ViewChildDecorator {
}): ViewChild;
}
// @public
export interface ViewChildFunction {
<LocatorT>(locator: ProviderToken<LocatorT> | string): Signal<LocatorT | undefined>;
// (undocumented)
<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
read: ProviderToken<ReadT>;
}): Signal<ReadT | undefined>;
required: {
<LocatorT>(locator: ProviderToken<LocatorT> | string): Signal<LocatorT>;
<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
read: ProviderToken<ReadT>;
}): Signal<ReadT>;
};
}
// @public
export type ViewChildren = Query;
// @public
export const ViewChildren: ViewChildrenDecorator;
// @public (undocumented)
export function viewChildren<LocatorT>(locator: ProviderToken<LocatorT> | string): Signal<ReadonlyArray<LocatorT>>;
// @public (undocumented)
export function viewChildren<LocatorT, ReadT>(locator: ProviderToken<LocatorT> | string, opts: {
read: ProviderToken<ReadT>;
}): Signal<ReadonlyArray<ReadT>>;
// @public
export interface ViewChildrenDecorator {
(selector: ProviderToken<unknown> | Function | string, opts?: {

View file

@ -13,3 +13,4 @@ export {InputFunction} from './authoring/input/input';
export {InputOptions, InputOptionsWithoutTransform, InputOptionsWithTransform, InputSignal, InputSignalWithTransform, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE} from './authoring/input/input_signal';
export {ɵUnwrapDirectiveSignalInputs} from './authoring/input/input_type_checking';
export {output as ɵoutput, OutputEmitter as ɵOutputEmitter, OutputOptions as ɵOutputOptions} from './authoring/output';
export {ContentChildFunction, ViewChildFunction} from './authoring/queries';

View file

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ProviderToken} from '../di';
import {ProviderToken} from '../di/provider_token';
import {createMultiResultQuerySignalFn, createSingleResultOptionalQuerySignalFn, createSingleResultRequiredQuerySignalFn} from '../render3/query_reactive';
import {Signal} from '../render3/reactivity/api';
@ -34,18 +34,6 @@ export interface ViewChildFunction {
* Initializes a view child query. Consider using `viewChild.required` for queries that should
* always match.
*
* @usageNotes
* Create a child query in your component by declaring a
* class field and initializing it with the `viewChild()` function.
*
* ```ts
* @Component({template: '<div #el></div><my-component #cmp />'})
* export class TestComponent {
* divEl = viewChild<ElementRef>('el'); // Signal<ElementRef|undefined>
* cmp = viewChild(MyComponent); // Signal<MyComponent|undefined>
* }
* ```
*
* @developerPreview
*/
<LocatorT>(locator: ProviderToken<LocatorT>|string): Signal<LocatorT|undefined>;
@ -55,18 +43,6 @@ export interface ViewChildFunction {
/**
* Initializes a view child query that is expected to always match an element.
*
* @usageNotes
* Create a required child query in your component by declaring a
* class field and initializing it with the `viewChild()` function.
*
* ```ts
* @Component({template: '<div #el></div><my-component #cmp />'})
* export class TestComponent {
* divElRequired = viewChild.required<ElementRef>('el'); // Signal<ElementRef>
* cmpRequired = viewChild.required(MyComponent); // Signal<MyComponent>
* }
* ```
*
* @developerPreview
*/
required: {
@ -160,18 +136,6 @@ export interface ContentChildFunction {
* Initializes a content child query.
*
* Consider using `contentChild.required` for queries that should always match.
*
* @usageNotes
* Create a child query in your component by declaring a
* class field and initializing it with the `contentChild()` function.
*
* ```ts
* @Component({...})
* export class TestComponent {
* headerEl = contentChild<ElementRef>('h'); // Signal<ElementRef|undefined>
* header = contentChild(MyHeader); // Signal<MyHeader|undefined>
* }
* ```
* @developerPreview
*/
<LocatorT>(locator: ProviderToken<LocatorT>|string, opts?: {descendants?: boolean}):
@ -183,17 +147,6 @@ export interface ContentChildFunction {
/**
* Initializes a content child query that is always expected to match.
*
* @usageNotes
* Create a child query in your component by declaring a
* class field and initializing it with the `contentChild()` function.
*
* ```ts
* @Component({...})
* export class TestComponent {
* headerElElRequired = contentChild.required<ElementRef>('h'); // Signal<ElementRef>
* headerRequired = contentChild.required(MyHeader); // Signal<MyHeader>
* }
* ```
* @developerPreview
*/
required: {

View file

@ -13,10 +13,11 @@
*/
export * from './authoring';
// Input is exported separately as this file is exempted from JSCompiler's
// conformance requirement for inferred const exports.
// See: https://docs.google.com/document/d/1RXb1wYwsbJotO1KBgSDsAtKpduGmIHod9ADxuXcAvV4/edit?tab=t.0
// Input and query authoring functions are exported separately as this file is exempted from
// JSCompiler's conformance requirement for inferred const exports. See:
// https://docs.google.com/document/d/1RXb1wYwsbJotO1KBgSDsAtKpduGmIHod9ADxuXcAvV4/edit?tab=t.0
export {input} from './authoring/input/input';
export {contentChild, contentChildren, viewChild, viewChildren} from './authoring/queries';
export * from './metadata';
export * from './version';

View file

@ -7,7 +7,7 @@
*/
import {ProviderToken} from '../../di';
import {ProviderToken} from '../../di/provider_token';
import {QueryFlags} from '../interfaces/query';
import {createContentQuery, createViewQuery} from '../query';
import {bindQueryToSignal} from '../query_reactive';

View file

@ -6,17 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
// TODO: update imports
import {Component, computed} from '@angular/core';
import {viewChild} from '@angular/core/src/authoring/queries';
import {getComponentDef} from '@angular/core/src/render3/definition';
import {Component, computed, viewChild} from '@angular/core';
import {TestBed} from '@angular/core/testing';
describe('queries as signals', () => {
describe('view', () => {
// TODO: Enable when `viewChild` is exposed publicly. Right now, compiler will
// not detect `viewChild` as it does not originate from a `@angular/core` import.
xit('view child', () => {
it('view child', () => {
@Component({
selector: 'test-cmp',
standalone: true,

View file

@ -7,9 +7,7 @@
*/
import {Component, computed, ContentChild, ContentChildren, Directive, ElementRef, ViewChild, ViewChildren} from '@angular/core';
// TODO: update imports to the exported authoring functions when those are public
import {contentChild, contentChildren, viewChild, viewChildren} from '@angular/core/src/authoring/queries';
import {Component, computed, ContentChild, contentChild, ContentChildren, contentChildren, Directive, ElementRef, ViewChild, viewChild, ViewChildren, viewChildren} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';

View file

@ -240,7 +240,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -264,7 +264,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -168,7 +168,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -198,7 +198,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"
@ -1709,6 +1709,9 @@
{
"name": "init_queries"
},
{
"name": "init_queries2"
},
{
"name": "init_queries_signals"
},

View file

@ -246,7 +246,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -231,7 +231,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -111,7 +111,7 @@
"name": "HelloWorldModule"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -195,7 +195,7 @@
"name": "HydrationFeatureKind"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -12,7 +12,7 @@
"name": "EnvironmentInjector"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -255,7 +255,7 @@
"name": "INITIAL_VALUE"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -150,7 +150,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"

View file

@ -171,7 +171,7 @@
"name": "GenericBrowserDomAdapter"
},
{
"name": "INJECTOR2"
"name": "INJECTOR"
},
{
"name": "INJECTOR_DEF_TYPES"