build(forms): expose signal forms compat package

Hooks up @angular/forms/signals/compat to be released and have its docs
published

(cherry picked from commit fc1ef79ad4)
This commit is contained in:
Miles Malerba 2025-11-13 20:25:25 -08:00 committed by Jessica Janiuk
parent 911999e75b
commit 966bfae337
17 changed files with 179 additions and 48 deletions

View file

@ -68,6 +68,7 @@ copy_to_directory(
"//packages/elements:elements_docs",
"//packages/forms:forms_docs",
"//packages/forms/signals:forms_signals_docs",
"//packages/forms/signals/compat:forms_signals_compat_docs",
"//packages/localize:localize_docs",
"//packages/localize/src/localize:localize_init_docs",
"//packages/platform-browser:platform-browser_docs",

View file

@ -0,0 +1,57 @@
## API Report File for "@angular/forms_signals_compat"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AbstractControl } from '@angular/forms';
import * as _angular_forms from '@angular/forms';
import { ControlValueAccessor } from '@angular/forms';
import { DestroyableInjector } from '@angular/core';
import { FormControlStatus } from '@angular/forms';
import * as i0 from '@angular/core';
import { Injector } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Signal } from '@angular/core';
import { ValidationErrors } from '@angular/forms';
import { ValidatorFn } from '@angular/forms';
import { WritableSignal } from '@angular/core';
import { ɵCONTROL } from '@angular/core';
import { ɵControl } from '@angular/core';
import { ɵFieldState } from '@angular/core';
import { ɵInteropControl } from '@angular/core';
// @public
export function compatForm<TModel>(model: WritableSignal<TModel>): FieldTree<TModel>;
// @public
export function compatForm<TModel>(model: WritableSignal<TModel>, schemaOrOptions: SchemaOrSchemaFn<TModel> | CompatFormOptions): FieldTree<TModel>;
// @public
export function compatForm<TModel>(model: WritableSignal<TModel>, schema: SchemaOrSchemaFn<TModel>, options: CompatFormOptions): FieldTree<TModel>;
// @public
export type CompatFormOptions = Omit<FormOptions, 'adapter'>;
// @public
export class CompatValidationError<T = unknown> implements ValidationError {
constructor({ context, kind, control }: {
context: T;
kind: string;
control: AbstractControl;
});
// (undocumented)
readonly context: T;
// (undocumented)
readonly control: AbstractControl;
// (undocumented)
readonly field: FieldTree<unknown>;
// (undocumented)
readonly kind: string;
// (undocumented)
readonly message?: string;
}
// (No @packageDocumentation comment for this package)
```

View file

@ -108,6 +108,7 @@ filegroup(
"//packages/elements:elements_docs_extraction",
"//packages/forms:forms_docs_extraction",
"//packages/forms/signals:forms_signals_docs_extraction",
"//packages/forms/signals/compat:forms_signals_compat_docs_extraction",
"//packages/localize:localize_docs_extraction",
"//packages/localize/src/localize:localize_init_docs_extraction",
"//packages/platform-browser:platform-browser_docs_extraction",

View file

@ -36,6 +36,7 @@ ng_package(
deps = [
"//packages/forms",
"//packages/forms/signals",
"//packages/forms/signals/compat",
],
)

View file

@ -1,4 +1,4 @@
load("//tools:defaults.bzl", "ng_project")
load("//tools:defaults.bzl", "generate_api_docs", "ng_project")
package(default_visibility = ["//visibility:public"])
@ -18,3 +18,22 @@ ng_project(
"//packages/forms/signals",
],
)
filegroup(
name = "files_for_docgen",
srcs = glob([
"*.ts",
"src/**/*.ts",
]) + ["PACKAGE.md"],
)
generate_api_docs(
name = "forms_signals_compat_docs",
srcs = [
":files_for_docgen",
"//packages:common_files_and_deps_for_docs",
"//packages/common:files_for_docgen",
],
entry_point = ":index.ts",
module_name = "@angular/forms/signals/compat",
)

View file

@ -0,0 +1,4 @@
# APIs for Signal Forms interop with Reactive Forms
This package contains APIs to enable interoperability between signal-based forms and reactive forms.
It allows developers to incrementally migrate their existing forms implementations to signal-based forms.

View file

@ -0,0 +1,14 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
// This file is not used to build this module. It is only used during editing
// by the TypeScript language service and during build for verification. `ngc`
// replaces this file with production index.ts when it rewrites private symbol
// names.
export * from './public_api';

View file

@ -0,0 +1,15 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
/**
* @module
* @description
* Entry point for all public APIs of this package.
*/
export * from './src/api/compat_form';
export * from './src/api/compat_validation_error';

View file

@ -6,12 +6,18 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {FieldTree, PathKind, SchemaOrSchemaFn} from '../../src/api/types';
import {form, FormOptions} from '../../public_api';
import {CompatFieldAdapter} from './compat_field_adapter';
import {normalizeFormArgs} from '../../src/util/normalize_form_args';
import {WritableSignal} from '@angular/core';
import {form, FormOptions} from '../../../public_api';
import {FieldTree, PathKind, SchemaOrSchemaFn} from '../../../src/api/types';
import {normalizeFormArgs} from '../../../src/util/normalize_form_args';
import {CompatFieldAdapter} from '../compat_field_adapter';
/**
* Options that may be specified when creating a compat form.
*
* @category interop
* @experimental 21.0.0
*/
export type CompatFormOptions = Omit<FormOptions, 'adapter'>;
/**

View file

@ -0,0 +1,31 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {AbstractControl} from '@angular/forms';
import {FieldTree} from '../../../src/api/types';
import {ValidationError} from '../../../src/api/validation_errors';
/**
* An error used for compat errors.
*
* @experimental 21.0.0
* @category interop
*/
export class CompatValidationError<T = unknown> implements ValidationError {
readonly kind: string = 'compat';
readonly control: AbstractControl;
readonly field!: FieldTree<unknown>;
readonly context: T;
readonly message?: string;
constructor({context, kind, control}: {context: T; kind: string; control: AbstractControl}) {
this.context = context;
this.kind = kind;
this.control = control;
}
}

View file

@ -6,23 +6,23 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {computed, Signal, WritableSignal} from '@angular/core';
import {AbstractControl} from '@angular/forms';
import {BasicFieldAdapter, FieldAdapter} from '../../src/field/field_adapter';
import {FormFieldManager} from '../../src/field/manager';
import {FieldNode} from '../../src/field/node';
import {FieldNodeState} from '../../src/field/state';
import {
ChildFieldNodeOptions,
FieldNodeOptions,
FieldNodeStructure,
} from '../../src/field/structure';
import {computed, Signal, WritableSignal} from '@angular/core';
import {CompatFieldNode} from './compat_field_node';
import {FieldNode} from '../../src/field/node';
import {CompatValidationState} from './compat_validation_state';
import {ValidationState} from '../../src/field/validation';
import {CompatChildFieldNodeOptions, CompatStructure} from './compat_structure';
import {CompatNodeState} from './compat_node_state';
import {FieldNodeState} from '../../src/field/state';
import {FieldAdapter, BasicFieldAdapter} from '../../src/field/field_adapter';
import {FieldPathNode} from '../../src/schema/path_node';
import {FormFieldManager} from '../../src/field/manager';
import {CompatFieldNode} from './compat_field_node';
import {CompatNodeState} from './compat_node_state';
import {CompatChildFieldNodeOptions, CompatStructure} from './compat_structure';
import {CompatValidationState} from './compat_validation_state';
/**
* This is a tree-shakable Field adapter that can create a compat node

View file

@ -7,13 +7,13 @@
*/
import {computed, linkedSignal, runInInjectionContext, Signal, untracked} from '@angular/core';
import {toSignal} from '@angular/core/rxjs-interop';
import {AbstractControl} from '@angular/forms';
import {Observable, ReplaySubject} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';
import {FieldNode} from '../../src/field/node';
import {getInjectorFromOptions} from '../../src/field/util';
import {toSignal} from '@angular/core/rxjs-interop';
import {map, takeUntil} from 'rxjs/operators';
import type {CompatFieldNodeOptions} from './compat_structure';
import {Observable, ReplaySubject} from 'rxjs';
/**
* Field node with additional control property.

View file

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {FieldNodeState} from '../../src/field/state';
import {computed, Signal} from '@angular/core';
import {AbstractControl} from '@angular/forms';
import {FieldNodeState} from '../../src/field/state';
import {CompatFieldNode, getControlEventsSignal, getControlStatusSignal} from './compat_field_node';
import {CompatFieldNodeOptions} from './compat_structure';

View file

@ -7,8 +7,7 @@
*/
import {AbstractControl, FormArray, FormGroup, ValidationErrors} from '@angular/forms';
import {ValidationError} from '../../src/api/validation_errors';
import {FieldTree} from '../../src/api/types';
import {CompatValidationError} from './api/compat_validation_error';
/**
* Converts reactive form validation error to signal forms CompatValidationError.
@ -44,21 +43,3 @@ export function extractNestedReactiveErrors(control: AbstractControl): CompatVal
return errors;
}
/**
* An error used for compat errors.
*/
export class CompatValidationError<T = unknown> implements ValidationError {
readonly kind: string = 'compat';
readonly control: AbstractControl;
readonly field!: FieldTree<unknown>;
context: T;
constructor({context, kind, control}: {context: T; kind: string; control: AbstractControl}) {
this.context = context;
this.kind = kind;
this.control = control;
}
message?: string | undefined;
}

View file

@ -6,13 +6,14 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {computed, signal, Signal} from '@angular/core';
import {computed, Signal} from '@angular/core';
import {AbstractControl} from '@angular/forms';
import {ValidationError} from '../../src/api/validation_errors';
import {calculateValidationSelfStatus, ValidationState} from '../../src/field/validation';
import type {CompatValidationError} from './api/compat_validation_error';
import {getControlStatusSignal} from './compat_field_node';
import {CompatFieldNodeOptions} from './compat_structure';
import {calculateValidationSelfStatus, ValidationState} from '../../src/field/validation';
import {CompatValidationError, extractNestedReactiveErrors} from './compat_validation_error';
import {ValidationError} from '../../src/api/validation_errors';
import {extractNestedReactiveErrors} from './compat_validation_error';
// Readonly signal containing an empty array, used for optimization.
const EMPTY_ARRAY_SIGNAL = computed(() => []);

View file

@ -7,6 +7,10 @@
*/
import {ApplicationRef, Injector, signal} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {compatForm} from '../../../compat/src/api/compat_form';
import {CompatValidationError} from '../../../compat/src/api/compat_validation_error';
import {
customError,
disabled,
@ -23,10 +27,6 @@ import {
validate,
validateTree,
} from '../../../public_api';
import {TestBed} from '@angular/core/testing';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {CompatValidationError} from '../../../compat/src/compat_validation_error';
import {compatForm} from '../../../compat/src/compat_form';
function promiseWithResolvers<T>(): {
promise: Promise<T>;

View file

@ -7,10 +7,10 @@
*/
import {FormArray, FormControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms';
import {CompatValidationError} from '../../../compat/src/api/compat_validation_error';
import {
reactiveErrorsToSignalErrors,
CompatValidationError,
extractNestedReactiveErrors,
reactiveErrorsToSignalErrors,
} from '../../../compat/src/compat_validation_error';
describe('destroy$', () => {