Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confirm before leaving page? #288

Open
ariroffe opened this issue Dec 19, 2022 · 6 comments
Open

Confirm before leaving page? #288

ariroffe opened this issue Dec 19, 2022 · 6 comments

Comments

@ariroffe
Copy link

Hi, thank you for creating and maintaining this package!

I need to show a confirmation dialog before a user leaves a specific route, and call a function if she decides to leave. I can use window.onbeforeunload to handle cases where, for example, the user just navigates to a diferent site.

But beforeunload does not fire if the user manually enters a different route within the same spa (one that is still prefixed with #) in the address bar, or, e.g., if she presses the back button.

I've tried asking for confirmation in the route's associated component onDestroy method, but I couldn't find a way to stop the component's destruction afterwards. Even if I raise an error, the component remains on the page but the url still changes.

Is there any way of doing this with svelte-spa-router?

@ItalyPaleAle
Copy link
Owner

I believe you should be able to do that with a route pre-condition: https://github.com/ItalyPaleAle/svelte-spa-router/blob/master/Advanced%20Usage.md#route-pre-conditions

@davidd7
Copy link

davidd7 commented Jan 5, 2023

Hi, I'm also currently trying to implement a confirm dialog with pre-conditions. A problem I came across thereby is that an empty page is loaded when the pre-conditions fail. To circumvent this, I've tried to use on:conditionsFailed to manually set the location back to the original page (using replace or pop), but this leads to the component being reloaded, which is a problem in my use case. Would you happen to know if there's a way to stay on the same page and not reload it when a pre-condition fails?

@ItalyPaleAle
Copy link
Owner

I don't believe that's possible at this time. Perhaps it could be done by creating another hook that is executed before the route is un-mounted.

@davidd7
Copy link

davidd7 commented Jan 6, 2023

Thank you very much for your reply. You mean kinda like an exit-conditions counterpart to pre-conditions that would check registered conditions before leaving a route?

I can try to implement such a functionality analogously to how pre-conditions are implemented, if you think that that would be a good addition. :)

@afreidz
Copy link

afreidz commented Jun 26, 2023

plus one for this. looking to have a user confirm exiting with unsaved data before navigation. reloading the existing route on failure of a route guard would cause the form state to be lost. 😞 @davidd7 did you ever find a workaround?

@afreidz
Copy link

afreidz commented Jun 27, 2023

The workaround that I eventually landed on for now:

<script lang="ts">
  import { get } from "svelte/store";
  import { link, location } from "svelte-spa-router";
  import { confirmNavigation, dirty } from "@/lib/stores/ui";

  function checkLink(node: HTMLAnchorElement, params: any) {
    node.addEventListener("click", check);

    async function check(e: MouseEvent) {
      if (get(dirty)) {
        e.preventDefault();
        e.stopImmediatePropagation();
        const response = await new Promise((r) => {
          confirmNavigation.set(r);
        });
        confirmNavigation.set(undefined);
        if (response) {
          dirty.set(false);
          window.location.hash = node.getAttribute("href") || "";
        }
      }
    }

    const { update } = link(node, params);

    return {
      update,
      destroy() {
        node.removeEventListener("click", check);
      },
    };
  }
</script>

<a ... use:checkLink>
  <slot/>
</a>

This is a global link component with a use directive that bails out on certain conditions ... it runs the svelte-spa-router link directive to maintain functionality. It then sets a writable with the resolve method of a promise. if that promise resolves to true it tries to mimic the functionality of the link directive (which is a fancy way of saying that it updates the hash) save for the scroll linking which was not needed in my case. if it resolves to false it just clears the writable value and exits. elsewhere in the app, like a confirmation dialog, the writable can be called thusly:

$confirmNavigation?.(true | false);

Hope this helps someone looking for a workaround to this! I explored the option suggested above and realized that an exit route guard would be tricky because the hash updates independently of the route logic. so it could get messy figuring out if the outgoing route has exit conditions that fail and reverting the hash. This seems easier for now.

Other considerations:

  • this won't account for programmatic routing. but i suspect that push/replace/pop/etc could be shimmed in a similar manner, i just don't have that use case.
  • it also isnt true onbeforeunload behavior, as browser native navigations would circumvent this check. but i suspect that would be trickier to implement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants