$ scope。$ on(' $ stateChangeStart')和$ modal对话框

时间:2015-09-15 15:22:04

标签: 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();
});

我想将其更改为使用bootstrap ui库中的$modal对话框。我遇到的问题是,当$modal.open()调用返回inmediatelly(异步)时,状态会在打开对话框之前发生变化,而且它永远不会打开。

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

有没有办法解决这个问题,还是我坚持使用普通的javascript确认对话框?

6 个答案:

答案 0 :(得分:4)

您可以使用ui-router和应用的run部分中的设置完美地完成此操作。

关键部分基本上是关注$rootScope.$on('$stateChangeStart')事件。 我用plunker呈现整个解决方案:http://plnkr.co/edit/RRWvvy?p=preview主要部分在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;
      }
    });
});

答案 1 :(得分:3)

这就是我解决问题的方法。在我的应用程序中,我使用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>

然后,您可以在路由控制器中为CAN_DEACTIVATE事件添加事件处理程序,以检查脏状态,例如

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

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

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

答案 2 :(得分:1)

您应该收听$locationChangeStart事件,如果您有未保存的更改,请执行event.prventDefault()并继续使用ui-bootstrap modal确认对话框的逻辑。事实是,在较新的角度版本中,事件的顺序稍有变化,而$stateChangeStart发生的时间有点太晚,无法在那里进行逻辑处理。如果你遇到困难,我可以为你提供工作实例。现在,这是示例代码:

&#13;
&#13;
$scope.$on('$locationChangeStart', () => {
    if (self.changed) {
       event.preventDefault();
       this.$dialog.open({...}).result.then(()=>{
            this.save();
            // your logic for the redirection if needed after the save
        });
    }
});
&#13;
&#13;
&#13;

答案 3 :(得分:1)

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

http://brewhouse.io/blog/2014/12/09/authentication-made-simple-in-single-page-angularjs-applications.html

然而,这提供了一个示例,只要状态发生变化,每个状态更改都有效,但您可以使其适用于单个或指定的状态,如下所示:

app.run(function ($rootScope) {

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

});

答案 4 :(得分:1)

由于我对使用broadcast不太满意,我使用了以下方法。

我只是在显示我的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');                            
            });                                            
        }
    }});

答案 5 :(得分:-2)

通过以下示例:

$modal.open({
        templateUrl: 'myModalId',
        controller: 'MyModalController',
        size: 'md',
        resolve: {
          params: function () {
            return {code: 'xyz'};
          }
        }
      });

app.module('myApp')
  .controller('MyModalController', MyModalController);

  function MyModalController($scope, $modalInstance, params) {

    $scope.onClose = function () {

      $modalInstance.dismiss('cancel');
    };

    $scope.param = {
      code: params.code,
      reason: ''
    };

    $scope.Save = function (param) {

    };
  }