mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat($ionicScrollDelegate): add scrollTo(left,top,animate) to delegate
Also moves $ionicScrollDelegate.register to $ionicScroll controller, and makes `<scroll>` directive be registered with $ionicScrollDelegate.
This commit is contained in:
@@ -18,7 +18,7 @@ module.exports = function(config) {
|
||||
'config/lib/js/angular/angular.js',
|
||||
'config/lib/js/angular/angular-animate.js',
|
||||
'config/lib/js/angular/angular-mocks.js',
|
||||
'config/lib/js/angular-ui/angular-ui-router.js',
|
||||
'config/lib/js/angular-ui/angular-ui-router.js'
|
||||
]
|
||||
.concat(buildConfig.ionicFiles)
|
||||
.concat(buildConfig.angularIonicFiles)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
angular.module('ionic.ui.scroll')
|
||||
|
||||
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout',
|
||||
function($scope, scrollViewOptions, $timeout) {
|
||||
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate',
|
||||
function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate) {
|
||||
|
||||
scrollViewOptions.bouncing = angular.isDefined(scrollViewOptions.bouncing) ?
|
||||
scrollViewOptions.bouncing :
|
||||
@@ -15,11 +15,14 @@ angular.module('ionic.ui.scroll')
|
||||
var element = this.element = scrollViewOptions.el;
|
||||
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
|
||||
|
||||
this.$element = angular.element(element);
|
||||
var $element = this.$element = angular.element(element);
|
||||
|
||||
//Attach self to element as a controller so other directives can require this controller
|
||||
//through `require: '$ionicScroll'
|
||||
this.$element.data('$$ionicScrollController', this);
|
||||
$element.data('$$ionicScrollController', this);
|
||||
|
||||
//Register delegate for event handling
|
||||
$ionicScrollDelegate.register($scope, $element, scrollView);
|
||||
|
||||
$timeout(function() {
|
||||
scrollView.run();
|
||||
|
||||
3
js/ext/angular/src/directive/ionicContent.js
vendored
3
js/ext/angular/src/directive/ionicContent.js
vendored
@@ -120,9 +120,6 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
|
||||
};
|
||||
}
|
||||
|
||||
// Register for scroll delegate event handling
|
||||
$ionicScrollDelegate.register($scope, $element);
|
||||
|
||||
// Check if this supports infinite scrolling and listen for scroll events
|
||||
// to trigger the infinite scrolling
|
||||
// TODO(ajoslin): move functionality out of this function and make testable
|
||||
|
||||
21
js/ext/angular/src/directive/ionicScroll.js
vendored
21
js/ext/angular/src/directive/ionicScroll.js
vendored
@@ -33,8 +33,6 @@ angular.module('ionic.ui.scroll', [])
|
||||
function prelink($scope, $element, $attr) {
|
||||
var scrollView, scrollCtrl, sc = $element[0].children[0];
|
||||
|
||||
// Create the internal scroll div
|
||||
sc.className = 'scroll';
|
||||
if(attr.padding == "true") {
|
||||
sc.classList.add('padding');
|
||||
}
|
||||
@@ -63,25 +61,6 @@ angular.module('ionic.ui.scroll', [])
|
||||
scrollViewOptions: scrollViewOptions
|
||||
});
|
||||
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
|
||||
|
||||
$element.bind('scroll', function(e) {
|
||||
$scope.onScroll({
|
||||
event: e,
|
||||
scrollTop: e.detail ? e.detail.scrollTop : e.originalEvent ? e.originalEvent.detail.scrollTop : 0,
|
||||
scrollLeft: e.detail ? e.detail.scrollLeft: e.originalEvent ? e.originalEvent.detail.scrollLeft : 0
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$parent.$on('scroll.resize', function(e) {
|
||||
// Run the resize after this digest
|
||||
$timeout(function() {
|
||||
scrollView && scrollView.resize();
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$parent.$on('scroll.refreshComplete', function(e) {
|
||||
scrollView && scrollView.finishPullToRefresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,6 +14,9 @@ angular.module('ionic.ui.service.scrollDelegate', [])
|
||||
scrollBottom: function(animate) {
|
||||
$rootScope.$broadcast('scroll.scrollBottom', animate);
|
||||
},
|
||||
scrollTo: function(left, top, animate) {
|
||||
$rootScope.$broadcast('scroll.scrollTo', left, top, animate);
|
||||
},
|
||||
resize: function() {
|
||||
$rootScope.$broadcast('scroll.resize');
|
||||
},
|
||||
@@ -45,18 +48,14 @@ angular.module('ionic.ui.service.scrollDelegate', [])
|
||||
getScrollView: function($scope) {
|
||||
return $scope.scrollView;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a scope for scroll event handling.
|
||||
* Register a scope and scroll view for scroll event handling.
|
||||
* $scope {Scope} the scope to register and listen for events
|
||||
*/
|
||||
register: function($scope, $element) {
|
||||
//Get scroll controller from parent
|
||||
var scrollCtrl = $element.controller('$ionicScroll');
|
||||
if (!scrollCtrl) {
|
||||
return;
|
||||
}
|
||||
var scrollView = scrollCtrl.scrollView;
|
||||
var scrollEl = scrollCtrl.element;
|
||||
register: function($scope, $element, scrollView) {
|
||||
|
||||
var scrollEl = $element[0];
|
||||
|
||||
function scrollViewResize() {
|
||||
// Run the resize after this digest
|
||||
@@ -93,14 +92,14 @@ angular.module('ionic.ui.service.scrollDelegate', [])
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Called to scroll to the top of the content
|
||||
*
|
||||
* @param animate {boolean} whether to animate or just snap
|
||||
*/
|
||||
$scope.$parent.$on('scroll.scrollTo', function(e, left, top, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
scrollView.scrollTo(left, top, !!animate);
|
||||
});
|
||||
});
|
||||
$scope.$parent.$on('scroll.scrollTop', function(e, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
scrollView.scrollTo(0, 0, animate === false ? false : true);
|
||||
scrollView.scrollTo(0, 0, !!animate);
|
||||
});
|
||||
});
|
||||
$scope.$parent.$on('scroll.scrollBottom', function(e, animate) {
|
||||
@@ -108,7 +107,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
|
||||
var sv = scrollView;
|
||||
if (sv) {
|
||||
var max = sv.getScrollMax();
|
||||
sv.scrollTo(0, max.top, animate === false ? false : true);
|
||||
sv.scrollTo(max.left, max.top, !!animate);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
describe('$ionicScroll Controller', function() {
|
||||
|
||||
beforeEach(module('ionic.ui.scroll'));
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
var scope, ctrl, timeout;
|
||||
function setup(options) {
|
||||
@@ -41,6 +41,12 @@ describe('$ionicScroll Controller', function() {
|
||||
expect(ctrl.scrollView.run).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should register with $ionicScrollDelegate', inject(function($ionicScrollDelegate) {
|
||||
spyOn($ionicScrollDelegate, 'register');
|
||||
setup();
|
||||
expect($ionicScrollDelegate.register).toHaveBeenCalledWith(scope, ctrl.$element, ctrl.scrollView);
|
||||
}));
|
||||
|
||||
it('should not setup if no child .scroll-refresher', function() {
|
||||
setup();
|
||||
timeout.flush();
|
||||
@@ -70,7 +76,6 @@ describe('$ionicScroll Controller', function() {
|
||||
});
|
||||
|
||||
scope.onRefresh = jasmine.createSpy('onRefresh');
|
||||
scope.$parent.$broadcast = jasmine.createSpy('$broadcast');
|
||||
|
||||
timeout.flush();
|
||||
var refresher = ctrl.refresher;
|
||||
@@ -87,13 +92,11 @@ describe('$ionicScroll Controller', function() {
|
||||
expect(refresher.classList.contains('refreshing')).toBe(false);
|
||||
|
||||
expect(scope.onRefresh).not.toHaveBeenCalled();
|
||||
expect(scope.$parent.$broadcast).not.toHaveBeenCalledWith('scroll.onRefresh');
|
||||
|
||||
doneCb();
|
||||
expect(refresher.classList.contains('active')).toBe(false);
|
||||
expect(refresher.classList.contains('refreshing')).toBe(true);
|
||||
expect(scope.onRefresh).toHaveBeenCalled();
|
||||
expect(scope.$parent.$broadcast).toHaveBeenCalledWith('scroll.onRefresh');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
describe('Ionic Content directive', function() {
|
||||
var compile, element, scope;
|
||||
|
||||
beforeEach(module('ionic.ui.content'));
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
beforeEach(inject(function($compile, $rootScope, $timeout, $window) {
|
||||
compile = $compile;
|
||||
@@ -11,6 +11,11 @@ describe('Ionic Content directive', function() {
|
||||
ionic.Platform.setPlatform('Android');
|
||||
}));
|
||||
|
||||
it('Has $ionicScroll controller', function() {
|
||||
element = compile('<content></content>')(scope);
|
||||
expect(element.controller('$ionicScroll').element).toBe(element[0]);
|
||||
});
|
||||
|
||||
it('Has content class', function() {
|
||||
element = compile('<content></content>')(scope);
|
||||
expect(element.hasClass('scroll-content')).toBe(true);
|
||||
|
||||
67
js/ext/angular/test/directive/ionicScroll.unit.js
Normal file
67
js/ext/angular/test/directive/ionicScroll.unit.js
Normal file
@@ -0,0 +1,67 @@
|
||||
describe('Ionic Scroll Directive', function() {
|
||||
var compile, element, scope;
|
||||
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
beforeEach(inject(function($compile, $rootScope, $timeout, $window) {
|
||||
compile = $compile;
|
||||
scope = $rootScope;
|
||||
timeout = $timeout;
|
||||
window = $window;
|
||||
ionic.Platform.setPlatform('Android');
|
||||
}));
|
||||
|
||||
it('Has $ionicScroll controller', function() {
|
||||
element = compile('<scroll></scroll>')(scope);
|
||||
expect(element.controller('$ionicScroll').element).toBe(element[0]);
|
||||
});
|
||||
|
||||
it('Has scroll-view class', function() {
|
||||
element = compile('<scroll></scroll>')(scope);
|
||||
expect(element.hasClass('scroll-view')).toBe(true);
|
||||
});
|
||||
|
||||
it('should add padding classname', function() {
|
||||
element = compile('<scroll padding="true"></scroll>')(scope);
|
||||
expect(element.children().eq(0).hasClass('padding')).toEqual(true);
|
||||
var scrollElement = element.find('.scroll');
|
||||
expect(scrollElement.hasClass('padding')).toEqual(true);
|
||||
});
|
||||
|
||||
it('Enables bouncing by default', function() {
|
||||
ionic.Platform.setPlatform('iPhone');
|
||||
element = compile('<content has-header="true"></scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(true);
|
||||
});
|
||||
|
||||
it('Disables bouncing when has-bouncing = false', function() {
|
||||
ionic.Platform.setPlatform('iPhone');
|
||||
element = compile('<content has-header="true" has-bouncing="false"></scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(false);
|
||||
});
|
||||
|
||||
it('Disables bouncing by default on Android', function() {
|
||||
ionic.Platform.setPlatform('Android');
|
||||
element = compile('<content has-header="true"></scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(false);
|
||||
});
|
||||
|
||||
it('Should set start x and y', function() {
|
||||
element = compile('<content start-x="100" start-y="300" has-header="true"></scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
var vals = scrollView.getValues();
|
||||
expect(vals.left).toBe(100);
|
||||
expect(vals.top).toBe(300);
|
||||
});
|
||||
});
|
||||
@@ -52,20 +52,24 @@
|
||||
|
||||
<content has-header="true" scroll="false">
|
||||
<h3>Hourly Forecast</h3>
|
||||
<scroll direction="x" style="width: 100%">
|
||||
<scroll direction="x" style="width: 100%; height: 20%;">
|
||||
<div class="hours">
|
||||
<div ng-repeat="hour in hours">
|
||||
<div class="icons">
|
||||
<i class="icon ion-ios7-sunny-outline"></i>
|
||||
<i class="icon ion-cloud"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
{{hour.temp}} °F
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</scroll>
|
||||
<scroll direction="y" style="height: 400px; width: 300px">
|
||||
<a class="button" ng-click="scrollTo()">
|
||||
Scroll to 100
|
||||
</a>
|
||||
<scroll direction="y" style="height: 400px; width: 300px; background: red;">
|
||||
<div style="width: 100px; height: 4000px; background: url('tree_bark.png') repeat"></div>
|
||||
</scroll>
|
||||
</content>
|
||||
@@ -83,7 +87,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
.controller('ThisCtrl', function($scope) {
|
||||
.controller('ThisCtrl', function($scope, $ionicScrollDelegate) {
|
||||
var header = document.getElementById('header');
|
||||
var content = document.getElementById('container');
|
||||
|
||||
@@ -97,6 +101,10 @@
|
||||
$scope.onScrollComplete = function(event, scrollTop, scrollLeft) {
|
||||
console.log('Scroll complete', scrollTop, scrollLeft);
|
||||
}
|
||||
$scope.scrollTo = function() {
|
||||
console.log('scrollTo');
|
||||
$ionicScrollDelegate.scrollTo(0, 100, true);
|
||||
};
|
||||
$scope.onScroll = function(event, scrollTop, scrollLeft) {
|
||||
/*
|
||||
if(scrollTop > startTop) {
|
||||
|
||||
@@ -11,15 +11,6 @@ describe('Ionic ScrollDelegate Service', function() {
|
||||
compile = $compile;
|
||||
}));
|
||||
|
||||
it('Should register', function() {
|
||||
spyOn(del, 'register');
|
||||
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content></content>')(scope);
|
||||
|
||||
expect(del.register).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should get scroll view', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content></content>')(scope);
|
||||
@@ -33,43 +24,64 @@ describe('Ionic ScrollDelegate Service', function() {
|
||||
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
|
||||
del.resize();
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should resize & scroll top', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content start-y="100"></content>')(scope);
|
||||
testWithAnimate(true);
|
||||
testWithAnimate(false);
|
||||
function testWithAnimate(animate) {
|
||||
describe('with animate='+animate, function() {
|
||||
it('should resize & scroll top', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content start-y="100"></content>')(scope);
|
||||
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'resize');
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
del.scrollTop(animate);
|
||||
|
||||
expect(sv.getValues().top).toBe(100);
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([0, 0, animate]);
|
||||
});
|
||||
|
||||
del.scrollTop(false);
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
it('should resize & scroll bottom', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content start-y="100"><br/><br/></content>')(scope);
|
||||
|
||||
expect(sv.getValues().top).toBe(0);
|
||||
});
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'getScrollMax').andCallFake(function() {
|
||||
return { left: 10, top: 11 };
|
||||
});
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
var max = sv.getScrollMax();
|
||||
del.scrollBottom(animate);
|
||||
|
||||
it('Should resize & scroll bottom', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content start-y="100"></content>')(scope);
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([max.left, max.top, animate]);
|
||||
});
|
||||
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'resize');
|
||||
it('should resize & scrollTo', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<content start-y="100"><br/><br/></content>')(scope);
|
||||
|
||||
expect(sv.getValues().top).toBe(100);
|
||||
var sv = del.getScrollView(scope);
|
||||
spyOn(sv, 'scrollTo');
|
||||
spyOn(sv, 'resize');
|
||||
del.scrollTo(2, 3, animate);
|
||||
|
||||
del.scrollBottom(false);
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
|
||||
expect(sv.getValues().top).toBe(sv.getScrollMax().top);
|
||||
});
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([2, 3, animate]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should finish refreshing', function() {
|
||||
var scope = rootScope.$new();
|
||||
|
||||
Reference in New Issue
Block a user