When to use form actions vs. on:submit?
Asked Answered
S

4

8

I am currently learning Svelte and SvelteKit and so far I like it.

However one thing that puzzles me a bit is the question when I should use form actions and when should I use a simple on:submit handler?

Are there different use cases? Or maybe they can be both used at the same time? What should I do if I want to send JSON data to an external API?

Thanks for any tips!

Shelled answered 1/3, 2023 at 23:29 Comment(0)
C
21

A major difference between form actions and on:submit is form actions are intended to work without JS; on:submit will not work if JS is disabled/broken.

So SvelteKit encourages using form actions whenever possible to provide the optimal user browsing experience.

If you want to send JSON data to an external API, there are a few options including:

  • Call the external API directly from the client (browser). Probably the simplest option, but not recommended if private API keys are exposed anywhere in the URL/payload. Also this will probably not work if JS is disabled or broken.
  • Wrap the API with a SvelteKit +server route, which calls the external API from the server (vs. from the client above). This helps prevent leaking private API keys, but still will not work if JS is disabled or broken.
  • Call the API from a form action. Form actions don't take JSON as input though; they take FormData as input. So the action should construct the JSON from the form inputs.
Conlon answered 2/3, 2023 at 12:12 Comment(2)
I wish I could click the upvote again. Thank you for the clarity.Chemosphere
@CaseyPlummer: Your comment was even nicer than an extra upvote! Glad I could help ^^Conlon
G
4

If the access to the external APIs has to happen from the server (e.g. because of non-disclosed credentials), using a form action is recommended.

Ideally with SvelteKit one should use the enhance action which allows the page to be updated with response data without a full reload automatically (data is set to the form property) and with form actions the page should still work even if JS fails to load/is disabled, then with a regular form post.

There should rarely be any reason to handle submit, the enhance action can also be used to intercept the submission and e.g. add additional data to the request.

If you want to go directly to the external API from the browser, then intercepting it makes sense and allows you to send a custom JSON request if the API does not support regular form encoded data.

Gujarati answered 1/3, 2023 at 23:55 Comment(7)
So e.g. a login request to a non-protected endpoint should be done via on:submit handler - am I understanding correctly?Shelled
It can be, yes. May depend a bit on the API as well, some even require that the user's client sends the request directly to set certain cookies as far as I know. Also, even protected endpoints may be accessed if the credentials being used are those provided by the user (e.g. cookies) and not some secret key provided by your application. I am not an expert on various authentication protocols involving third party providers, but I could also imagine a flow where it has to go through the server and a response may contain something only intended for said server.Gujarati
If the API is something provided as an external commercial service, there usually would be documentation explaining where from and how requests should be sent.Gujarati
Late comment, but would that also relate to buttons without inputs? Like a simple button which calls the backend to delete something, no other inputs. Could either be a form or a normal endpoint, but seems like form is the way to go?Robbery
@Mark: Forms are usually the way to go, especially if you want to make things work even in case JS fails to load (or is disabled).Gujarati
@Gujarati okay great, that’s what I thought. Thanks. But triggering them with fetch won’t trigger the load function again - correct?Robbery
@Mark: If you don't use a form (with or without enhance), you don't get the default form action behavior. You can invalidateAll manually, but I would recommend just using a form.Gujarati
S
2

While form actions are very useful and handy, in reality you might want your webapp to be deployed with adapter-static for better performance and/or hosting with providers like Cloudflare.

If that's the case, you'll need to use endpoints (+server.js) and fetch rather than +page.server.js files to hanlde all your back-end operations.

I find my reason more important to base the decision on than the other two answers, though they still valid to some point.

Selima answered 23/4, 2023 at 10:22 Comment(0)
L
0

In my recent app, I even do one level down, and I use on:click to handle the POST action of a form.

  1. I have a page showing all the details of a book, including the tags.
  2. I have a modal that pops up to ask the user to input a few tags of that book by his own.
  3. Then I insert any 'new' tags into the database.

I use PHP to develop the backend API. I use on:click is because I think this saves me a lot of time. The code snippet is quite straightforward.

<Modal bind:open={popupModal} size="xs" autoclose={false} class="w-full">
  <form class="flex flex-col space-y-6" method="post">
    <h3 class="text-xl font-medium text-gray-900 dark:text-white p-0">
      为《{book.title}》添加tag
    </h3>
    <Label class="space-y-2">
      <span>Tags(用空格分隔)</span>
      <Input type="text" name="tags" bind:value={finalTags} required />
      <Input type="hidden" name="id" value={book.id} />
    </Label>
    <Button type="button" on:click={doPost} class="w-full1">创建新的TAG</Button>
  </form>
</Modal>

And then in the doPost() function:

async function doPost() {
    const inputTags = finalTags.trim().split(" ");
    const oldTags = tags;
    const difference = inputTags.filter((x) => !oldTags.includes(x));
    const insert = difference.join(" ");
    const res = await fetch(
      form_uri,
      {
        headers: {
          "Content-Type": "application/json",
        },
        method: "POST",
        body: JSON.stringify({
          'id': book.id,
          'tags': insert,
        }),
      }
    );
    const json = await res.json();
    location.reload();
  }

So I can refresh the page after any tags being inserted.

Just my thoughts.

Lawyer answered 24/4, 2023 at 7:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.