@rytrox/form-signals
Version:
Simple Reimplementation of [Angular Forms](https://www.npmjs.com/package/@angular/forms) in Signals. Focused on simplicity, with full [Angular Material](https://www.npmjs.com/package/@angular/material) support.
339 lines (314 loc) • 17.3 kB
TypeScript
import * as i0 from '@angular/core';
import { WritableSignal, Signal, OnDestroy } from '@angular/core';
import { MatButtonToggleGroup, MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatRadioChange } from '@angular/material/radio';
import { MatSelectChange } from '@angular/material/select';
import { MatSliderRangeThumb, MatSliderThumb } from '@angular/material/slider';
import * as i17 from '@angular/common';
type ValidationErrors = Record<string, string>;
type ValidatorFn<V> = (value: V) => (ValidationErrors | null);
/**
* Resolves any factory function inside your Code and returns the Type of the generated Form.
*/
type FormFactoryType<F> = (F extends (val?: any) => infer FG ? FG : never);
/**
* Abstract Interface of a Form.
*
* Every Form is a Signal, that can hold and set values.
* Additionally, there are Signals inside to check states or validators
*/
interface Form<T, E> extends WritableSignal<T> {
/**
* The current value of errors.
* null is considered as VALID!
*/
readonly errors: Signal<E | null>;
/**
* Holds all registered validators of the form
*/
readonly validators: Signal<ValidatorFn<T>[]>;
/**
* Adds some validators to this form.
* You can't add the same validator twice, this will be ignored by its implementation.
*
* After adding, the form will be validated again
*
* @param validators the validators you want to add
*/
addValidators(...validators: ValidatorFn<T>[]): void;
/**
* Removes some validators from this form.
* If you try to remove a validator that doesn't exist in this form, it will be ignored
*
* After removing, the form will be validated again
*
* @param validators the validators you want to remove
*/
removeValidators(...validators: ValidatorFn<T>[]): void;
/**
* Checks if a validator exists inside this form
*
* @param validator the validator you want to check
*/
hasValidator(validator: ValidatorFn<T>): boolean;
}
interface FormArray<F extends Form<any, any>> extends Form<FormArrayValue<F>, FormArrayErrors<FormError<F>>>, Iterable<F> {
/**
* Returns the amount of elements inside this FormArray
*/
readonly length: number;
/**
* Access the control of the index. Returns undefined if the index is out of bounds
*/
[index: number]: F | undefined;
/**
* Creates a new control based on a value and pushes it to this FormArray.
*
* @param value the value of the form array
*/
push(value: FormValue<F>): void;
/**
* Removes the element at a certain index.
* All elements after the index is going to be reordered.
* If the index does not exist, this method will do nothing
*
* @param index the index you want to remove
*/
removeAt(index: number): void;
/**
* Removes the last control of this FormArray and returns it.
*/
pop(): void;
/**
* Disables all included Forms of this FormArray
*/
disable(): void;
/**
* Enables all included Forms of this FormArray
*/
enable(): void;
/**
* Calls a defined callback function on each element of an array, and returns an array that contains the results.
*
* @param callbackFn A function that accepts up to three arguments.
* The map method calls the callbackFn function one time for each element in the array.
*/
map<U>(callbackFn: (control: F, index: number, array: F[]) => U): U[];
/**
* Performs the specified action for each element in an array.
* @param callbackFn A function that accepts up to three arguments.
* forEach calls the callbackFn function one time for each element in the array.
*/
forEach(callbackFn: (control: F, index: number, array: F[]) => void): void;
}
type FormError<F> = F extends Form<any, infer E> ? E : never;
type FormValue<F> = F extends Form<infer T, any> ? T : never;
type FormArrayValue<F> = F extends Form<infer T, any> ? T[] : never;
declare const formArrayFactory: <F extends Form<any, any>>(fn: (val: FormValue<F>) => F) => (val: FormArrayValue<F>) => FormArray<F>;
interface FormArrayErrors<F> {
this: ValidationErrors | null;
controls: FormError<F>[];
}
type FormGroup<T extends {
[K in keyof T]: T[K];
}, F extends TForm<T>> = GroupControls<T, F> & Form<T, FormGroupErrors<T, F>> & {
/**
* Set Control of optional keys in T.
* Controls that are not set, will have undefined keys in their value.
* If your set the control with null, you will remove the control
*
* @param key the key you want to set
* @param control the control you want to add
*/
setControl<K extends OptionalKeys<T>>(key: K, control: F[K] | null): void;
/**
* Disables all included Forms of this FormGroup
*/
disable(): void;
/**
* Enables all included Forms of this FormGroup
*/
enable(): void;
};
declare const formGroupFactory: <T extends { [K in keyof T]: T[K]; }, F extends TForm<T>>(fn: (val?: T) => Required<F>) => (val?: T) => FormGroup<T, F>;
interface FormGroupErrors<T extends {
[K in keyof T]: T[K];
}, F extends TForm<T>> {
this: ValidationErrors | null;
controls: ControlErrors<T, F>;
}
type ControlErrors<T extends {
[K in keyof T]: T[K];
}, F extends TForm<T>> = Partial<{
[K in keyof F]: F[K] extends Form<any, infer E> ? E : never;
}>;
type TForm<T> = {
[K in keyof T]: K extends OptionalKeys<T> ? (Form<Exclude<T[K], undefined>, any> | undefined) : Form<Exclude<T[K], undefined>, any>;
};
type OptionalKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];
type GroupControls<T extends {
[K in keyof T]: T[K];
}, F extends TForm<T>> = {
[K in keyof T]: K extends OptionalKeys<T> ? F[K] | undefined : F[K];
};
interface FormControlOptions<T> {
value: T;
disabled?: boolean;
validators?: ValidatorFn<T>[];
}
interface FormControl<T> extends Form<T, ValidationErrors> {
/**
* Signal that declares if the control is disabled or not
*/
readonly disabled: WritableSignal<boolean>;
}
declare const isFormControl: <T>(val: unknown) => val is FormControl<T>;
declare function formControl<T>(val: T | FormControlOptions<T>): FormControl<T>;
declare class AbstractFormDirective<T> {
protected updateValue(form: FormControl<T>, value: T): void;
}
declare class InputCheckboxDirective extends AbstractFormDirective<boolean> {
readonly form: i0.InputSignal<FormControl<boolean> | undefined>;
private readonly element;
constructor();
onChange(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputCheckboxDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputCheckboxDirective, "input[type=checkbox][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputDateDirective extends AbstractFormDirective<Date | null> {
readonly form: i0.InputSignal<FormControl<Date | null> | undefined>;
private readonly element;
constructor();
protected onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputDateDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputDateDirective, "input[type=date][form], input[type=datetime-local][form], input[type=time][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputFileDirective extends AbstractFormDirective<File | File[] | null> {
readonly multiple: i0.InputSignalWithTransform<boolean, unknown>;
readonly form: i0.InputSignal<FormControl<File | null> | FormControl<File[] | null> | undefined>;
private readonly element;
constructor();
onFileChange(): void;
private isFileArrayForm;
static ɵfac: i0.ɵɵFactoryDeclaration<InputFileDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputFileDirective, "input[multiple][type=file][form], input[type=\"file\"][form]", never, { "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputNumberDirective extends AbstractFormDirective<number> {
readonly form: i0.InputSignal<FormControl<number> | undefined>;
private readonly element;
constructor();
/**
* Blocks incoming input that are not numbers
*
* @param event
*/
onKeyDown(event: KeyboardEvent): void;
onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputNumberDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputNumberDirective, "input[type=number][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputRadioDirective extends AbstractFormDirective<string> {
readonly form: i0.InputSignal<FormControl<string> | undefined>;
private readonly element;
constructor();
onClick(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputRadioDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputRadioDirective, "input[type=radio][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputRangeDirective extends AbstractFormDirective<number> {
readonly form: i0.InputSignal<FormControl<number> | undefined>;
private readonly element;
constructor();
onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputRangeDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputRangeDirective, "input[type=range][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class InputTextDirective extends AbstractFormDirective<string> {
readonly form: i0.InputSignal<FormControl<string> | undefined>;
private readonly element;
constructor();
onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputTextDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<InputTextDirective, "textarea[form], input:not([matDatepicker]):not([matSliderThumb]):not([matSliderStartThumb]):not([matSliderEndThumb]):not([type=number]):not([type=checkbox]):not([type=date]):not([type=datetime-local]):not([type=time]):not([type=radio]):not([type=range]):not([type=file])[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class SelectDirective extends AbstractFormDirective<string> {
readonly form: i0.InputSignal<FormControl<string> | undefined>;
private readonly element;
constructor();
onChange(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<SelectDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<SelectDirective, "select[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatButtonToggleGroupDirective<T> extends AbstractFormDirective<T | T[]> {
readonly form: i0.InputSignal<FormControl<T> | FormControl<T[]> | undefined>;
readonly element: MatButtonToggleGroup;
constructor();
onChange(event: MatButtonToggleChange): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatButtonToggleGroupDirective<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatButtonToggleGroupDirective<any>, "mat-button-toggle-group[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatCheckboxDirective extends AbstractFormDirective<boolean> {
readonly form: i0.InputSignal<FormControl<boolean> | undefined>;
private readonly element;
constructor();
onValueChange(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatCheckboxDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatCheckboxDirective, "mat-checkbox[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatDatepickerDirective<D> extends AbstractFormDirective<D | null> implements OnDestroy {
readonly form: i0.InputSignal<FormControl<D | null> | undefined>;
private readonly datePicker;
private readonly subscription;
constructor();
ngOnDestroy(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatDatepickerDirective<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatDatepickerDirective<any>, "input[matDatepicker][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatRadioGroupDirective<T> extends AbstractFormDirective<T> {
readonly form: i0.InputSignal<FormControl<T> | undefined>;
private readonly element;
constructor();
onValueChange(event: MatRadioChange): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatRadioGroupDirective<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatRadioGroupDirective<any>, "mat-radio-group[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatSelectDirective<T> extends AbstractFormDirective<T | null | T[]> {
readonly form: i0.InputSignal<FormControl<T> | FormControl<T | null> | FormControl<T[]> | undefined>;
private readonly element;
constructor();
onChange(event: MatSelectChange<T | T[]>): void;
private isArrayForm;
static ɵfac: i0.ɵɵFactoryDeclaration<MatSelectDirective<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatSelectDirective<any>, "mat-select[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatSlideToggleDirective extends AbstractFormDirective<boolean> {
readonly form: i0.InputSignal<FormControl<boolean> | undefined>;
private readonly element;
constructor();
onChange(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatSlideToggleDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatSlideToggleDirective, "mat-slide-toggle[form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatSliderRangeThumbDirective extends MatSliderRangeThumb {
readonly form: i0.InputSignal<FormControl<number> | undefined>;
constructor();
onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatSliderRangeThumbDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatSliderRangeThumbDirective, "input[matSliderStartThumb][form], input[matSliderEndThumb][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class MatSliderThumbDirective extends MatSliderThumb {
readonly form: i0.InputSignal<FormControl<number> | undefined>;
constructor();
onInput(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<MatSliderThumbDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<MatSliderThumbDirective, "input[matSliderThumb][form]", never, { "form": { "alias": "form"; "required": false; "isSignal": true; }; }, {}, never, never, false, never>;
}
declare class FormsModule {
static ɵfac: i0.ɵɵFactoryDeclaration<FormsModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<FormsModule, [typeof InputCheckboxDirective, typeof InputDateDirective, typeof InputFileDirective, typeof InputNumberDirective, typeof InputRadioDirective, typeof InputRangeDirective, typeof InputTextDirective, typeof SelectDirective, typeof MatButtonToggleGroupDirective, typeof MatCheckboxDirective, typeof MatDatepickerDirective, typeof MatRadioGroupDirective, typeof MatSelectDirective, typeof MatSlideToggleDirective, typeof MatSliderRangeThumbDirective, typeof MatSliderThumbDirective], [typeof i17.CommonModule], [typeof InputCheckboxDirective, typeof InputDateDirective, typeof InputFileDirective, typeof InputNumberDirective, typeof InputRadioDirective, typeof InputRangeDirective, typeof InputTextDirective, typeof SelectDirective, typeof MatButtonToggleGroupDirective, typeof MatCheckboxDirective, typeof MatDatepickerDirective, typeof MatRadioGroupDirective, typeof MatSelectDirective, typeof MatSlideToggleDirective, typeof MatSliderRangeThumbDirective, typeof MatSliderThumbDirective]>;
static ɵinj: i0.ɵɵInjectorDeclaration<FormsModule>;
}
export { AbstractFormDirective, FormsModule, InputCheckboxDirective, InputDateDirective, InputFileDirective, InputNumberDirective, InputRadioDirective, InputRangeDirective, InputTextDirective, MatButtonToggleGroupDirective, MatCheckboxDirective, MatDatepickerDirective, MatRadioGroupDirective, MatSelectDirective, MatSlideToggleDirective, MatSliderRangeThumbDirective, MatSliderThumbDirective, SelectDirective, formArrayFactory, formControl, formGroupFactory, isFormControl };
export type { ControlErrors, Form, FormArray, FormArrayErrors, FormControl, FormFactoryType, FormGroup, FormGroupErrors, GroupControls, OptionalKeys, TForm, ValidationErrors, ValidatorFn };