Angularjs $scope.$on(';$stateChangeStart';)和$modal对话框

Angularjs $scope.$on(';$stateChangeStart';)和$modal对话框,angularjs,twitter-bootstrap,dialog,typescript,angular-ui-router,Angularjs,Twitter Bootstrap,Dialog,Typescript,Angular Ui Router,我有一个AngularJs应用程序,它正在检测状态的变化(使用ui.router),向用户提供保存未保存的变化的选项。现在,我通过确认对话框执行此操作: $scope.$on('$stateChangeStart', () => { if (self.changed && confirm('There are unsaved changes. Do you want to save them?')) this.save(); }); 我想将其更改为

我有一个AngularJs应用程序,它正在检测状态的变化(使用ui.router),向用户提供保存未保存的变化的选项。现在,我通过确认对话框执行此操作:

$scope.$on('$stateChangeStart', () => {
    if (self.changed && confirm('There are unsaved changes. Do you want to save them?'))
        this.save();
});
我想将其更改为使用bootstrap ui库中的
$modal
对话框。我遇到的问题是,
$modal.open()
调用会立即返回(异步),因此在打开对话框之前状态会发生变化,而对话框从未打开过

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

有没有办法克服这个问题,或者我一直坚持使用普通的javascript确认对话框?

您应该收听
$locationChangeStart
事件,如果您有未保存的更改,则执行
event.prventDefault()
并继续使用
ui引导模式的确认对话框的逻辑。问题是,在较新的angular版本中,事件的顺序略有改变,
$stateChangeStart
发生得有点晚,无法在那里执行逻辑。如果你陷入困境,我可以为你提供一个有效的例子。现在,以下是示例代码:

$scope.$on(“$locationChangeStart”,()=>{
如果(自我改变){
event.preventDefault();
这个.dialog.open({…}).result.then(()=>{
这是save();
//如果需要,请在保存后选择重定向逻辑
});
}

});如果要处理状态更改事件,最好在应用程序上应用该事件并使用$modal服务。你可以在这里找到详细的解释

但是,这给出了一个在状态发生变化时适用于每个状态变化的示例,但您可以使其适用于单个或指定的状态,如下所示:

app.run(function ($rootScope) {

  $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
  if(toState.name =="desiredState") {
  event.preventDefault();
  // perform your task here
  }
  });

});

这就是我解决问题的方法。在我的应用程序中,我使用AppCtrl(父级)来处理导航、脏状态等

    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>

使用应用程序的
ui路由器
run
部分中的设置,您可以做得非常好。

关键部分基本上是观察(“$stateChangeStart”)上的
$rootScope.$on
事件。 我用plunker展示了整个解决方案:主要部分在
scipt.js
中,如下所示:

routerApp.run(function($rootScope, $uibModal, $state) {
  $rootScope.modal = false; // this has to be set so the modal is not loaded constantly, and skips the 1st application entrance

  $rootScope.$on('$stateChangeStart',
    function(event, toState, toParams, fromState, fromParams) {

      if ($rootScope.modal) {
        event.preventDefault(); //this stops the transition
        $rootScope.modal = false;
        var modalInstance = $uibModal.open({
          templateUrl: 'modal.html'
        });

        modalInstance.result.then(function(selectedItem) {
          console.log('changing state to:'+ toState.name);
          $state.go(toState, {}, {reload:true});
        }, function() {
          console.log('going back to state:'+ fromState.name);
          $state.go(fromState, {}, {reload:true});
        });
      } else {
        $rootScope.modal = true;
      }
    });
});

因为我不太习惯使用
广播
,所以我使用了下面的方法

在显示我的
JQuery
对话框之前,我只需取消当前事件(状态更改)。若用户选择“是”,则我触发
$state.go
,若选择“取消/否”-我们不需要做任何事情,我们已经取消了事件

$rootScope.$on('$stateChangeStart',function (event, toState, toParams, fromState, fromParams, options) {

    console.log('$stateChangeStart- fromState= '+fromState.name +'-toState= '+toState.name);

    /*If user starts to change state, show him a confirmation dialog
     * If user selects 'Yes' - continue with state change and go in pause mode for collection.
     * If user selects 'No'- stop state change.
     */
    if (/*some condition*/) {                    


        /*stateChangeStart gets triggered again by $state.go initiated from our modal dialog-
        hence avoid extra cycle of listening to stateChangeStart*/
        if (service.stateChangeTriggeredByDialog) {
            service.stateChangeTriggeredByDialog = false;
            return;
        }

        if (fromParams.paramName !== toParams.paramName) {
            event.preventDefault();                        

            Dialog.confirm({
                dialogClass: 'my-customDialog',
                template: $translate.instant('STATE_CHANGE_MSG'),
                resizable: false,
                width: 550                            
            }).then(function () {   
                console.log('User choose to continue state change');  
                service.stateChangeTriggeredByDialog = true;
                $state.go(toState, toParams, { inherit: false });
            }, function () {
                console.log('User choose to stop state change');                            
            });                                            
        }
    }});

是什么触发了状态变化?可能需要在那里捕获它,甚至不需要输入更改它可能是ui sref链接或通过
$state.go
手动更改状态。我想检测用户何时退出该状态,无论原因是什么。我唯一能想到的是基于$rootScope或服务中存储的标志阻止事件,并使用模式事件切换标志。如果一切顺利的话,
$state.go()
将非常重要simpler@CarlesCompany,@AmanMahajan,我在下面添加我的答案版本,因为我现在不太习惯使用
广播。请建议我的方法是否正确。@AmanMahajan,我在我的服务中添加了
stateChangeStart
listener。可以在任何地方添加,对吗?
$rootScope.$on('$stateChangeStart',function (event, toState, toParams, fromState, fromParams, options) {

    console.log('$stateChangeStart- fromState= '+fromState.name +'-toState= '+toState.name);

    /*If user starts to change state, show him a confirmation dialog
     * If user selects 'Yes' - continue with state change and go in pause mode for collection.
     * If user selects 'No'- stop state change.
     */
    if (/*some condition*/) {                    


        /*stateChangeStart gets triggered again by $state.go initiated from our modal dialog-
        hence avoid extra cycle of listening to stateChangeStart*/
        if (service.stateChangeTriggeredByDialog) {
            service.stateChangeTriggeredByDialog = false;
            return;
        }

        if (fromParams.paramName !== toParams.paramName) {
            event.preventDefault();                        

            Dialog.confirm({
                dialogClass: 'my-customDialog',
                template: $translate.instant('STATE_CHANGE_MSG'),
                resizable: false,
                width: 550                            
            }).then(function () {   
                console.log('User choose to continue state change');  
                service.stateChangeTriggeredByDialog = true;
                $state.go(toState, toParams, { inherit: false });
            }, function () {
                console.log('User choose to stop state change');                            
            });                                            
        }
    }});