How can I enhance the type safety of page form storage in SvelteKit?

My HTML code is organized into two separate files, +page.svelte and Role.svelte. This division makes it easier to manage and maintain as I continue adding more svelte files to +page.svelte in the future. The actual form is located within the Role.svelte file.

After some research, I discovered that since the form variable is only passed to +page.svelte and not to any components, using $page.form directly in Role.svelte instead of declaring a separate variable like let form: ActionData is recommended. However, I noticed that $page.form does not provide type safety or inference like ActionData. How can I ensure that $page.form has the same type inference as let form: ActionData while still maintaining my current file organization?

(The following is the code):

Role.svelte

<script lang="ts">
  import { enhance } from "$app/forms";
  import { page } from "$app/stores";
  import type { ActionData } from "./$types";
  let form: ActionData = $page.form // attempted but returned undefined
  form = form // had no effect
  let userRoles = ["admin", "student"];
</script>

<form method="post" action="?/role" use:enhance>
  <!-- unable to work, had to resort to using $page.form which lacks typing -->
  {#if form?.noSelectedRole}
    <p>{form?.error}</p>
  {/if}

  <p>You are a/an...</p>
  {#each userRoles as userRole}
    <input type="radio" value={userRole} name="role" />
    <label for={userRole}>{userRole}</label>
    <br />
  {/each}

  <slot />
  <!-- For when the school can't be found -->

  <p>What school are you a part of? Type its DepEd school ID.</p>
  <input type="text" name="schoolId" placeholder="School" />
  <br />
  <button type="submit">Check</button>
</form>

+page.svelte

<script lang="ts">
  import { page } from "$app/stores";
  import type { ActionData } from "./$types";
  import Role from "./Role.svelte";
  let form: ActionData = $page.form // did not produce desired results
</script>

<Role>
  <p>
    <!-- undefined result, had to rely on $page.form without proper typing :( -->
    {console.log(form?.noSelectedRole)}
    {#if form?.noSelectedRole}
      Select a role please.
    {/if}
  </p>
</Role>

+page.server.ts

import type { Actions } from "./$types";
import { fail, redirect } from '@sveltejs/kit';

export const actions = {
    role: async ({ request }) => {
        const formData = await request.formData()
        const userRole = formData.get("role") as string
        const schoolId = formData.get("schoolId") as string

        console.log(`role: ${userRole}`)
        console.log(`school id: ${schoolId}`)

        if (typeof userRole !== "string" ||
            userRole == null) {
            console.log("no user role selected")
            return fail(400, {
                noSelectedRole: true,
                error: "Please select a role.",
                data: {...{userRole, schoolId}}
            })
        }
    }
} satisfies Actions;

Answer №1

Your code is on the right path, but there are some subtle bugs that need fixing.

// <Role.svelte>
import type { ActionData } from "./$types";
let form: ActionData = $page.form

When you initially create the component, the variable form is set to undefined as there is no form data yet. Even after submitting the form and changing $page.form, your form variable does not update because SvelteKit does not recreate your Role component, instead it reuses it. More information here.

To resolve this issue, you need to make your form variable reactive:

// <Role.svelte>
import type { ActionData } from "./$types";
$: form = $page.form as ActionData

There is another problem with your code, specifically inside +page.svelte:

// <+page.svelte>
import type { ActionData } from "./$types";
let form: ActionData = $page.form;

Two corrections needed: you do not require the assignment, and you must export the variable. It should be like this:

// <+page.svelte>
import type { ActionData } from "./$types";
export let form: ActionData;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the possibilities of developing WebComponents within Angular using TypeScript

My Angular application was originally created using the default method with ng new project-name". However, for performance reasons, I had to incorporate single standard WebComponents. The JavaScript code associated with these components is stored in a ...

Setting the TypeScript version while initializing CDK

When creating a new CDK app in typescript, I typically use the following command: npx --yes <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d9babdb299e8f7e8eae1f7eb">[email protected]</a> init app --language typesc ...

After running a yarn build on a TypeScript project using a .projenrc.js file, make sure to include any packaged or additional text files in the lib folder, rather

After utilizing projen to create my typescript project, I followed these steps: mkdir my-project, git init, and npx projen new typescript. Additionally, I created two files - sample.txt and sample.js, along with the default file index.ts within the folder ...

Attempting to execute a synchronous delete operation in Angular 6 upon the browser closing event, specifically the beforeunload or unload event

Is there a way to update a flag in the database using a service call (Delete method) when the user closes the browser? I have tried detecting browser close actions using the onbeforeunload and onunload events, but asynchronous calls do not consistently wor ...

Display sub-objects within Chart.js

I'm currently tackling a project in Ionic 3 where I am utilizing an API that returns a JSON response with nested objects. My goal is to display certain aspects of these objects within a bar graph using chart.js. Unfortunately, I lack experience in ma ...

Is the naming convention for parameterized types (T, U, V, W) in Generics adhered to by Typescript?

Is TypeScript following the same naming convention for parameterized types as other languages like C++ and Java, using T,U,V,W, or is there a mixed usage of conventions? In the TS 2.8 release notes, we see examples like: type ReturnType<T> = T exten ...

Tips for updating the text content of an HTML input element during a unit test

I am currently writing unit tests for an Angular application and I am attempting to set the text content of an input element using a unit test written with Jasmine. <input type="text" id="accountid" class="form-control col-sm-3" [(ngModel)]="record.acc ...

Validating React components with TypeScript using an array structure where the field name serves as the key

Trying to implement form validation with React. I have a main Controller that contains the model and manages the validation process. The model is passed down to child controllers along with the validation errors. I am looking for a way to create an array ...

Tips for sending a file rather than a json object in nextjs

Is there a way to send a file from either route.ts or page.ts, regardless of its location in the file-system? Currently, I am using the following code in my back-end python + flask... @app.route("/thumbnail/<string:filename>") def get_file ...

Setting up @cypress/code-coverage with Angular and TypeScript: A comprehensive guide

When following the instructions in Cypress documentation for @cypress/code-coverage, it recommends using the following code... // cypress/support/e2e.js import '@cypress/code-coverage/support' ...as well as... // cypress.config.ts import { defin ...

How does the type of the original array influence the inferred types of the destructured array values?

let arr = [7, "hello", true]; let [a, ...bc] = arr; typeof bc : (string | number | boolean)[] why bc type is (string | number | boolean) expect: because bc = ["hello", true], so bc type should be (string | boolean)[] ...

Exploring the differences between Angular's @Input and @Output directives and utilizing Injectable Services

When considering the differences between @Input/@Output in parent and child components versus using services that are instantiated only once with dependency injection (@Injectable()), I find myself questioning whether there are any distinctions beyond the ...

How to handle a Node.js promise that times out if execution is not finished within a specified timeframe

return await new Promise(function (resolve, reject) { //some work goes here resolve(true) }); Using Delayed Timeout return await new Promise(function (resolve, reject) { //some work goes here setTimeout(function() { resolve(true); }, 5000); } ...

Accessing the ViewModel property of a parent component from the ViewModel of its child in Aurelia

Having a scenario with two distinct components: <parent-component type="permanent"> <div child-component></div> </parent-component> class ParentComponentCustomElement { @bindable public type: string = "permanent"; } clas ...

Utilize Angular roles to sort and organize website data

Exploring the utilization of login roles in my Angular SPA application which operates solely on the client side, integrated with Spring Security and Spring Boot. I have concerns about potential unauthorized access by a skilled developer who could manipula ...

What is the reason behind receiving the error message "`Foo` only represents a type, but is being treated as a value here" when using `instanceof` in TypeScript?

As the creator of this code interface Foo { abcdef: number; } let x: Foo | string; if (x instanceof Foo) { // ... } Upon running this code, I encountered an error in TypeScript: 'Foo' is recognized only as a type and cannot be used a ...

The onNodeContextMenuSelect function does not seem to be functioning properly within the p-tree

<p-tree [value]="files" selectionMode="single" (onNodeContextMenuSelect)="showContect($event)" > </p-tree> Whenever I right click, the event doesn't seem to be triggering. Instead, the default browser c ...

When Typescript calls the toString method on a Function, it produces unexpected characters like "path_1, (0, promises.writeFile)"

I'm currently attempting to convert a function into a string for transmission to a worker thread for execution. However, when imported code is included, the resulting string contains strange characters. import { HttpStatus } from '@nestjs/common& ...

How to Establish an Angular Global Variable

What is the process for establishing a global variable in Angular? I have established a variable within a service, entered a value in the login component, and attempted to access this variable from another component. However, I noticed that the value res ...

Merge the inverse property and inverse class selector with the current selector for enhanced specificity

I was looking at this jQuery code snippet: $("#Filter ul input:checked").each(function () { if (!$(this).prop("disabled") && !$(this).hasClass("ignoreInput")) { Is there a way to simplify this into just one selector? It seems like I'm ha ...