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?