Carles Company Carles Company - 3 months ago 39
TypeScript Question

$scope.$on('$stateChangeStart') and $modal dialog

I have an AngularJs application that is detecting the change of the state (using ui.router) to present the user with the option to save unsaved changes. Now I'm doing this with a confirm dialog:

$scope.$on('$stateChangeStart', () => {
if (self.changed && confirm('There are unsaved changes. Do you want to save them?'))
this.save();
});


I wanted to change it to using the
$modal
dialog from the bootstrap ui library. The problem I have is that as the
$modal.open()
call returns inmediatelly (being asynchronous) the state changes before opening the dialog and it's never opened.

$scope.$on('$stateChangeStart', () => {
if (self.changed)
this.$dialog.open({...}).result.then(()=>{
this.save();
});
});


Is there a way to overcome this problem or am I stuck to using the plain javascript confirm dialog?

Answer

This is how I solved the problem. In my application, I am using an AppCtrl (parent) to handle navigation, dirty state etc.

    function AppCtrl($rootScope, events, modalDialog) {
       var vm = this,
           handlingUnsavedChanges = false;

       function isDirty() {
            return $rootScope.$broadcast(events.CAN_DEACTIVATE).defaultPrevented;
       }

       function onStateChangeStart(event, toState, toParams) {
            if (handlingUnsavedChanges) {
                // if the dirty state has already been checked then continue with the state change
                return;
            }

            // check for dirty state
            if (isDirty()) {
                // cancel navigation
                event.preventDefault();
                modalDialog
                    .confirmNavigation()
                    .then(function () {
                        // ignore changes
                        handlingUnsavedChanges = true;
                        $state.go(toState.name, toParams);
                    });
            } 
            // Else let the state change
        }

        $rootScope.$on('$stateChangeStart', onStateChangeStart);
    }

<div ng-controller="AppCtrl as app">
   <div ui-view />
</div>

Then you can add an event handler for CAN_DEACTIVATE event in your route controller to check for dirty state, for example

    function UserDetailCtrl($scope, events) {
        function isDirty() {
            // Your logic, return a boolean
        }

        function canDeactivate(e) {
            if (isDirty()) {
                e.preventDefault();
            }
        }

        $scope.$on(events.CAN_DEACTIVATE, canDeactivate);
    }