IdoPesok / zsa

https://zsa.vercel.app
MIT License
761 stars 23 forks source link

Additional parameters in addition to formData #134

Closed koveitan closed 3 months ago

koveitan commented 3 months ago

I have defined a Zod Schema and the type: formData, but i need to add additional parameters to the server action. I used Bind on the method before transitioning to ZSA, but it does not seem to work. I can't figure out if it's supported or not, do we have a way to do add additional parameters ?

IdoPesok commented 3 months ago

Hi, can you possibly share a small snippet of the code you are trying? The formData actions take the first parameter as formData and the second is an override object that you can pass additional values. It should all be in one zod object though, you can't do > 1 parameters.

So for instance, if you are trying to do:

const action = (arg1: string, arg2: string)

It would need to be

const action = (input: { arg1: string, arg2: string })
koveitan commented 3 months ago

.createServerAction() .input( z.object({ title: z.string().min(5, t.titleTooShort), description: z.string().min(5, t.descriptionTooShort), }), { type: "formData" }, ) .handler(async ({ input, ctx }) => { await createTaskUseCase( input.title, input.description, selectedDate, ctx.user, );

You can see in the code snippet that i need to pass the selectedDate to the createTask method, but the selected date is not coming from the form, it is a property in my react component. so i need a way to add this to the server action.

IdoPesok commented 3 months ago

Hi, would something like this work for your use case?

export const zsaAction = createServerAction()
  .input(
    z.object({
      title: z.string().min(5),
      description: z.string().min(5),
      selectedDate: z.date(),
    }),
    {
      type: "formData",
    }
  )
  .handler(async ({ input }) => {
    await createTaskUseCase(input.title, input.description, input.selectedDate);
  });
"use client";

import { zsaAction } from "./action";
import { useState } from "react";

export default function ZSAForm() {
  const [selectedDate] = useState(new Date());

  return (
    <div>
      <form
        onSubmit={async (e) => {
          const formData = new FormData(e.currentTarget);
          const [data, err] = await zsaAction(formData, {
            selectedDate,
          });
        }}
      >
        <input type="text" name="title" style={{ color: "black" }} />
        <input type="text" name="description" style={{ color: "black" }} />
        <button type="submit">Invoke action</button>
      </form>
    </div>
  );
}
IdoPesok commented 3 months ago

I realize though that this won't work if you are using useServerAction's execute. Fixing now. It should work if you are not using useServerAction.

koveitan commented 3 months ago

I did use the useServeAction hook, so it does not work, only if i call it directly same as your example. if you can implement this in the hook that will be great. Thanks for the support ! highly appreciated.

IdoPesok commented 3 months ago

Just released : ) Give it a shot with zsa-react@0.1.7

"use client";

import { useServerAction } from "zsa-react";
import { zsaAction } from "./action";
import { useState } from "react";

export default function ZSAForm() {
  const { execute, data } = useServerAction(zsaAction);
  const [selectedDate] = useState(new Date());

  return (
    <div>
      <form
        onSubmit={async (e) => {
          e.preventDefault();
          const formData = new FormData(e.currentTarget);
          const [data, err] = await execute(formData, {
            selectedDate,
          });
        }}
      >
        <input type="text" name="title" style={{ color: "black" }} />
        <input type="text" name="description" style={{ color: "black" }} />
        <button type="submit">Invoke action</button>
      </form>
      <div>{data && <pre>{JSON.stringify(data, null, 2)}</pre>}</div>
    </div>
  );
}