I have an Angular 7 app that uses UI-Router for routing, which has an Angular reactive form users fill out. How do I prevent the user from navigating away when this form has unsaved changes?

In Angular Router, there's the concept of guards you can use to check the component in a view for a 'unsavedChanges' boolean and confirm that the user wants to navigate away. UI-Router doesn't use guards, but instead uses 'hooks' that run before and after a state is loaded. These hooks return promises, and if the promise returns false, the transition doesn't happen.

Is there a good way in Angular 7 / UI-Router to check whether a form in a component has unsaved changes, and if so prevent navigation (both internal router and externally)? I've got a couple theories on how I could do this, but there might be a better way:

  1. Check an UnsavedChangesService to see if we've set an unsavedChanges boolean to true. Problem: I can't figure out how to get a reference to an arbitrary service from inside the UI-Router hook.
  2. Check the $default component specified in a state definition for an unsavedChanges boolean. Problem: I can't figure out how to get a reference to the component instance from inside the UI-Router hook.

Is there a good way to do this? It seems like there's lots of solutions for Angular Router, but all the suggestions for UI-Router are for old-school AngularJS.

Here's a sample hook I've been playing around with, trying to get access to some kind of reference to either the component or a service. I might not even be on the right path.

        export function unsavedChangesHook(transitionService: TransitionService) {
        const criteria = { to: (state) => state.data && state.data.checkUnsavedChanges };

        const callback = async (transition: Transition) => {

            // Pseudo-function where we would find out the form is dirty
            const formDirty = formIsDirty();

            if (formDirty) {
                const ok = confirm('Navigate away? All changes will be lost.');

                if (!ok) {
                    return false;
                }
            }

            return true;
        };

        transitionService.onBefore(criteria, callback, { priority: 10 });

    }

0 Answers