@modular-forms/qwik
Version:
The modular and type-safe form library for Qwik
97 lines (96 loc) • 3.95 kB
JavaScript
import { $, implicit$FirstArg, noSerialize } from '@builder.io/qwik';
import { globalActionQrl, } from '@builder.io/qwik-city';
import { AbortMessage } from '@builder.io/qwik-city/middleware/request-handler';
import { isDev } from '@builder.io/qwik/build';
import { decode } from 'decode-formdata';
import { FormError } from '../exceptions';
/**
* See {@link formAction$}
*/
export function formActionQrl(action, arg2) {
return globalActionQrl($(async (jsonData, event) => {
// Destructure validate function and form data info
const { validate, ...formDataInfo } = typeof arg2 === 'object' ? arg2 : { validate: arg2 };
// Get content type of request
const type = event.request.headers
.get('content-type')
?.split(/[;,]/, 1)[0];
// Get form values from form or JSON data
const values = type === 'application/x-www-form-urlencoded' ||
type === 'multipart/form-data'
? decode(event.sharedMap.get('@actionFormData'), formDataInfo, ({ output }) => output instanceof Blob ? noSerialize(output) : output)
: jsonData;
// Validate values and get errors if necessary
const errors = validate ? await validate(values) : {};
// Create form action store object
let formActionStore = {
values,
errors,
response: {},
};
// Try to run submit action if form has no errors
if (!Object.keys(errors).length) {
try {
const result = await action(values, event);
// Add result to form action store if necessary
if (result && typeof result === 'object') {
formActionStore = {
values,
errors: result.errors || {},
response: {
status: result.status,
message: result.message,
data: result.data,
},
};
}
// If an abort message was thrown (e.g. a redirect), forward it
}
catch (error) {
if (error instanceof AbortMessage ||
(isDev &&
(error?.constructor?.name === 'AbortMessage' ||
error?.constructor?.name === 'RedirectMessage'))) {
throw error;
// Otherwise log error and set error response
}
else {
console.error(error);
// If it is an expected error, use its error info
if (error instanceof FormError) {
formActionStore = {
values,
errors: error.errors,
response: {
status: 'error',
message: error.message,
},
};
// Otherwise return a generic message to avoid leaking
// sensetive information
}
else {
formActionStore.response = {
status: 'error',
message: 'An unknown error has occurred.',
};
}
}
}
}
// Return form action store object
return formActionStore;
}), {
id: action.getHash(),
});
}
/**
* Creates an action for progressively enhanced forms that handles validation
* and submission on the server.
*
* @param action The server action function.
* @param arg2 Validation and/or form data info.
*
* @returns Form action constructor.
*/
export const formAction$ = implicit$FirstArg(formActionQrl);