mobx-form
Version:
A simple form helper for mobx
261 lines (260 loc) • 9.05 kB
TypeScript
import type { DebouncedFunc } from "lodash";
export type Descriptors<T> = {
[P in keyof T]: FieldDescriptor<T[P], T>;
};
export type FormModelArgs<T> = {
descriptors: Partial<Descriptors<T>>;
initialState?: Partial<T>;
options?: ThrowIfMissingFieldType;
};
export type ResultObj = {
error: string;
};
export type ErrorLike = {
message: string;
} | Error;
export type ValidatorResult = boolean | ResultObj | void;
export type ValidateFnArgs<T, K> = {
field: Field<T, K>;
fields: FormModel<K>["fields"];
model: FormModel<K>;
value: Field<T, K>["value"];
};
export type ValidateFn<T, K> = (args: ValidateFnArgs<T, K>) => Promise<ValidatorResult> | ValidatorResult;
export type ResetInteractedFlagType = {
resetInteractedFlag?: boolean;
};
export type ThrowIfMissingFieldType = {
throwIfMissingField?: boolean;
};
export interface FieldDescriptor<T, K> {
waitForBlur?: boolean;
disabled?: boolean;
errorMessage?: string;
validator?: ValidateFn<T, K> | ValidateFn<T, K>[];
hasValue?: (value: T) => boolean;
value?: T;
required?: boolean | string;
autoValidate?: boolean;
validationDebounceThreshold?: number;
clearErrorOnValueChange?: boolean;
meta?: Record<string, any>;
}
export type CommitType = {
commit?: boolean;
};
export type ForceType = {
force?: boolean;
};
export type SetValueFnArgs = ResetInteractedFlagType & CommitType;
/**
* Field class provides abstract the validation of a single field
*/
export declare class Field<T, K> {
_name: string;
meta?: Record<string, any>;
_model: FormModel<K>;
_waitForBlur?: boolean | undefined;
_disabled?: boolean | undefined;
_required?: boolean | string;
_validatedOnce: boolean;
_clearErrorOnValueChange?: boolean | undefined;
_hasValueFn?: (value: T) => boolean;
get name(): string;
get model(): FormModel<K>;
get validatedAtLeastOnce(): boolean;
get waitForBlur(): boolean;
get disabled(): boolean;
get required(): boolean;
resetInteractedFlag(): void;
markAsInteracted(): void;
resetValidatedOnce(): void;
get hasValue(): boolean;
_validationTs: number;
/**
* flag to know if a validation is in progress on this field
*/
_validating: boolean;
/**
* field to store the initial value set on this field
* */
_initialValue?: T;
/**
* the value of the field
* */
_value?: T;
/**
* whether the user interacted with the field
* this means if there is any value set on the field
* either setting it using the `setValue` or using
* the setter `value`. This is useful to know if
* the user has interacted with teh form in any way
*/
_interacted: boolean;
/**
* whether the field was blurred at least once
* usually validators should only start being applied
* after the first blur, otherwise they become
* too invasive. This flag be used to keep track of
* the fact that the user already blurred of a field
*/
_blurredOnce: boolean;
get blurred(): boolean;
/** the raw error in caes validator throws a real error */
rawError?: ErrorLike;
/**
* the error message associated with this field.
* This is used to indicate what error happened during
* the validation process
*/
get errorMessage(): string | undefined;
/**
* whether the validation should be launch after a
* new value is set in the field. This is usually associated
* to forms that set the value on the fields after each
* onChange event
*/
_autoValidate: boolean;
get autoValidate(): boolean;
/**
* used to keep track of the original message
*/
_originalErrorMessage?: string;
/**
* whether the field is valid or not
*/
get valid(): boolean;
/**
* whether the user has interacted or not with the field
*/
get interacted(): boolean;
/**
* get the value set on the field
*/
get value(): T | undefined;
_setValueOnly: (val?: T) => void;
_setValue: (val?: T) => void;
/**
* setter for the value of the field
*/
set value(val: T);
setValue: (value?: T, { resetInteractedFlag, commit }?: SetValueFnArgs) => void;
/**
* Restore the initial value of the field
*/
restoreInitialValue: ({ resetInteractedFlag, commit }?: SetValueFnArgs) => void;
get dirty(): boolean;
commit(): void;
/**
* clear the valid state of the field by
* removing the errorMessage string. A field is
* considered valid if the errorMessage is not empty
*/
resetError(): void;
clearValidation(): void;
/**
* mark the field as already blurred so validation can
* start to be applied to the field.
*/
markBlurredAndValidate: () => void;
_validateFn?: ValidateFn<T, K> | Array<ValidateFn<T, K>>;
_doValidate: () => Promise<ValidatorResult | undefined>;
setDisabled(disabled: boolean): void;
validate: (opts?: ForceType) => Promise<void>;
get originalErrorMessage(): string;
setValidating: (validating: boolean) => void;
get validating(): boolean;
/**
* validate the field. If force is true the validation will be perform
* even if the field was not initially interacted or blurred
*
*/
_validate: ({ force }?: ForceType) => Promise<void>;
setRequired: (val: boolean | string) => void;
setErrorMessage: (msg?: string) => void;
setError: (error: ErrorLike) => void;
get error(): string | undefined;
_debouncedValidation?: DebouncedFunc<Field<T, K>["_validate"]>;
constructor(model: FormModel<K>, value: T, validatorDescriptor: FieldDescriptor<T, K>, fieldName: string);
}
/**
* a helper class to generate a dynamic form
* provided some keys and validators descriptors
*
* @export
* @class FormModel
*/
export declare class FormModel<K> {
get validatedAtLeastOnce(): boolean;
get dataIsReady(): boolean;
get requiredFields(): (keyof K)[];
get requiredAreFilled(): boolean;
fields: {
[P in keyof K]: Field<K[P], K>;
};
_validating: boolean;
get valid(): boolean;
/**
* whether or not the form has been "interacted", meaning that at
* least a value has set on any of the fields after the model
* has been created
*/
get interacted(): boolean;
/**
* Restore the initial values set at the creation time of the model
* */
restoreInitialValues(opts?: SetValueFnArgs): void;
commit(): void;
get dirty(): boolean;
/**
* Set multiple values to more than one field a time using an object
* where each key is the name of a field. The value will be set to each
* field and from that point on the values set are considered the new
* initial values. Validation and interacted flags are also reset if the second argument is true
* */
updateFrom(obj: Partial<K>, { resetInteractedFlag, ...opts }?: SetValueFnArgs & ThrowIfMissingFieldType): void;
/**
* return the array of errors found. The array is an Array<String>
* */
get summary(): string[];
setValidating: (validating: boolean) => void;
get validating(): boolean;
/**
* Manually perform the form validation
* */
validate: () => Promise<void>;
/**
* Update the value of the field identified by the provided name.
* Optionally if reset is set to true, interacted and
* errorMessage are cleared in the Field.
* */
updateField: (name: keyof K, value?: K[keyof K], opts?: SetValueFnArgs & ThrowIfMissingFieldType) => void;
/**
* return the data as plain Javascript object (mobx magic removed from the fields)
* */
get serializedData(): K;
/**
* Creates an instance of FormModel.
* initialState => an object which keys are the names of the fields and the values the initial values for the form.
* validators => an object which keys are the names of the fields and the values are the descriptors for the validators
*/
constructor(args: FormModelArgs<K>);
_getField(name: keyof K, { throwIfMissingField }?: ThrowIfMissingFieldType): { [P in keyof K]: Field<K[P], K>; }[keyof K];
_eachField(cb: (field: Field<K[keyof K], K>) => void): void;
get _fieldKeys(): (keyof K)[];
resetInteractedFlag(): void;
disableFields: (fieldKeys: (keyof K)[]) => void;
_createField({ name, descriptor, }: {
name: keyof K;
descriptor: FieldDescriptor<K[keyof K], K>;
}): void;
addFields: (fieldsDescriptor: Partial<Descriptors<K>>) => void;
enableFields(fieldKeys: (keyof K)[]): void;
resetValidatedOnce(): void;
}
/**
* return an instance of a FormModel refer to the constructor
*
*/
export declare const createModel: <T>(args: FormModelArgs<T>) => FormModel<T>;
export declare const createModelFromState: <T>(initialState?: Partial<T>, validators?: Descriptors<T>, options?: ThrowIfMissingFieldType) => FormModel<T>;