mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(backbutton): Allow only one back button listener to run per click, closes #693
This commit is contained in:
12
js/ext/angular/src/service/ionicActionSheet.js
vendored
12
js/ext/angular/src/service/ionicActionSheet.js
vendored
@@ -36,18 +36,14 @@ angular.module('ionic.service.actionSheet', ['ionic.service.templateLoad', 'ioni
|
||||
});
|
||||
|
||||
$document[0].body.classList.remove('action-sheet-open');
|
||||
};
|
||||
|
||||
var onHardwareBackButton = function() {
|
||||
hideSheet();
|
||||
scope.$deregisterBackButton && scope.$deregisterBackButton();
|
||||
};
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
$ionicPlatform.offHardwareBackButton(onHardwareBackButton);
|
||||
});
|
||||
|
||||
// Support Android back button to close
|
||||
$ionicPlatform.onHardwareBackButton(onHardwareBackButton);
|
||||
scope.$deregisterBackButton = $ionicPlatform.registerBackButtonAction(function(){
|
||||
hideSheet();
|
||||
}, 300);
|
||||
|
||||
scope.cancel = function() {
|
||||
hideSheet(true);
|
||||
|
||||
22
js/ext/angular/src/service/ionicModal.js
vendored
22
js/ext/angular/src/service/ionicModal.js
vendored
@@ -26,25 +26,13 @@ angular.module('ionic.service.modal', ['ionic.service.templateLoad', 'ionic.serv
|
||||
|
||||
$timeout(function(){
|
||||
element.addClass('ng-enter-active');
|
||||
|
||||
if(!self.didInitEvents) {
|
||||
var onHardwareBackButton = function() {
|
||||
self.hide();
|
||||
};
|
||||
|
||||
self.scope.$on('$destroy', function() {
|
||||
$ionicPlatform.offHardwareBackButton(onHardwareBackButton);
|
||||
});
|
||||
|
||||
// Support Android back button to close
|
||||
$ionicPlatform.onHardwareBackButton(onHardwareBackButton);
|
||||
|
||||
self.didInitEvents = true;
|
||||
}
|
||||
|
||||
self.scope.$parent.$broadcast('modal.shown');
|
||||
}, 20);
|
||||
|
||||
self._deregisterBackButton = $ionicPlatform.registerBackButtonAction(function(){
|
||||
self.hide();
|
||||
}, 200);
|
||||
|
||||
},
|
||||
// Hide the modal
|
||||
hide: function() {
|
||||
@@ -65,6 +53,8 @@ angular.module('ionic.service.modal', ['ionic.service.templateLoad', 'ionic.serv
|
||||
ionic.views.Modal.prototype.hide.call(this);
|
||||
|
||||
this.scope.$parent.$broadcast('modal.hidden');
|
||||
|
||||
this._deregisterBackButton && this._deregisterBackButton();
|
||||
},
|
||||
|
||||
// Remove and destroy the modal scope
|
||||
|
||||
52
js/ext/angular/src/service/ionicPlatform.js
vendored
52
js/ext/angular/src/service/ionicPlatform.js
vendored
@@ -12,7 +12,7 @@ angular.module('ionic.service.platform', [])
|
||||
.provider('$ionicPlatform', function() {
|
||||
|
||||
return {
|
||||
$get: ['$q', function($q) {
|
||||
$get: ['$q', '$rootScope', function($q, $rootScope) {
|
||||
return {
|
||||
/**
|
||||
* Some platforms have hardware back buttons, so this is one way to bind to it.
|
||||
@@ -36,6 +36,54 @@ angular.module('ionic.service.platform', [])
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a hardware back button action. Only one action will execute when
|
||||
* the back button is clicked, so this method decides which of the registered
|
||||
* back button actions has the highest priority. For example, if an actionsheet
|
||||
* is showing, the back button should close the actionsheet, but it should not
|
||||
* also go back a page view or close a modal which may be open.
|
||||
*
|
||||
* @param {function} fn the listener function that was originally bound.
|
||||
* @param {number} priority Only the highest priority will execute.
|
||||
*/
|
||||
registerBackButtonAction: function(fn, priority, actionId) {
|
||||
var self = this;
|
||||
|
||||
if(!self._hasBackButtonHandler) {
|
||||
// add a back button listener if one hasn't been setup yet
|
||||
$rootScope.$backButtonActions = {};
|
||||
self.onHardwareBackButton(self.hardwareBackButtonClick);
|
||||
self._hasBackButtonHandler = true;
|
||||
}
|
||||
|
||||
var action = {
|
||||
id: (actionId ? actionId : ionic.Utils.nextUid()),
|
||||
priority: (priority ? priority : 0),
|
||||
fn: fn
|
||||
};
|
||||
$rootScope.$backButtonActions[action.id] = action;
|
||||
|
||||
// return a function to de-register this back button action
|
||||
return function() {
|
||||
delete $rootScope.$backButtonActions[action.id];
|
||||
};
|
||||
},
|
||||
|
||||
hardwareBackButtonClick: function(e){
|
||||
// loop through all the registered back button actions
|
||||
// and only run the last one of the highest priority
|
||||
var priorityAction, actionId;
|
||||
for(actionId in $rootScope.$backButtonActions) {
|
||||
if(!priorityAction || $rootScope.$backButtonActions[actionId].priority >= priorityAction.priority) {
|
||||
priorityAction = $rootScope.$backButtonActions[actionId];
|
||||
}
|
||||
}
|
||||
if(priorityAction) {
|
||||
priorityAction.fn(e);
|
||||
return priorityAction;
|
||||
}
|
||||
},
|
||||
|
||||
is: function(type) {
|
||||
return ionic.Platform.is(type);
|
||||
},
|
||||
@@ -57,7 +105,7 @@ angular.module('ionic.service.platform', [])
|
||||
};
|
||||
}]
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
})(ionic);
|
||||
|
||||
2
js/ext/angular/src/service/ionicView.js
vendored
2
js/ext/angular/src/service/ionicView.js
vendored
@@ -63,7 +63,7 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
$ionicPlatform.onHardwareBackButton(onHardwareBackButton);
|
||||
$ionicPlatform.registerBackButtonAction(onHardwareBackButton, 100);
|
||||
|
||||
}])
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
describe('Ionic ActionSheet Service', function() {
|
||||
var sheet, timeout;
|
||||
var sheet, timeout, ionicPlatform;
|
||||
|
||||
beforeEach(module('ionic.service.actionSheet'));
|
||||
beforeEach(module('ionic.service.platform'));
|
||||
|
||||
beforeEach(inject(function($ionicActionSheet, $timeout) {
|
||||
beforeEach(inject(function($ionicActionSheet, $timeout, $ionicPlatform) {
|
||||
sheet = $ionicActionSheet;
|
||||
timeout = $timeout;
|
||||
ionicPlatform = $ionicPlatform;
|
||||
}));
|
||||
|
||||
it('Should show', function() {
|
||||
@@ -23,15 +25,10 @@ describe('Ionic ActionSheet Service', function() {
|
||||
expect(wrapper.hasClass('action-sheet-up')).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should handle hardware back button', function() {
|
||||
// Fake cordova
|
||||
window.device = {};
|
||||
ionic.Platform.isReady = true;
|
||||
it('should handle hardware back button', function() {
|
||||
var s = sheet.show();
|
||||
|
||||
ionic.trigger('backbutton', {
|
||||
target: document
|
||||
});
|
||||
ionicPlatform.hardwareBackButtonClick();
|
||||
|
||||
expect(s.el.classList.contains('active')).toBe(false);
|
||||
});
|
||||
@@ -41,9 +38,7 @@ describe('Ionic ActionSheet Service', function() {
|
||||
|
||||
expect(angular.element(document.body).hasClass('action-sheet-open')).toBe(true);
|
||||
|
||||
ionic.trigger('backbutton', {
|
||||
target: document
|
||||
});
|
||||
ionicPlatform.hardwareBackButtonClick();
|
||||
|
||||
expect(angular.element(document.body).hasClass('action-sheet-open')).toBe(false);
|
||||
}));
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
describe('Ionic Modal', function() {
|
||||
var modal, q, timeout;
|
||||
var modal, q, timeout, ionicPlatform, rootScope;
|
||||
|
||||
beforeEach(module('ionic.service.modal'));
|
||||
beforeEach(module('ionic.service.platform'));
|
||||
|
||||
beforeEach(inject(function($ionicModal, $q, $templateCache, $timeout) {
|
||||
beforeEach(inject(function($ionicModal, $q, $templateCache, $timeout, $ionicPlatform, $rootScope) {
|
||||
q = $q;
|
||||
modal = $ionicModal;
|
||||
timeout = $timeout;
|
||||
ionicPlatform = $ionicPlatform;
|
||||
rootScope = $rootScope;
|
||||
|
||||
$templateCache.put('modal.html', '<div class="modal"></div>');
|
||||
}));
|
||||
@@ -87,14 +90,12 @@ describe('Ionic Modal', function() {
|
||||
|
||||
timeout.flush();
|
||||
|
||||
expect(modalInstance.el.classList.contains('active')).toBe(true);
|
||||
expect(modalInstance.isShown()).toBe(true);
|
||||
|
||||
ionic.trigger('backbutton', {
|
||||
target: document
|
||||
});
|
||||
expect( Object.keys(rootScope.$backButtonActions).length ).toEqual(1);
|
||||
|
||||
timeout.flush();
|
||||
expect(modalInstance.el.classList.contains('active')).toBe(false);
|
||||
ionicPlatform.hardwareBackButtonClick();
|
||||
expect(modalInstance.isShown()).toBe(false);
|
||||
});
|
||||
|
||||
it('should broadcast "modal.shown" on show', function() {
|
||||
@@ -105,6 +106,7 @@ describe('Ionic Modal', function() {
|
||||
timeout.flush();
|
||||
expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.shown');
|
||||
});
|
||||
|
||||
it('should broadcast "modal.hidden" on hide', function() {
|
||||
var template = '<div class="modal"></div>';
|
||||
var m = modal.fromTemplate(template, {});
|
||||
@@ -112,6 +114,7 @@ describe('Ionic Modal', function() {
|
||||
m.hide();
|
||||
expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.hidden');
|
||||
});
|
||||
|
||||
it('should broadcast "modal.removed" on remove', inject(function($animate) {
|
||||
var template = '<div class="modal"></div>';
|
||||
var m = modal.fromTemplate(template, {});
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
describe('Ionic Platform Service', function() {
|
||||
var window;
|
||||
var window, ionicPlatform, rootScope;
|
||||
|
||||
beforeEach(inject(function($window) {
|
||||
beforeEach(module('ionic.service.platform'));
|
||||
|
||||
beforeEach(inject(function($window, $ionicPlatform, $rootScope) {
|
||||
window = $window;
|
||||
ionic.Platform.ua = '';
|
||||
ionicPlatform = $ionicPlatform;
|
||||
rootScope = $rootScope;
|
||||
}));
|
||||
|
||||
it('should set platform name', function() {
|
||||
@@ -114,7 +118,7 @@ describe('Ionic Platform Service', function() {
|
||||
window.cordova = {};
|
||||
ionic.Platform.setPlatform('iOS');
|
||||
ionic.Platform.setVersion('7.0.3');
|
||||
|
||||
|
||||
ionic.Platform._checkPlatforms()
|
||||
|
||||
expect(ionic.Platform.platforms[0]).toEqual('cordova');
|
||||
@@ -127,7 +131,7 @@ describe('Ionic Platform Service', function() {
|
||||
window.cordova = {};
|
||||
ionic.Platform.setPlatform('android');
|
||||
ionic.Platform.setVersion('4.2.3');
|
||||
|
||||
|
||||
ionic.Platform._checkPlatforms()
|
||||
|
||||
expect(ionic.Platform.platforms[0]).toEqual('cordova');
|
||||
@@ -243,4 +247,62 @@ describe('Ionic Platform Service', function() {
|
||||
expect(ionic.Platform.is('android')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should register/deregister a hardware back button action and add it to $ionicPlatform.backButtonActions', function() {
|
||||
var deregisterFn = ionicPlatform.registerBackButtonAction(function(){});
|
||||
expect( Object.keys( rootScope.$backButtonActions ).length ).toEqual(1);
|
||||
deregisterFn();
|
||||
expect( Object.keys( rootScope.$backButtonActions ).length ).toEqual(0);
|
||||
});
|
||||
|
||||
it('should register multiple back button actions and only call the highest priority on hardwareBackButtonClick', function() {
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 1, 'action1');
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 2, 'action2');
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 3, 'action3');
|
||||
|
||||
var rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp.priority).toEqual(3);
|
||||
expect(rsp.id).toEqual('action3');
|
||||
});
|
||||
|
||||
it('should register multiple back button actions w/ the same priority and only call the last highest priority on hardwareBackButtonClick', function() {
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 3, 'action1');
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 3, 'action2');
|
||||
ionicPlatform.registerBackButtonAction(function(){}, 3, 'action3');
|
||||
|
||||
var rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp.priority).toEqual(3);
|
||||
expect(rsp.id).toEqual('action3');
|
||||
});
|
||||
|
||||
it('should register no back button actions and do nothing on hardwareBackButtonClick', function() {
|
||||
var rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should register multiple back button actions, call hardwareBackButtonClick, deregister, and call hardwareBackButtonClick again', function() {
|
||||
var dereg1 = ionicPlatform.registerBackButtonAction(function(){}, 1, 'action1');
|
||||
var dereg2 = ionicPlatform.registerBackButtonAction(function(){}, 2, 'action2');
|
||||
var dereg3 = ionicPlatform.registerBackButtonAction(function(){}, 3, 'action3');
|
||||
|
||||
var rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp.priority).toEqual(3);
|
||||
expect(rsp.id).toEqual('action3');
|
||||
|
||||
dereg3();
|
||||
|
||||
rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp.priority).toEqual(2);
|
||||
expect(rsp.id).toEqual('action2');
|
||||
|
||||
dereg2();
|
||||
|
||||
rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp.priority).toEqual(1);
|
||||
expect(rsp.id).toEqual('action1');
|
||||
|
||||
dereg1();
|
||||
rsp = ionicPlatform.hardwareBackButtonClick();
|
||||
expect(rsp).toBeUndefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user