0

I'm working on a project using Sveltekit for the frontend. To display on screen alert messages I had created the following.

In +layout.svelte (the main layout file) I have:

<!-- Main content -->
            <main class="items-center min-h-screen justify-center w-full pt-12 p-12 border-accent-content/5 bg-gray-100">
                <Alert />
                <slot/>
            </main>

Notice the <Alert /> component.

The <Alert /> component looks like:

<script>

    import { ALERT_TYPE, alertMessage, alertType } from "../stores/alert.js";
    import { fade } from 'svelte/transition';

    // computed property to display the pertinent icon depending on the type of alert.
    $: alertIcon = () => {
        switch ($alertType) {
            case ALERT_TYPE.SUCCESS:
                return `<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24" style="display: inline" ><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"  /></svg>`
            case ALERT_TYPE.ERROR:
                return `<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24" style="display: inline"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>`
            case ALERT_TYPE.WARNING:
                return `<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24" style="display: inline"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>`
            case ALERT_TYPE.INFO:
            default:
                return `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current flex-shrink-0 w-6 h-6" style="display: inline"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`
        }
    }

</script>

<!-- Display the alert only if there's a message to be displayed -->
{#if ($alertMessage) }
    <div class="alert alert-{$alertType} shadow-lg mb-8" transition:fade>
        <div>
            { @html alertIcon() }
            <span>{ @html $alertMessage }</span>
        </div>
    </div>
{/if}

And I've created a store so I can access whatever value is stored in the store from my layout file:

import { writable } from "svelte/store";

export const ALERT_TYPE = {
    ERROR: 'error',
    INFO: 'info',
    SUCCESS: 'success',
    WARNING: 'warning',
};

Object.freeze(ALERT_TYPE);

export const alertMessage = writable('');
export const alertType = writable('');

/**
 * Displays a new alert message by setting up the alert values in the store.
 * @param message
 * @param type
 * @param resetTimeInSecs
 */
export const displayAlert = (message, type = ALERT_TYPE.INFO, resetTimeInSecs=5) => {
    alertMessage.set(message);
    alertType.set(type);

    if (resetTimeInSecs) {
        setTimeout(() => {
            alertMessage.set("");
        }, resetTimeInSecs * 1000);
    }
}

This works just fine when I'm displaying the message and stay in the same page where it was triggered, but it works a bit funky when I redirect to another page.

Let's say I have a page such as the following:

<script>
    import CategoryForm from "$lib/forms/CategoryForm.svelte";
    import {ALERT_TYPE, displayAlert} from "../../../lib/stores/alert.js";
    import {browser} from "$app/environment";

    /* data returned from load function in +page.server.js */
    export let data;

    /* data returned from actions function in +page.server.js */
    export let form;

    if (data.errors) {
        if (browser) { // to prevent error window is not defined, because it's SSR
            displayAlert(data.errors, ALERT_TYPE.ERROR, 10);
            window.location.href = '/categories';
        }
    }

</script>

<CategoryForm formData={ {...data, ...form}} loadedData={data} mode="edit" />

In this page, whenever errors are returned from the server in data I want to redirect to /categories and show the error alert message. It works as it is now, but I see the message in the current form before I'm redirected and that simply looks ugly. I want the message to be visible only in /categories.

Is there a better way to do this? I've got two possible solutions but none of them seem very elegant nor the right way to do it for me, so I'm looking for other suggestions.

1 Use setTimeout. That way I can give some time for the redirect to happen. 2 Use an extra parameter in displayAlert. This parameter would be optional and would indicate the page where the alert is allowed to be displayed. In this specific case that parameter would be /categories. I can take the url object and get the current location, if the location matches that of the one passed in the param then go ahead and show the alert.

Any suggestions?

2 Answers 2

1

sveltekit-flash-message might help:

...passes temporary data to the next request, usually from form actions and endpoints. It's useful when you want a success or failure message displayed after a POST, which should not always be displayed at the form, rather as a message on the page that the request was redirected to.

You can either use this library or use the same technique of adding data to the next request.


Otherwise, I would:

  1. Add the alert content as optional URL query params. For example:
const params = new URLSearchParams({
    alertMessage: data.errors,
    alertType: ALERT_TYPE.ERROR,
    alertResetTime: 10
})
window.location.href = '/categories?' + params.toString()
  1. If the /categories route has those optional query params then render your alert.
0

You generally should not redirect using window.location.href. Instead call goto, that does a client-side redirect without fully reloading the page (unless the application has been updated). That should preserve any layouts that remain on the new page and thus the message.

Another option would be to temporarily store the data on the server, which requires that requests are associated with a session, usually via a session cookie.

Similarly you could also store the message on the client, e.g. in sessionStorage, then when a page loads you can read any messages from there.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.