IdoPesok / zsa

https://zsa.vercel.app
MIT License
447 stars 13 forks source link

How to FormData.getAll? #88

Closed bduff9 closed 1 month ago

bduff9 commented 1 month ago

Hello! We just found this library a couple of weeks ago and have started trying to implement it into our application where we heavily use server actions. We ran into a case that the documentation does not mention so we are hoping there is an answer.

We have several cases in our server actions where we need to call FormData.getAll to get all inputs instead of FormData.get. How can we do this with this library?

For instance, we might have the code in the server action that looks like this:

        const result = zodSchema.safeParse({
        ...Object.fromEntries(formData),
        files: formData.getAll('files'),
    });
IdoPesok commented 1 month ago

Just in time (just merged a PR for this right now)!

Please upgrade to zsa@0.3.1 and zsa-openapi@0.0.10

The TLDR is you want to use a z.array(z.instanceof(File)). For a more comprehensive example, we posted a new section on our docs.

"use server"

import z from "zod"
import { createServerAction } from "zsa"

export const myFilesAction = createServerAction()
  .input(
    z.object({
      filefield: z.array(z.instanceof(File)),
    }),
    {
      type: "state",
    }
  )
  .handler(async ({ input }) => {
    await new Promise((resolve) => setTimeout(resolve, 500))
    console.log(input.filefield) // this is an array of File objects
  })

in client:

"use client"

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { useActionState } from "react"
import { myFilesAction } from "./actions"

export default function Example() {
  let [[data, err], submitAction, isPending] = useActionState(
    myFilesAction,
    [null, null]
  )

  return (
    <Card className="not-prose">
      <CardHeader>
        <CardTitle>Multi Entry Form</CardTitle>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        <form action={submitAction} className="flex flex-col gap-4">
          <Input name="filefield" type="file" multiple />
        </form>
        {isPending && <div>Loading...</div>}
        {data && <p>Result: {data}</p>}
        {err && <div>Error: {JSON.stringify(err.fieldErrors)}</div>}
      </CardContent>
    </Card>
  )
}

Closing this issue now, please ping back here if something isn't working or with any further questions!