How do I prevent page from refreshing when a from component is submitted in svelte kit?

45 Views Asked by At

so on this page I have 2 drop-down and when submit button is clicked it is fetching data from a pocketbase backend. I am making a table component for each raw of data. (Component displays a id, link, a dropdown to change it's status and a submit button that uses form action again to update that particular row of data in the collection). I am using enhance (also tried using prevent default) but still the page is still refreshing. 1 thing to point out is I am using a variable named linkType in main script tag that stores value for what a user have selected from dropdown. It is shared among all components

Any idea how can I prevent it?

<script>
    import { enhance } from '$app/forms';

    export let data;
    let selectedUser = '';
    let selectedCourse = '';
    function handleUserChange(event) {
        selectedUser = event.target.value;
        selectedCourse = '';
    }
    function handleCourseChange(event) {
        selectedCourse = event.target.value;
    }
    async function showSelection() {
        console.log('Selected User:', selectedUser);
        console.log('Selected Course:', selectedCourse);
    }

    export let form;
    let linkType = '';
    let linkResult = '';

    let linkTypes = ['Type1', 'Type'];
    let linkResults = ['Result1', 'Result2'];

    function handleLinkTypeChange(event) {
        linkType = event.target.value;
    }

    function handleLinkResultChange(event) {
        linkResult = event.target.value;
    }
</script>

<div>
    <h1 class="text-white-700 text-center text-4xl font-bold">Select Client Data</h1>

    {#if data}
        <form
            action="?/userSelection"
            method="POST"
            use:enhance
            class="flex flex-col items-center space-y-4 p-10"
        >
            <select
                name="user"
                class="select select-accent w-full max-w-xs"
                value={selectedUser}
                on:change={handleUserChange}
            >
                <option disabled selected>Select a User</option>
                {#each data.userData as user}
                    <option value={user.user}>{user.user}</option>
                {/each}
            </select>
            {#if selectedUser !== ''}
                <select
                    name="course"
                    class="select select-accent w-full max-w-xs"
                    value={selectedCourse}
                    on:change={handleCourseChange}
                >
                    <option disabled selected>Select a Course</option>
                    {#each data.userData.find((user) => user.user === selectedUser)?.courses || [] as course}
                        <option value={course}>{course}</option>
                    {/each}
                </select>
            {/if}
            <button type="submit" on:click={showSelection} class="btn btn-primary">Get Data</button>
        </form>
    {:else}
        <p>Loading...</p>
    {/if}

    {#if form && form.response}
        {#each form.response as row}
            <div class="overflow-x-auto">
                <table class="min-w-full divide-y divide-gray-200">
                    <thead class="bg-gray-50">
                        <tr>
                            <th
                                scope="col"
                                class="w-1/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500"
                                >ID</th
                            >
                            <th
                                scope="col"
                                class="w-1/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500"
                                >Link</th
                            >
                            <th
                                scope="col"
                                class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500"
                                >Link Type</th
                            >
                            <th
                                scope="col"
                                class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500"
                                >Link Result</th
                            >
                            <th
                                scope="col"
                                class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500"
                                >Update</th
                            >
                        </tr>
                    </thead>
                    <tbody class="divide-y divide-gray-200 bg-white">
                        <tr class="border-b border-gray-200 hover:bg-gray-100">
                            <td class="whitespace-nowrap px-6 py-4">{row.id}</td>
                            <td
                                class="whitespace-nowrap px-6 py-4"
                                style="max-width: 300px; overflow-x: auto; width: 100%;"
                                ><a href={row.url} class="truncate">{row.url}</a></td
                            >
                            <td class="whitespace-nowrap px-6 py-4">
                                <select
                                    class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
                                >
                                    {#each linkTypes as type}
                                        <option value={type}>{type}</option>
                                    {/each}
                                </select>
                            </td>
                            <td class="whitespace-nowrap px-6 py-4">
                                <select
                                    class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
                                >
                                    {#each linkResults as result}
                                        <option value={result}>{result}</option>
                                    {/each}
                                </select>
                            </td>
                            <td class="whitespace-nowrap px-6 py-4 text-right">
                                <form
                                    action="?/update"
                                    method="POST"
                                    use:enhance
                                    on:submit={(event) => event.preventDefault()}
                                >
                                    <input type="hidden" hidden value={row.id} name="id" />
                                    <input type="hidden" hidden value={linkType} name="linkType" />
                                    <input type="hidden" hidden value={linkResult} name="linkResult" />
                                    <button
                                        type="submit"
                                        class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-700"
                                        >Update</button
                                    >
                                </form>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        {/each}
    {:else}
        <div><p>Select User First</p></div>
    {/if}
</div>


Also this is my +page.server.js file that has action -->

export async function load({ locals }) {
    try {
        const users = await locals.pb.collection('UserData').getFullList();
        const userCoursesMap = new Map();
        users.forEach(entry => {
            const userId = entry.user[0];
            const courseName = entry.courseName;
            if (userCoursesMap.has(userId)) {
                userCoursesMap.get(userId).push(courseName);
            } else {
                userCoursesMap.set(userId, [courseName]);
            }
        });
        const userData = Array.from(userCoursesMap).map(([userId, courses]) => ({
            user: userId,
            courses: courses
        }));
        return { userData };
    } catch (error) {
        console.error('Error fetching user data:', error);
        throw error;
    }
}
export const actions = {
    userSelection: async ({ request, locals }) => {
        const formData = await request.formData();
        // const user = Object.fromEntries(formData);
        const user = formData.get("user"); // add course name as well. ""
        try {
            const response = await locals.pb.collection("Orbittest").getFullList({
                filter: `user = "${user}"`, fields: "id, user, url, linkType, linkResult"
            });
            const jsonResponse = JSON.stringify(response);
            console.log("Fetched data:", jsonResponse);
            return { response };
        } catch (error) {
            console.error("Error fetching data:", error);
            return { error };
        }

    },

    update: async ({ request }) => {
        const formData = await request.formData();
        const id = formData.get("id")
        const linkType = formData.get("linkType")
        const linkResult = formData.get("linkResult")
        console.log(id, linkType, linkResult)
        return { status: 200, id }
    }

};

I am getting id, and type for the link I just updated but after that entire page refreshing.

2

There are 2 best solutions below

3
brunnerh On

You can give enhance an argument that is a submit function and if that function returns another function (which is called with the server response), you can override the default behavior. See the docs.

<form
    method="POST"
    use:enhance={({ formElement, formData, action, cancel, submitter }) => {
        // `formElement` is this `<form>` element
        // `formData` is its `FormData` object that's about to be submitted
        // `action` is the URL to which the form is posted
        // calling `cancel()` will prevent the submission
        // `submitter` is the `HTMLElement` that caused the form to be submitted

        return async ({ result, update }) => {
            // `result` is an `ActionResult` object
            // `update` is a function which triggers the default logic
            // that would be triggered if this callback wasn't set
        };
    }}
>
2
Hyunbin On

I don't think the page should reload (full page navigation) on a form submit.

Seems like use:enhance is not working, and would love to see a reproduction.


Suggestions are:

  1. Use URLs to determine what data to display.
  2. Wrap the table with a single <form>. You can set a value in the button.
<script lang="ts">
  import { enhance } from '$app/forms';
  import { goto } from '$app/navigation';

  export let data;
  export let form;

  let linkType: string;
</script>


<select name="" on:change={()=>(goto('desired_url'))}>
  <option value=""></option>
</select>
  

<form action="?/update" method="post" use:enhance>
  <input type="hidden" name="link_type" value={linkType}>
  <table>
    <tbody>
      {#each { length: 3 } as _}
        <tr>
          <button value="{row.id}"></button>
        </tr>
      {/each}
    </tbody>
  </table>
</form>