我有一个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确认对话框?
答案 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
发生的时间有点太晚,无法在那里进行逻辑处理。如果你遇到困难,我可以为你提供工作实例。现在,这是示例代码:
$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;
答案 3 :(得分:1)
如果您想使用状态更改事件,最好在应用程序上应用该事件并使用$ modal服务。你可以在这里找到详细的解释
然而,这提供了一个示例,只要状态发生变化,每个状态更改都有效,但您可以使其适用于单个或指定的状态,如下所示:
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) {
};
}