Closed Maikpwwq closed 1 year ago
This is a minimal reproduction, abstracted from my main project. I need to solve this issue now. How to use forms in Qwik?
The documentation talks about @modular-forms/qwik
I follow the configuration but it still fails. Really I have prove almost everything, serverless functions on Vercel, server$
functions on Qwik, now actions$
and I am a bit frustrated with this. It works in development mode in production deploy continue failing.
I have tried first with mongo, now with supabase the problem persist. My file Index.tsx
import { component$, $, useTask$ } from "@builder.io/qwik"; // , useSignal
import { isServer } from "@builder.io/qwik/build";
import { createClient } from "@supabase/supabase-js";
import { v4 as uuidv4 } from 'uuid'
import clsx from "clsx";
import {
routeLoader$,
z,
} from "@builder.io/qwik-city";
import type { InitialValues, SubmitHandler } from "@modular-forms/qwik"; //
import {
useForm,
formAction$,
zodForm$,
reset,
} from "@modular-forms/qwik";
import styles from "~/components/modular-forms/modularForm.module.css";
import { MUITypography, MUIPaper } from "~/integrations/react/mui";
import { TextInput } from "~/components/modular-forms/TextInput";
const SUPABASE_URL = `${import.meta.env.VITE_SUPABASE_URL}`;
const SUPABASE_KEY = `${import.meta.env.VITE_SUPABASE_KEY}`;
type LoginForm = {
name: string;
email: string;
phone: string;
issue: string;
message: string;
};
const loginSchema = z.object({
name: z.string().min(1, "Por favor introduzca su nombre."),
email: z
.string()
.min(1, "Por favor introduzca su email.")
.email("The email address is badly formatted."),
phone: z
.string()
.min(1, "Por favor introduzca su teléfono.")
.min(10, "Tu teléfono debe tener 10 caracteres o más."),
issue: z.string().min(1, "Por favor introduzca su asunto."),
message: z
.string()
.min(1, "Por favor introduzca su mensaje.")
.min(8, "Tu mensaje debe tener 8 caracteres o más."),
});
// Also posible infer typos
// type LoginForm = z.infer<typeof loginSchema>;
// can only be declared in `layout.tsx`, `index.tsx` and `plugin.tsx` inside the src/routes directory
export const useFormLoader = routeLoader$<InitialValues<LoginForm>>(() => ({
name: "",
email: "",
phone: "",
issue: "",
message: "",
}));
type ResponseData = {
customerId: string;
};
export const useFormAction = formAction$<LoginForm, ResponseData>(
async (values) => {
// Runs on SERVER
console.log("useFormAction", values);
try {
// Create a single supabase client for interacting with your database
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const { name, email, phone, issue, message } = values;
const recordID : string = uuidv4();
const hexNumber : number = 1; // parseInt(recordID.replace(/-/g, ''), 16);
const { data: customer_form, error } = await supabase
.from("customer_form")
.insert([{ id: hexNumber, created_at: new Date(), name, email, phone, issue, message }])
.select("*");
console.log("supabase contact form", customer_form, error);
if (customer_form) {
console.log("Success supabase contact form", customer_form[0].id);
}
if (error) {
console.log("Error supabase contact form", error);
}
return {
status: "success",
message: `Gracias, su mensaje ha sido recibido. ${recordID}`,
data: { customerId: recordID },
};
} catch (error) {
console.error(error);
return {
status: "error",
message: `No se ha podido enviar su mensaje. ${error}`,
data: { customerId: "" },
};
}
},
zodForm$(loginSchema),
); // valiForm$(LoginSchema)
export default component$(() => {
// const nav = useNavigate();
// , FieldArray
const [loginForm, { Form, Field }] = useForm<LoginForm, ResponseData>({
loader: useFormLoader(),
action: useFormAction(),
validate: zodForm$(loginSchema),
});
const handleSubmit = $<SubmitHandler<LoginForm>>(
async (values: any, event: any) => {
// Runs on CLIENT
console.log("handleSubmit", values, event);
},
);
const successData = $(async () => {
console.log(
"handleSubmitSuccess",
loginForm.submitted,
loginForm.submitting,
loginForm.response,
);
alert(loginForm.response.message);
reset(loginForm); // , useFormLoader
// clearResponse(loginForm);
// const value = getValue(form, name, options);
// await nav("/");
});
const errorData = $(async () => {
console.log(
"handleSubmitError",
loginForm.submitted,
loginForm.submitting,
loginForm.response,
);
alert(loginForm.response.message);
});
useTask$(({ track }) => {
track(() => loginForm.response.status);
if (isServer) {
return; // Server guard
}
if (
loginForm.submitted &&
loginForm.submitting === false &&
loginForm.response.status === "success"
) {
successData();
} else if (
loginForm.submitted &&
loginForm.submitting === false &&
loginForm.response.status === "error"
) {
errorData();
}
});
return (
<div class="container container-center flex justify-center" style={{}}>
<MUIPaper className={styles.cardContactForm} elevation={16}>
<div class={styles.sheetFormStyle}>
<MUITypography
variant="h6"
color={"var(--qwik-dark-blue)"}
align="center"
>
Formulario de contacto
</MUITypography>
<MUITypography variant="body1" className="pt-2 pb-4" align="center">
Solicita información adicional o una presentación de nuestros
servicios.
</MUITypography>
<Form
class={styles.formFlex}
onSubmit$={handleSubmit}
// preventdefault:submit
// reloadDocument={true}
>
<Field
name="name"
>
{(field, props) => (
<TextInput
{...props}
value={field.value}
error={field.error}
type="text"
label="Nombre:"
placeholder="Nombre"
required
/>
)}
</Field>
<Field
name="email"
>
{(field, props) => (
<TextInput
{...props}
value={field.value}
error={field.error}
type="email"
label="Email:"
placeholder="Correo electrónico"
required
/>
)}
</Field>
<Field
name="phone"
>
{(field, props) => (
<TextInput
{...props}
value={field.value}
error={field.error}
type="tel"
label="Teléfono:"
placeholder="+57"
required
/>
)}
</Field>
<Field
name="issue"
>
{(field, props) => (
<TextInput
{...props}
value={field.value}
error={field.error}
type="text"
label="Asunto:"
placeholder="Asunto"
required
/>
)}
</Field>
<Field
name="message"
>
{(field, props) => (
<TextInput
{...props}
value={field.value}
error={field.error}
type="text"
label="Mensaje:"
placeholder="Mensaje"
required
/>
)}
</Field>
<button
type="submit"
class={clsx("mx-3 lg:mx-5", styles.btnStyle)}
>
Enviar
</button>
</Form>
</div>
</MUIPaper>
</div>
);
});
Are you by any chance generating SSG (static site?) If so then server$
can't work as there is no server.
Does npm run preview
work? That should be same as replayed behavior
@mhevery Yes this site use Adapters for SSG
. This because Vercel deploy only static content. Use server$()
was one of my first alternatives, after I tried with express adapter
, and get that response about Vercel site content. So I tried again with routeLoader$()
and routeAction$()
doing a page instead a component.
When I execute command npm run preview
it works on my local.
I have store forms sucessfully from NuxtJS and ViteJS in Vercel projects.
None of the static server solutions can work with serevr$
or routeAction$()
solution as all of this requires as server running JS. So I don't think this is a Qwik bug.
I am going to close this issue as this is a limitation of SSG.
@mhevery why it is not about Qwik if Nuxt and Vite-plugin-ssr bouth are SSR frameworks by default and they work in Vercel?
Which component is affected?
Qwik City (routing)
Describe the bug
I have a Qwik app that uses Static adapter. In one of my components I made a request into a
server$(async function(data){ const resume = await connectionDB(data); ...})
, it all runs correctly in my local enviroment, catchin my servicemongo-init.js
. Then I deploy my project in Vercel. During build stage everything works fine. But when in production I try to request, I get this Error:Why it is calling that route in my project. ie
/?qfunc=...
.Reproduction
https://github.com/Maikpwwq/nexasoft.git
Steps to reproduce
my
mongo-init.js
file:my
FormComponent.jsx
file:System Info
Additional Information
No response