mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
BREAKING CHANGE: $ionicScrollDelegate, $ionicSlideBoxDelegate, and
$ionicSideMenuDelegate have been removed.
- $ionicScrollDelegate has been changed to $ionicScrollController.
Documentation:
[ionContent](
http://ajoslin.github.io/docs/nightly/api/directive/ionContent),
[ionScroll](
http://ajoslin.github.io/docs/nightly/api/directive/ionScroll)
Change your code from this:
```html
<ion-content ng-controller="MyCtrl">
<button ng-click="scrollBottom()">Scroll to bottom!</button>
</ion-content>
```
```js
function MyCtrl($scope, $ionicScrollDelegate) {
$scope.scrollBottom = function() {
$ionicScrollDelegate.scrollBottom();
};
}
```
To this:
```html
<!-- optional attr controller-bind, see docs -->
<ion-content ng-controller="MyCtrl">
<button ng-click="scrollBottom()">Scroll to bottom!</button>
</ion-content>
```
```js
function MyCtrl($scope) {
$scope.scrollBottom = function() {
$scope.$ionicScrollController.scrollBottom();
};
}
```
- $ionicSideMenuDelegate has been changed to
$ionicSideMenusController. Documentation:
[ionSideMenus](http://ajoslin.github.io/docs/nightly/api/directive/ionSideMenus)
Change your code from this:
```html
<ion-side-menus>
<ion-side-menu side="left">Side Menu Left</ion-side-menu>
<ion-pane ion-side-menu-content ng-controller="MyCtrl">
<button ng-click="toggleLeftMenu()">
Toggle Left Menu!
</button>
</ion-pane>
</ion-side-menus>
```
```js
function MyCtrl($scope, $ionicSideMenuDelegate) {
$scope.toggleLeftMenu = function() {
$ionicSideMenuDelegate.toggleLeft();
};
}
```
To this:
```html
<!-- optional attr controller-bind, see documentation -->
<ion-side-menus>
<ion-side-menu side="left">Side Menu Left</ion-side-menu>
<ion-pane ion-side-menu-content ng-controller="MyCtrl">
<button ng-click="toggleLeftMenu()">
Toggle Left Menu!
</button>
</ion-pane>
</ion-side-menus>
```
```js
function MyCtrl($scope) {
$scope.toggleLeftMenu = function() {
$scope.$ionicSideMenuController.toggleLeft();
};
}
```
- $ionicSlideBoxDelegate has been removed and upgraded to
$ionicSlideBoxController. It had only one method that
was unneeded. [Documentation](
http://ajoslin.github.io/docs/nightly/api/directive/ionSlideBox)
314 lines
11 KiB
JavaScript
314 lines
11 KiB
JavaScript
describe('$ionicScroll Controller', function() {
|
|
|
|
beforeEach(module('ionic'));
|
|
|
|
var scope, ctrl, timeout;
|
|
function setup(options) {
|
|
options = options || {};
|
|
|
|
options.el = options.el ||
|
|
//scrollView requires an outer container element and a child
|
|
//content element
|
|
angular.element('<div><div></div></div>')[0];
|
|
angular.element('<div>').append(options.el);
|
|
|
|
inject(function($controller, $rootScope, $timeout) {
|
|
scope = $rootScope.$new();
|
|
ctrl = $controller('$ionicScroll', {
|
|
$scope: scope,
|
|
scrollViewOptions: options
|
|
});
|
|
spyOn(ctrl.scrollView, 'run'); // don't actually call run, this is a dumb test
|
|
timeout = $timeout;
|
|
});
|
|
}
|
|
|
|
it('should set $scope.$ionicScrollController by default', function() {
|
|
setup()
|
|
expect(scope.$ionicScrollController).toBe(ctrl);
|
|
});
|
|
|
|
it('should set options.controllerBind to ctrl', function() {
|
|
setup({
|
|
controllerBind: 'something'
|
|
});
|
|
expect(scope.something).toBe(ctrl);
|
|
});
|
|
|
|
it('should set bounce to !isAndroid after platformReady, if not options.boucing', function() {
|
|
var isAndroid;
|
|
spyOn(ionic.Platform, 'isAndroid').andCallFake(function() {
|
|
return isAndroid;
|
|
});
|
|
setup({
|
|
bouncing: true
|
|
});
|
|
expect(ionic.Platform.isAndroid).not.toHaveBeenCalled();
|
|
|
|
isAndroid = true;
|
|
setup();
|
|
expect(ctrl.scrollView.options.bouncing).toBe(false);
|
|
|
|
isAndroid = false;
|
|
setup();
|
|
expect(ctrl.scrollView.options.bouncing).toBe(true);
|
|
});
|
|
|
|
it('should set this.element and this.$element', function() {
|
|
setup();
|
|
expect(ctrl.element.tagName).toMatch(/div/i);
|
|
expect(ctrl.$element[0]).toBe(ctrl.element);
|
|
});
|
|
|
|
it('should register scrollView as this.scrollView', function() {
|
|
setup();
|
|
expect(ctrl.scrollView instanceof ionic.views.Scroll).toBe(true);
|
|
});
|
|
|
|
it('should register controller on element.data', function() {
|
|
setup();
|
|
expect(ctrl.$element.parent().data('$$ionicScrollController')).toBe(ctrl);
|
|
});
|
|
|
|
it('should run after a timeout', function() {
|
|
setup();
|
|
timeout.flush();
|
|
expect(ctrl.scrollView.run).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should resize the scrollview on window resize', function() {
|
|
setup();
|
|
timeout.flush();
|
|
spyOn(ctrl.scrollView, 'resize');
|
|
ionic.trigger('resize', { target: window });
|
|
expect(ctrl.scrollView.resize).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should remember scroll position on $viewContentLoaded event', function() {
|
|
var historyData = { viewId: '1' };
|
|
setup();
|
|
spyOn(ctrl, 'rememberScrollPosition');
|
|
spyOn(ctrl, 'scrollToRememberedPosition');
|
|
scope.$broadcast('$viewContentLoaded', historyData);
|
|
timeout.flush();
|
|
expect(ctrl.rememberScrollPosition).toHaveBeenCalledWith('1');
|
|
expect(ctrl.scrollToRememberedPosition).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should forget on $viewHistory.viewBack after $viewContentLoaded', inject(function($rootScope) {
|
|
var historyData = { viewId: 'foo' };
|
|
setup();
|
|
spyOn($rootScope, '$on').andCallThrough();;
|
|
scope.$broadcast('$viewContentLoaded', historyData);
|
|
expect(scope.$on).toHaveBeenCalledWith('$viewHistory.viewBack', jasmine.any(Function));
|
|
|
|
//Should not forget unless backViewId is the same
|
|
spyOn(ctrl, 'forgetScrollPosition');
|
|
$rootScope.$broadcast('$viewHistory.viewBack', 'bar');
|
|
expect(ctrl.forgetScrollPosition).not.toHaveBeenCalled();
|
|
$rootScope.$broadcast('$viewHistory.viewBack', 'foo');
|
|
expect(ctrl.forgetScrollPosition).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('should not remember scrollValues on $destroy without id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
spyOn(ctrl.scrollView, 'getValues');
|
|
scope.$destroy();
|
|
expect(ctrl.scrollView.getValues).not.toHaveBeenCalled();
|
|
expect($$scrollValueCache).toEqual({});
|
|
}));
|
|
|
|
it('should remember scrollValues on $destroy with id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
ctrl.rememberScrollPosition('super');
|
|
spyOn(ctrl.scrollView, 'getValues').andCallFake(function() {
|
|
return 'scrollValues';
|
|
});
|
|
scope.$destroy();
|
|
expect(ctrl.scrollView.getValues).toHaveBeenCalled();
|
|
expect($$scrollValueCache).toEqual({
|
|
'super': 'scrollValues'
|
|
});
|
|
}));
|
|
|
|
it('rememberScrollPosition should throw without id', function() {
|
|
setup();
|
|
expect(function() {
|
|
ctrl.rememberScrollPosition();
|
|
}).toThrow();
|
|
});
|
|
|
|
it('should unbind window event listener on scope destroy', function() {
|
|
spyOn(window, 'removeEventListener');
|
|
spyOn(window, 'addEventListener');
|
|
setup();
|
|
expect(window.addEventListener).toHaveBeenCalled();
|
|
expect(window.addEventListener.mostRecentCall.args[0]).toBe('resize');
|
|
scope.$destroy();
|
|
expect(window.removeEventListener).toHaveBeenCalled();
|
|
expect(window.removeEventListener.mostRecentCall.args[0]).toBe('resize');
|
|
});
|
|
|
|
it('rememberScrollPosition should set id', function() {
|
|
setup();
|
|
expect(ctrl._rememberScrollId).toBeFalsy();
|
|
ctrl.rememberScrollPosition('banana');
|
|
expect(ctrl._rememberScrollId).toBe('banana');
|
|
});
|
|
|
|
it('forgetScrollPosition should remove from cache and unset id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
ctrl._rememberScrollId = 'elephant';
|
|
$$scrollValueCache.elephant = 'stampede';
|
|
ctrl.forgetScrollPosition();
|
|
expect(ctrl._rememberScrollId).toBeFalsy();
|
|
expect($$scrollValueCache.elephant).toBeFalsy();
|
|
}));
|
|
|
|
it('should listen to scroll event and call $onScroll', function() {
|
|
setup();
|
|
scope.$onScroll = jasmine.createSpy();
|
|
|
|
expect(scope.$onScroll).not.toHaveBeenCalled();
|
|
|
|
ionic.trigger('scroll', {target: ctrl.element});
|
|
expect(scope.$onScroll).toHaveBeenCalled();
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(0);
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(0);
|
|
|
|
scope.$onScroll.reset();
|
|
ionic.trigger('scroll', {target: ctrl.element, scrollTop: 3, scrollLeft: 4});
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(4);
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(3);
|
|
});
|
|
|
|
it('.resize() should resize after timeout', inject(function($timeout) {
|
|
setup();
|
|
$timeout.flush();
|
|
spyOn(ctrl.scrollView, 'resize');
|
|
ctrl.resize();
|
|
expect(ctrl.scrollView.resize).not.toHaveBeenCalled();
|
|
$timeout.flush();
|
|
expect(ctrl.scrollView.resize).toHaveBeenCalled();
|
|
}));
|
|
|
|
[false, true].forEach(function(shouldAnimate) {
|
|
describe('with animate='+shouldAnimate, function() {
|
|
|
|
it('scrollToRememberedPosition should work', inject(function($$scrollValueCache) {
|
|
setup();
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
$$scrollValueCache.foo = { left: 3, top: 4 };
|
|
ctrl._rememberScrollId = 'foo';
|
|
expect(ctrl.scrollView.scrollTo).not.toHaveBeenCalled();
|
|
ctrl.scrollToRememberedPosition(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(3, 4, shouldAnimate);
|
|
}));
|
|
|
|
describe('scroll action', function() {
|
|
beforeEach(function() {
|
|
setup();
|
|
//Mock resize to insta-call through for easier tests
|
|
ctrl.resize = function() {
|
|
return { then: function(cb) { cb(); } };
|
|
};
|
|
});
|
|
it('.scrollTop', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.scrollTop(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
});
|
|
it('.scrollBottom', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
spyOn(ctrl.scrollView, 'getScrollMax').andCallFake(function() {
|
|
return { left: 33, top: 44 };
|
|
});
|
|
ctrl.scrollBottom(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(33, 44, shouldAnimate);
|
|
});
|
|
it('.scrollTo', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.scrollTo(1, 2, shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(1, 2, shouldAnimate);
|
|
});
|
|
it('.anchorScroll without hash should scrollTop', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy();
|
|
$location.hash = function() { return null; };
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect($document[0].getElementById).not.toHaveBeenCalled();
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
}));
|
|
it('.anchorScroll with hash but no element should scrollTop', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy();
|
|
$location.hash = function() { return 'foo'; };
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect($document[0].getElementById).toHaveBeenCalledWith('foo');
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
}));
|
|
it('.anchorScroll with el matching hash should scroll to it', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy('byId').andCallFake(function() {
|
|
return { offsetLeft: 8, offsetTop: 9 };
|
|
});
|
|
spyOn($location, 'hash').andCallFake(function() {
|
|
return 'foo';
|
|
});
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(8, 9, shouldAnimate);
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should not activatePullToRefresh if setRefresher is not called', function() {
|
|
setup();
|
|
timeout.flush();
|
|
expect(ctrl.refresher).toBeFalsy();
|
|
spyOn(ctrl.scrollView, 'activatePullToRefresh');
|
|
expect(ctrl.scrollView.activatePullToRefresh).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should activatePullToRefresh and work when setRefresher', function() {
|
|
var startCb, refreshingCb, doneCb, refresherEl;
|
|
setup({
|
|
el: angular.element('<div><div class="scroll-refresher"></div></div>')[0]
|
|
});
|
|
spyOn(ctrl.scrollView, 'activatePullToRefresh').andCallFake(function(height, start, refreshing, done) {
|
|
startCb = start;
|
|
refreshingCb = refreshing;
|
|
doneCb = done;
|
|
});
|
|
ctrl._setRefresher(scope, ctrl.element);
|
|
|
|
var scrollOnRefreshSpy = jasmine.createSpy('scroll.onRefresh');
|
|
|
|
scope.$onRefresh = jasmine.createSpy('onRefresh');
|
|
scope.$onPulling = jasmine.createSpy('onPulling');
|
|
|
|
timeout.flush();
|
|
var refresher = ctrl.refresher;
|
|
|
|
expect(refresher.classList.contains('active')).toBe(false);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
|
|
startCb();
|
|
expect(refresher.classList.contains('active')).toBe(true);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
expect(scope.$onPulling).toHaveBeenCalled();
|
|
|
|
refreshingCb();
|
|
expect(refresher.classList.contains('active')).toBe(false);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
|
|
expect(scope.$onRefresh).not.toHaveBeenCalled();
|
|
|
|
doneCb();
|
|
expect(refresher.classList.contains('active')).toBe(false);
|
|
expect(refresher.classList.contains('refreshing')).toBe(true);
|
|
expect(scope.$onRefresh).toHaveBeenCalled();
|
|
});
|
|
|
|
});
|