【问题标题】:$scope.$on('$stateChangeStart') and $modal dialog$scope.$on('$stateChangeStart') 和 $modal 对话框
【发布时间】:2015-09-15 15:22:03
【问题描述】:

我有一个 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 确认对话框?

【问题讨论】:

  • 是什么触发了状态变化?可能需要抓住它,甚至不进入改变
  • 可能是 ui-sref 链接或通过 $state.go 手动更改状态。无论出于何种原因,我都想检测用户何时退出该状态。
  • 我唯一能想到的就是根据可能存储在 $rootScope 或服务中的标志来阻止事件,并使用模式事件切换标志。如果全部通过$state.go()会简单得多

标签: angularjs twitter-bootstrap dialog typescript angular-ui-router


【解决方案1】:

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

【讨论】:

  • @CarlesCompany,@AmanMahajan,我在下面添加我的答案版本,因为此时我对使用broadcast 不太满意。请建议我的方法是否正确。
  • @AmanMahajan,我在服务中添加了stateChangeStart 监听器。可以在任何地方添加对吗?
【解决方案2】:

您可以使用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;
      }
    });
});

【讨论】:

    【解决方案3】:

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

    $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
            });
        }
    });

    【讨论】:

      【解决方案4】:

      如果您想使用状态更改事件,最好将事件应用到应用程序并使用 $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
        }
        });
      
      });
      

      【讨论】:

        【解决方案5】:

        由于我不太习惯使用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');                            
                    });                                            
                }
            }});
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-26
          • 1970-01-01
          • 2017-11-29
          • 2011-12-14
          • 1970-01-01
          相关资源
          最近更新 更多