diff --git a/js/ext/angular/src/controller/ionicScrollController.js b/js/ext/angular/src/controller/ionicScrollController.js
index 1318628f92..fd0b066069 100644
--- a/js/ext/angular/src/controller/ionicScrollController.js
+++ b/js/ext/angular/src/controller/ionicScrollController.js
@@ -6,7 +6,7 @@ angular.module('ionic.ui.scroll')
/**
* @private
*/
-.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate', '$window', '$ionicViewService', function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate, $window, $ionicViewService) {
+.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate', '$window', function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate, $window) {
var self = this;
@@ -34,13 +34,27 @@ angular.module('ionic.ui.scroll')
var resize = angular.bind(scrollView, scrollView.resize);
$window.addEventListener('resize', resize);
+ $scope.$on('$viewContentLoaded', function(e, historyData) {
+ if (e.defaultPrevented) {
+ return;
+ }
+ //only the top-most scroll area under a view should remember that view's
+ //scroll position
+ e.preventDefault();
+
+ var values = historyData && historyData.rememberedScrollValues;
+ if (values) {
+ $timeout(function() {
+ scrollView.scrollTo(+values.left || null, +values.top || null);
+ }, 0, false);
+ }
+ $scope.$on('$destroy', function() {
+ historyData && (historyData.rememberedScrollValues = scrollView.getValues());
+ });
+ });
+
$scope.$on('$destroy', function() {
$window.removeEventListener('resize', resize);
-
- var view = $ionicViewService.getCurrentView();
- if (view) {
- view.rememberedScrollValues = scrollView.getValues();
- }
});
this.setRefresher = function(refresherScope, refresherElement) {
diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js
index 2b017ebfe4..5d1625ee0f 100644
--- a/js/ext/angular/src/directive/ionicContent.js
+++ b/js/ext/angular/src/directive/ionicContent.js
@@ -61,10 +61,9 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
.directive('ionContent', [
'$parse',
'$timeout',
- '$ionicScrollDelegate',
'$controller',
'$ionicBind',
-function($parse, $timeout, $ionicScrollDelegate, $controller, $ionicBind) {
+function($parse, $timeout, $controller, $ionicBind) {
return {
restrict: 'E',
replace: true,
@@ -135,9 +134,6 @@ function($parse, $timeout, $ionicScrollDelegate, $controller, $ionicBind) {
});
//Publish scrollView to parent so children can access it
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
-
- var delegate = $ionicScrollDelegate($scope);
- delegate.rememberScrollPosition();
}
transclude($scope, function(clone) {
diff --git a/js/ext/angular/src/service/delegates/ionicScrollDelegate.js b/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
index 7d2cd8747c..f55f6de847 100644
--- a/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
+++ b/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
@@ -14,10 +14,17 @@ angular.module('ionic.ui.service.scrollDelegate', [])
* directive).
*
* Inject it into a controller, create a new instance based upon the current scope,
- * and its methods will send messages to the nearest scrollView and all of
- * its children.
+ * and its methods will send messages to the nearest scrollView and its children.
*
* @usage
+ * ```js
+ * function MyController($scope, $ionicScrollDelegate) {
+ * var delegate = $ionicScrollDelegate($scope);
+ * $scope.scrollToTop = function() {
+ * delegate.scrollTop();
+ * };
+ * }
+ * ```
* ```html
*
*
*
* ```
- * ```js
- * function MyController($scope, $ionicScrollDelegate) {
- * $scope.scrollToTop = function() {
- * var delegate = $ionicScrollDelegate($scope);
- * delegate.scrollTop();
- * };
- * }
- * ```
*/
.factory('$ionicScrollDelegate', ['$rootScope', '$timeout', '$location', '$ionicViewService', function($rootScope, $timeout, $location, $ionicViewService) {
+ //Exposed for testing
+ var rememberedScrollValues = ionicScrollDelegate._rememberedScrollValues = {};
function getScrollCtrl($scope) {
var ctrl;
@@ -55,7 +56,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#scrollTop
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
scrollTop: function(animate) {
@@ -64,7 +65,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#scrollBottom
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
scrollBottom: function(animate) {
@@ -73,7 +74,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#scroll
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
* @param {number} left The x-value to scroll to.
* @param {number} top The y-value to scroll to.
* @param {boolean=} shouldAnimate Whether the scroll should animate.
@@ -84,7 +85,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#anchorScroll
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
*
* Tell the scrollView to scroll to the element with an id
* matching window.location.hash.
@@ -99,7 +100,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#resize
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
*
* Tell the scrollView to recalculate the size of its container.
*/
@@ -134,13 +135,36 @@ angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc method
* @name $ionicScrollDelegate#rememberScrollPosition
- * @description Used on an instance of $ionicScrollDelegate.
+ * @description
*
- * If this scroll area is associated with a view in the history,
- * load the last scroll position from the last time this view was shown.
+ * When this scroll area is destroyed, its last scroll position will be
+ * saved using the given id.
+ *
+ * @param {string} id The identifier for this saved scroll position.
*/
- rememberScrollPosition: function(animate) {
- scrollScope.$broadcast('scroll.rememberPosition', !!animate);
+ rememberScrollPosition: function(id) {
+ if (!id) {
+ throw new Error("Must supply a unique id!");
+ }
+ scrollScope.$broadcast('scroll.rememberPosition', id);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $ionicScrollDelegate#scrollToRememberedPosition
+ * @description
+ *
+ * If a scroll position was remembered using the given id, loads the
+ * remembered scroll position and scrolls there.
+ *
+ * @param {string} id The identifier for this saved scroll position.
+ * @param {boolean=} shouldAnimate Whether to animate the scroll.
+ */
+ scrollToRememberedPosition: function(id, animate) {
+ if (!id) {
+ throw new Error("Must supply a unique id!");
+ }
+ scrollScope.$broadcast('scroll.scrollToRememberedPosition', id, !!animate);
},
/**
@@ -216,14 +240,24 @@ angular.module('ionic.ui.service.scrollDelegate', [])
}
});
});
- $scope.$on('scroll.rememberPosition', function(e, animate) {
- scrollViewResize().then(function() {
- var view = $ionicViewService.getCurrentView();
- var values = view && view.rememberedScrollValues;
- if (view && values) {
+
+ var rememberScrollId;
+ $scope.$on('scroll.rememberPosition', function(e, id) {
+ rememberScrollId = id;
+ });
+ $scope.$on('$destroy', function() {
+ if (rememberScrollId) {
+ rememberedScrollValues[rememberScrollId] = scrollView.getValues();
+ }
+ });
+
+ $scope.$on('scroll.scrollToRememberedPosition', function(e, id, animate) {
+ var values = rememberedScrollValues[id];
+ if (values) {
+ scrollViewResize().then(function() {
scrollView.scrollTo(+values.left || null, +values.top || null, animate);
- }
- });
+ });
+ }
});
};
diff --git a/js/ext/angular/src/service/ionicView.js b/js/ext/angular/src/service/ionicView.js
index a5d0fb7de0..e1fabea1c6 100644
--- a/js/ext/angular/src/service/ionicView.js
+++ b/js/ext/angular/src/service/ionicView.js
@@ -157,7 +157,9 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
// they went back one, set the old current view as a forward view
rsp.viewId = backView.viewId;
rsp.navAction = 'moveBack';
- currentView.scrollValues = {}; //when going back, erase scrollValues
+ rsp.viewId = backView.viewId;
+ //when going back, erase scrollValues
+ currentView.rememberedScrollValues = {};
if(backView.historyId === currentView.historyId) {
// went back in the same history
rsp.navDirection = 'back';
diff --git a/js/ext/angular/test/controller/ionicScrollController.unit.js b/js/ext/angular/test/controller/ionicScrollController.unit.js
index eee1f0e60e..3e0682319c 100644
--- a/js/ext/angular/test/controller/ionicScrollController.unit.js
+++ b/js/ext/angular/test/controller/ionicScrollController.unit.js
@@ -63,6 +63,27 @@ describe('$ionicScroll Controller', function() {
expect(ctrl.scrollView.resize).toHaveBeenCalled();
});
+ it('should remember scroll position on $viewContentLoaded event', function() {
+ var historyData = { rememberedScrollValues: { left: 1, top: 2 } };
+ setup();
+ spyOn(ctrl.scrollView, 'scrollTo');
+ scope.$broadcast('$viewContentLoaded', historyData);
+ timeout.flush();
+ expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(1, 2);
+
+ spyOn(ctrl.scrollView, 'getValues').andCallFake(function() {
+ return {
+ left: 33,
+ top: 44
+ };
+ });
+ scope.$broadcast('$destroy');
+ expect(historyData.rememberedScrollValues).toEqual({
+ left: 33,
+ top: 44
+ });
+ });
+
it('should unbind window event listener on scope destroy', function() {
spyOn(window, 'removeEventListener');
spyOn(window, 'addEventListener');
diff --git a/js/ext/angular/test/directive/ionicContent.unit.js b/js/ext/angular/test/directive/ionicContent.unit.js
index 41f18159db..b8212410da 100644
--- a/js/ext/angular/test/directive/ionicContent.unit.js
+++ b/js/ext/angular/test/directive/ionicContent.unit.js
@@ -81,63 +81,6 @@ describe('Ionic Content directive', function() {
expect(vals.top).toBe(300);
});
- describe('save scroll', function() {
-
- function compileWithParent() {
- var parent = angular.element('
');
- //Make a phony element that tells the world it's a navView when in reality it's just a div
- parent.data('$navViewController', true);
- parent.append('
hello
');
- compile(parent)(scope);
- scope.$apply();
-
- /* Mock setting and getting scroll because we don't have time for the dom to load */
- var scrollValues = {};
- spyOn(scope.scrollView, 'scrollTo').andCallFake(function(left, top, a, zoom) {
- scrollValues = {
- left: left || 0,
- top: top || 0,
- zoom: zoom || 1
- };
- });
- spyOn(scope.scrollView, 'getValues').andCallFake(function() {
- return scrollValues;
- });
- }
-
- it('should set x and y with historyData.scrollValues passed in through $viewContentLoaded', function() {
- compileWithParent();
- var scrollValues = { top: 40, left: -20, zoom: 3 };
- scope.$broadcast('$viewContentLoaded', {
- scrollValues: scrollValues
- });
- timeout.flush();
- expect(scope.scrollView.scrollTo.mostRecentCall.args).toEqual([-20, 40]);
- });
-
- it('should set null with historyData.scrollValues not valid', function() {
- compileWithParent();
- var scrollValues = { left: 'bar', top: 'foo' };
- scope.$broadcast('$viewContentLoaded', { scrollValues: scrollValues });
- timeout.flush();
- expect(scope.scrollView.scrollTo.mostRecentCall.args).toEqual([null, null]);
- });
-
- it('should save scroll on the historyData passed in on $destroy', function() {
- compileWithParent();
- var historyData = {};
- scope.$broadcast('$viewContentLoaded', historyData);
- timeout.flush();
- scope.scrollView.scrollTo(null, 9, false);
- expect(historyData.scrollValues).toBeUndefined(); //sanity test
- scope.$destroy();
- expect(historyData.scrollValues).toEqual({
- left: 0,
- top: 9,
- zoom: 1
- });
- });
- });
});
/* Tests #555 */
describe('Ionic Content Directive scoping', function() {
diff --git a/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js b/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
index 461af502ac..ca7c7e0560 100644
--- a/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
+++ b/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
@@ -11,6 +11,13 @@ describe('Ionic ScrollDelegate Service', function() {
compile = $compile;
}));
+ it('should just return rootScope if no scrollCtrl', inject(function($rootScope) {
+ expect(function() {
+ $ionicScrollDelegate($rootScope);
+ }).not.toThrow();
+ expect($ionicScrollDelegate($rootScope).getScrollView()).toBeFalsy();
+ }));
+
it('Should get scroll view', function() {
var scope = rootScope.$new();
var el = compile('')(scope);
@@ -137,6 +144,50 @@ describe('Ionic ScrollDelegate Service', function() {
expect(sv.resize).toHaveBeenCalled();
expect(sv.scrollTo.mostRecentCall.args).toEqual([2, 3, animate]);
});
+
+ it('should throw error on rememberScroll if no id', function() {
+ var scope = rootScope.$new();
+ var el = compile('');
+ var del = $ionicScrollDelegate(scope);
+ expect(del.rememberScrollPosition).toThrow();
+ });
+
+ it('scrollToRememberedPosition should scroll if exists', function() {
+ var scope = rootScope.$new();
+ var el = compile('')(scope);
+ var del = $ionicScrollDelegate(scope);
+ var sv = del.getScrollView();
+ scope.$apply();
+ $ionicScrollDelegate._rememberedScrollValues['1'] = {
+ left: 3,
+ top: 4
+ };
+ del.scrollToRememberedPosition('1', animate);
+ spyOn(sv, 'scrollTo');
+ timeout.flush();
+ expect(sv.scrollTo).toHaveBeenCalledWith(3, 4, animate);
+ });
+
+ it('should save on destroy for rememberScrollPosition', function() {
+ var scope = rootScope.$new();
+ var el = compile('')(scope);
+ var del = $ionicScrollDelegate(scope);
+ var sv = del.getScrollView();
+ scope.$apply();
+ $ionicScrollDelegate._rememberedScrollValues['1'] = {
+ left: -1,
+ top: -1
+ };
+ del.rememberScrollPosition('1', animate);
+ spyOn(sv, 'getValues').andCallFake(function() {
+ return { foo: 'bar' };
+ });
+ scope.$destroy();
+ expect(sv.getValues).toHaveBeenCalled();
+ expect($ionicScrollDelegate._rememberedScrollValues['1']).toEqual({
+ foo: 'bar'
+ });
+ });
});
}
});
diff --git a/js/ext/angular/test/service/ionicViewService.unit.js b/js/ext/angular/test/service/ionicViewService.unit.js
index 56521ab418..0395085a1f 100644
--- a/js/ext/angular/test/service/ionicViewService.unit.js
+++ b/js/ext/angular/test/service/ionicViewService.unit.js
@@ -141,7 +141,7 @@ describe('Ionic View Service', function() {
registerData = viewService.register({});
currentView = viewService.getCurrentView();
//Set test value for remembered scroll
- currentView.scrollValues = 'foo';
+ currentView.rememberedScrollValues = 'foo';
backView = viewService.getBackView();
forwardView = viewService.getForwardView();
expect(backView.stateName).toEqual('about');
@@ -159,7 +159,7 @@ describe('Ionic View Service', function() {
currentView = viewService.getCurrentView();
backView = viewService.getBackView();
forwardView = viewService.getForwardView();
- expect(forwardView.scrollValues).toEqual({});
+ expect(forwardView.rememberedScrollValues).toEqual({});
expect(currentView.stateName).toEqual('about');
expect(currentView.backViewId).toEqual(backView.viewId);
expect(currentView.forwardViewId).toEqual(forwardView.viewId);