0

So I have a SvelteKit project with the following project structure:

lib/
├─ components/
│  └─ my_module/
│     └─ my_resource/
│        ├─ CreateObjectButton.svelte ← Button component (important by dashboard, don't think this is relevant)
│        └─ CreateObjectModal.svelte ← Modal component (imported by dashboard)
routes/
└─ (protected)/
   ├─ dashboard/
   │  ├─ +page.svelte               ← Contains the grid of cards
   │  └─ cards/
   │     └─ MyResourceCard.svelte  ← Card component with button that triggers modal
   │
   └─ my_module/
      └─ my_resource/
         └─ +page.server.js         ← Contains the CRUD actions, only create for now

I’m trying to have a modal (CreateObjectModal.svelte) open from /dashboard and submit a form to the create action in /my_module/my_resource/+page.server.js.

The simplified modal component:

<script>
  import { createNotableModal } from '$lib/stores/modal';
  import { enhance } from '$app/forms';
  import { goto } from '$app/navigation';

  export let num_rows = 2;

  const closeModal = () => $createNotableModal.isOpen = false;
</script>

{#if $createNotableModal.isOpen}
  <div class="modal-overlay" on:click={closeModal}>
    <div class="modal" role="dialog" on:click|stopPropagation>
      <form
        method="POST"
        action="/my_module/my_resource?/create"
        use:enhance={({ result, update }) => {
          console.log(result);
          update();
        }}
      >
        <input name="alias" type="text" required />
        <input name="name" type="text" required />
        <textarea name="jottings" rows={num_rows}></textarea>
        <button type="submit">Create</button>
        <button type="button" on:click={closeModal}>Cancel</button>
      </form>
    </div>
  </div>
{/if}

The server action:

import { fail } from '@sveltejs/kit';

export const actions = {
  create: async ({ request }) => {
    console.log('Action hit!');  // This never runs
    const data = await request.formData();
    return { success: true, data: Object.fromEntries(data) };
  }
};

What I’ve tried:

  • Using action="/my_module/my_resource?/_action=create" (absolute path) and ?/create (relative)
  • Removing on:submit|preventDefault
  • Moving the modal temporarily into /my_module/my_resource/
  • Checking for JS errors in the console — none present
  • Confirming the modal opens and clicking buttons works

Observations:

  • The network tab shows no POST request when I submit.
  • console.log in the server action never triggers.
  • Clicking the submit button does not trigger on:click handlers either.

Minimal example test: I created a standalone form directly in /my_module/my_resource/+page.svelte (no modal, one input, one button), and it works perfectly. The network request fires and the server logs the action.

Question:

Why does the form inside my modal never submit to the server action when the modal is opened from /dashboard, even when using the absolute path? How can I fix it so a modal component rendered on a different route can post to another route’s named action?

2 Answers 2

1

Something is just wrong with the HTML/JS if no request is triggered in the first place.

One thing that is wrong is the enhance usage. It takes a submit function, which then returns a result callback. The current code should just cause an error because update will be undefined which in turn will prevent the submission.

If anything, the code should be:

<form
  ...
  use:enhance={() => ({ result, update }) => {
    console.log(result);
    update();
  }}
>

Using form actions from another route is possible, but the update behavior will be different.

Without an argument, use:enhance will emulate the browser-native behaviour, just without the full-page reloads. It will:

  • update the form property, page.form and page.status on a successful or invalid response, but only if the action is on the same page you're submitting from. For example, if your form looks like <form action="/somewhere/else" ..>, the form prop and the page.form state will not be updated. This is because in the native form submission case you would be redirected to the page the action is on. If you want to have them updated either way, use applyAction
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, I was defining the callback directly. But SvelteKit expects enhance to be given a function that returns a callback. That way it can pass in the submit context (form, cancel, etc.), and then my returned function will receive { result, update }. This now works: "use:enhance={() => ({ result, update }) => { if (result.type === 'success') { closeModal(); goto('/my_module/my_resource'); } else if (result.type === 'failure') { console.log('Error:', result.data); } update(); }}
-1

Try removing the absolute path in the action and just post to ?/create. The component exists in the context of whatever page is using it, so when you submit the modal form it's as if you're submitting a form straight from that page.

2 Comments

No dice - note that the modal (from where the action is called) is in another directory than the page that's using it, which is in another directory than the actual +page.server.js.
My fault, I missed the fact that you were using enhance

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.