Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form actions #6469

Merged
merged 93 commits into from
Sep 6, 2022
Merged

Form actions #6469

merged 93 commits into from
Sep 6, 2022

Conversation

dummdidumm
Copy link
Member

@dummdidumm dummdidumm commented Aug 31, 2022

How to migrate

The POST/PUT/PATCH/DELETE methods on +page.server.js are replaced by an actions object.

If you used those methods for form submissions

Step 0 (before updating)

Remove method overrides from your svelte.config.js (they have been removed, see the other steps on how to migrate) - there have been reports that running npm install after bumping the version with this config still present fails.

// svelte.config.js
const config = {
  kit: {
-    methodOverride: {
-      allowed: ['PATCH', 'DELETE']
-    }
}

Step 1

If you have more than one form on that page, think about names that best describe it and add these names as keys to the new action object inside +page.server.js. Example:

// +page.server.js
/** @type {import('./$types').Actions} */
export const actions = {
  addTodo: () => {},
  editTodo: () => {}
};

If you only have one form, you don't need to give it a name, use default:

// +page.server.js
/** @type {import('./$types').Actions} */
export const actions = {
  default: () => {}
};

Step 2

Move your methods over to the respective names of the actions object. Adjust the return value accordingly:

  • If you redirect, replace return { location: 'X' } with throw redirect(303, 'X')
  • If you return errors replace return { status: 400, errors: errors } with return fail(400, errors) (return invalid(400, errors) if you are not migrating to the latest version of SvelteKit directly)
  • In case of a success you can now also return arbitrary data if you want (it needs to be a JSON-stringifiable object)
// +page.server.js
+ import { redirect, fail } from '@sveltejs/kit';

- export async function POST({ request }) {
+export const actions = {
+  default: async ({ request }) => {
      const values = await request.formData();
 
      const username = /** @type {string} */ (values.get('username'));
      const password = /** @type {string} */ (values.get('password'));
 
      const user = await db.findUser(username);
 
      if (!user) {
-        return {
-          status: 403,
-          errors: {
-            username: 'No user with this username'
-          }
-        };
+        return fail(403, { username: 'No user with this username' });
      }
 
-      return {
-        location: '/'
-      };
+      throw redirect(303, '/');
    }
+};

The success response and invalid response both become available on export let forms (see step 4 for more info).

Step 3

Update your <form> elements in +page.svelte (or components used by it), if you were previously using PUT/PATCH/DELETE and are now using named actions:

// +page.svelte
-<form method="POST" action="/todos?_method=PATCH">
+<form method="POST" action="?/editTodo">

The /editTodo is a normal query parameter. SvelteKit interprets a query parameter with a leading / as identifying a named action. ?/editTodo will call the editTodo action in export const actions = { editTodo: ..., ... }

Step 4

Replace export let errors with export let form in +page.svelte:

// +page.svelte
-/** @type {import('./$types').Errors} */
-export let errors;
+/** @type {import('./$types').ActionData} */
+export let form;

This will contain the return value of the action that just ran (as long as it didn't error or redirect), allowing you to show a 'success' message or (in the case of validation errors) fill in previously submitted values and indicate which fields were invalid.

Example:

This from the server..

// +page.server.js
export const actions = {
  default: () => {
    //...
    if (somethingNotRight) {
      return invalid(400, { error: 'wrong' };
    } else {
      return { success: 'right' };
    }
  }
};

... will become available on the component:

<script>
  export let form; // <- will be { success: 'right' } or { error: 'wrong' }
</script>

If you used those methods for something else

Either adjust your urls and only make POST requests with FormData to it, ot move these methods over to a +server.js file. We will very soon make it possible to colocate a +server.js file next to a +page.svelte file. The return value needs to be a Response object; you can use the json helper method to make creation of that easier.

Manually making the fetch requests

use:enhance should do all of this for you. If you're on the latest version of SvelteKit and want to do it, see #7494 on how.

PR description

Implements #5875
Closes #3533

TODO

  • export const actions
  • remove export let errors in favor of export let form
  • migration hints
  • migration docs
  • docs

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. All changesets should be patch until SvelteKit 1.0

@changeset-bot
Copy link

changeset-bot bot commented Aug 31, 2022

🦋 Changeset detected

Latest commit: f2cdab2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
create-svelte Patch
@sveltejs/kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

packages/kit/src/runtime/control.js Outdated Show resolved Hide resolved
packages/kit/src/exports/index.js Outdated Show resolved Hide resolved
packages/kit/src/runtime/control.js Outdated Show resolved Hide resolved
packages/kit/src/runtime/server/page/actions.js Outdated Show resolved Hide resolved
@twobitunicorn
Copy link

@dummdidumm Thanks! I'll get working through them.

@twobitunicorn
Copy link

@rowantrollope Check your svelte.config.js and make sure you are no longer adding the method overrides for PATCH and DELETE. The snippet below is possibly what you have if you have an older project (as I did!).

// Override http methods
methodOverride: {
    allowed: ['PATCH', 'DELETE']
}

@rowantrollope
Copy link

rowantrollope commented Sep 7, 2022 via email

@tsukhu
Copy link

tsukhu commented Sep 7, 2022

  • Upgraded to form actions for the login screen . This is a really nice feature
    From the form post to processing of the action is there a way or a hook to know the current state of the server (for example - form submitted, action is in progress etc.)
  • Some way to show the button spinner icon to know something is going on.

@dummdidumm
Copy link
Member Author

You can provide your own callback to the enhance action and set a variable to pending during fetching. When the return callback function is called you know the fetch is done.

@dimplast
Copy link

dimplast commented Sep 7, 2022

in docs
Anatomy of an action

in file:
src/routes/login/+page.svelte

<script>
export let data;
export let form;
</script>

{#if form?.success}
<p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}

Should we write?

{#if form?.success}
<p>Successfully logged in! Welcome back, {form.user.name}</p>
{/if}

export let data; is necessary

@psytrx
Copy link
Contributor

psytrx commented Sep 7, 2022

export let data; is necessary

Ooh, that likely explains why my experiments did not work as expected (and also #6611). I was wondering whether forms is typed automatically. Having seen the incredible typing magic by @dummdidumm I assume it is, and that's probably why we return validation errors instead of throwing them?

@vipero07
Copy link

vipero07 commented Sep 8, 2022

@rowantrollope Check your svelte.config.js and make sure you are no longer adding the method overrides for PATCH and DELETE. The snippet below is possibly what you have if you have an older project (as I did!).

// Override http methods
methodOverride: {
    allowed: ['PATCH', 'DELETE']
}

This should be mentioned in the migration guide.

@rowantrollope
Copy link

rowantrollope commented Sep 8, 2022 via email

Walker088 added a commit to Walker088/svelteBlog that referenced this pull request Sep 10, 2022
1. Remove methodOverride from svelte.conf.js
Ref: sveltejs/kit#6469
2. Rename hooks.js to hooks.server.js
Ref: sveltejs/kit#6586
3. Change post not found status code from 400 to 404
4. Remove useless import { page } in +layout.svelte
@franksplaudio
Copy link

So what about array-data inside my form?
Given a form to edit e.g. a product category with multiple names in different languages. The names are inside an input field inside an #each block.
How can the actions method get the different values form the input fields?

@franksplaudio
Copy link

Ok, got it. FormData.getAll().

@j2l
Copy link

j2l commented Oct 13, 2022

Does <p>Successfully logged in! Welcome back, {data.user.name}</p> work for you?
On success, data={} right?

@dlamon1
Copy link
Contributor

dlamon1 commented Dec 16, 2022

Step 2 should import fail, not invalid

@sifferhans sifferhans mentioned this pull request Dec 16, 2022
9 tasks
@feynmanliang
Copy link

feynmanliang commented Dec 19, 2022

May want to address JSON POST endpoints for the conversion guide? In my case I needed to use devalue.parse((await response.json()).data) to restore the original response.json() shape.

@dummdidumm
Copy link
Member Author

See #7494 for that, I added a note to the migration description

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

<Form>