Skip to content

Instantly share code, notes, and snippets.

@genki
Created August 31, 2023 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save genki/632d57ffc53a136521f486867756469b to your computer and use it in GitHub Desktop.
Save genki/632d57ffc53a136521f486867756469b to your computer and use it in GitHub Desktop.
`submit(formStore, onSubmit$)` method that enables the `modular-forms/qwik` to submit the form equivalent to simulate the manual submit.
import type {
FormStore, FieldValues, ResponseData, FormErrors, FieldPath, FieldArrayPath,
SetResponseOptions,
} from '@modular-forms/qwik';
import {
getValues, validate, setResponse, FormError, setError,
} from '@modular-forms/qwik';
import { type Maybe } from '~/utils';
type ErrorResponseOptions = SetResponseOptions &
Partial<{
shouldActive: boolean;
}>;
export type SetErrorOptions = Partial<{
shouldActive: boolean;
shouldTouched: boolean;
shouldDirty: boolean;
shouldFocus: boolean;
}>;
export function getFieldStore<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>
>(
form: FormStore<FieldValues, ResponseData>,
name: TFieldName
) {
return form.internal.fields[name];
}
export function getFieldArrayStore<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>
>(
form: FormStore<FieldValues, ResponseData>,
name: TFieldName
) {
return form.internal.fieldArrays[name];
}
function setFieldErrors(
form: FormStore<FieldValues, ResponseData>,
errors: FormErrors<FieldValues>,
options: SetErrorOptions
) {
Object.entries(errors).forEach(([name, error]) => {
if (!error) return;
setError(form, name, error, { ...options, shouldFocus: false });
});
}
function setErrorResponse(
form: FormStore<FieldValues, ResponseData>,
formErrors: FormErrors<FieldValues>,
{ duration, shouldActive = true }: ErrorResponseOptions
): void {
// Combine errors that were not set for any field or field array into one
// general form error response message
const message = Object.entries<Maybe<string>>(formErrors)
.reduce<string[]>((errors, [name, error]) => {
if (
[
getFieldStore(form, name as FieldPath<FieldValues>),
getFieldArrayStore(form, name as FieldArrayPath<FieldValues>),
].every(
(fieldOrFieldArray) =>
!fieldOrFieldArray || (shouldActive && !fieldOrFieldArray.active)
)
) {
errors.push(error!);
}
return errors;
}, [])
.join(' ');
// If there is a error message, set it as form response
if (message) {
setResponse(form, { status: 'error', message }, { duration });
}
}
type SubmitOptions = {
keepResponse?: boolean;
duration?: number;
names?: string[];
shouldActive?: boolean;
};
export async function submit(
form: FormStore<FieldValues, ResponseData>,
onSubmit$: any,
options:SubmitOptions = {}
) {
// Reset response if it is not to be kept
if (!options.keepResponse) form.response = {};
// Increase submit count and set submitted and submitting to "true"
form.submitCount++;
form.submitted = true;
form.submitting = true;
// Try to run submit actions if form is valid
try {
if (await validate(form, options)) {
// Get current values of form
const values = getValues(form, options);
// Run submit actions of form
const result = await onSubmit$?.(values as FieldValues);
// Set form action result if necessary
if (result?.value) {
const { errors, response } = result.value;
setFieldErrors(form, errors, { ...options, shouldFocus: false });
if (Object.keys(response).length) {
setResponse(form, response, options);
} else {
setErrorResponse(form, errors, options);
}
}
}
// If an error occurred, set error to fields and response
} catch (error: any) {
if (error instanceof FormError) {
setFieldErrors(form, error.errors, { ...options, shouldFocus: false });
}
if (!(error instanceof FormError) || error.message) {
setResponse(
form,
{
status: 'error',
message: error?.message || 'An unknown error has occurred.',
},
options
);
}
// Finally set submitting back to "false"
} finally {
form.submitting = false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment