Skip to content

Commit 7f4b28d

Browse files
committed
feat(list): reordering scrolls page, reordering performance better
Fixes #521. Reordering now uses webkitTransform instead of element.style.left. Additionally, as you drag the drag-element to the top or bottom of the scroll-area, it will scroll it up or down as allowed. Refactors necessary: Common code from `<content>` and `<scroll>` moved into js/ext/angular/controllers/ionicScrollController. Then `<content>` and `<scroll>` expose the controller, and `<list>` can require it. `<list>` then uses the controller (if exists) to pass the scrollView and scrollEl to ReorderDrag, and ReorderDrag uses that to scroll. Additionally, js/ext/angular/test/controller/ionicScrollController tests much functionality that was untested before.
1 parent 5ebbbab commit 7f4b28d

File tree

11 files changed

+300
-182
lines changed

11 files changed

+300
-182
lines changed

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ notifications:
1111
rooms:
1212
secure: mkHfRTsuxidtOOORbJJ0Jspb/DSa8jAiQwWWUljqLwefy1p4HGC9P/rLdXXg3vsjiulCzyjEkfvDWAHXvu34GhGWfQuD8U140Fon1Os3AO5Hbme+yRmjXmTcgH8XetSLQufyBBMqXHMd6o1tkxXql1p54G1IShhgAdPNe76d5ZE=
1313
template:
14-
- '<a href="%{build_url}">%{repository}: build#%{build_number} (%{duration%})</a> #{message} (%{branch} - %{commit} : %{author})'
14+
- '<a href="%{build_url}">%{repository}: build#%{build_number}</a> #{message} (%{branch} - %{commit} : %{author})'
1515
format: html
1616

1717
before_install:

Diff for: config/build.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ module.exports = {
3737
'js/_license.js',
3838
'js/ext/angular/src/ionicAngular.js',
3939
'js/ext/angular/src/service/**/*.js',
40-
'js/ext/angular/src/directive/**/*.js'
40+
'js/ext/angular/src/directive/**/*.js',
41+
'js/ext/angular/src/controller/**/*.js'
4142
],
4243
//Which vendor files to include in dist, used by build
4344
//Matched relative to config/lib/
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
(function() {
2+
'use strict';
3+
4+
angular.module('ionic.ui.scroll')
5+
6+
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout',
7+
function($scope, scrollViewOptions, $timeout) {
8+
9+
scrollViewOptions.bouncing = angular.isDefined(scrollViewOptions.bouncing) ?
10+
scrollViewOptions.bouncing :
11+
!ionic.Platform.isAndroid();
12+
13+
var element = this.element = scrollViewOptions.el;
14+
var refresher = this.refresher = element.querySelector('.scroll-refresher');
15+
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
16+
17+
this.$element = angular.element(element);
18+
19+
//Attach self to element as a controller so other directives can require this controller
20+
//through `require: '$ionicScroll'
21+
this.$element.data('$$ionicScrollController', this);
22+
23+
$timeout(function() {
24+
scrollView.run();
25+
26+
// Activate pull-to-refresh
27+
if(refresher) {
28+
var refresherHeight = refresher.clientHeight || 0;
29+
scrollView.activatePullToRefresh(refresherHeight, function() {
30+
refresher.classList.add('active');
31+
}, function() {
32+
refresher.classList.remove('refreshing');
33+
refresher.classList.remove('active');
34+
}, function() {
35+
refresher.classList.add('refreshing');
36+
$scope.onRefresh && $scope.onRefresh();
37+
$scope.$parent.$broadcast('scroll.onRefresh');
38+
});
39+
}
40+
});
41+
42+
}]);
43+
44+
})();

Diff for: js/ext/angular/src/directive/ionicContent.js

+47-78
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(function() {
22
'use strict';
33

4-
angular.module('ionic.ui.content', ['ionic.ui.service'])
4+
angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
55

66
/**
77
* Panel is a simple 100% width and height, fixed panel. It's meant for content to be
@@ -18,7 +18,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
1818

1919
// The content directive is a core scrollable content area
2020
// that is part of many View hierarchies
21-
.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', function($parse, $timeout, $ionicScrollDelegate) {
21+
.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', '$controller', function($parse, $timeout, $ionicScrollDelegate, $controller) {
2222
return {
2323
restrict: 'E',
2424
replace: true,
@@ -52,46 +52,33 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
5252
if(attr.hasTabs == "true") { element.addClass('has-tabs'); }
5353
if(attr.padding == "true") { element.find('div').addClass('padding'); }
5454

55-
return function link($scope, $element, $attr, navViewCtrl) {
56-
var clone, sc, sv,
55+
return {
56+
//Prelink <content> so it can compile before other directives compile.
57+
//Then other directives can require ionicScrollCtrl
58+
pre: prelink
59+
};
60+
61+
function prelink($scope, $element, $attr, navViewCtrl) {
62+
var clone, sc, scrollView, scrollCtrl,
5763
c = angular.element($element.children()[0]);
5864

5965
if($scope.scroll === "false") {
6066
// No scrolling
6167
return;
62-
}
63-
64-
if (navViewCtrl) {
65-
// If we do have a parent navView, wait for them to give us $viewContentLoaded event
66-
// before we fully initialize
67-
$scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
68-
initScroll(viewHistoryData);
69-
});
70-
} else {
71-
// If we are standalone view, just initialize immediately.
72-
initScroll();
7368
}
7469

75-
function initScroll(viewHistoryData) {
76-
viewHistoryData || (viewHistoryData = {});
77-
var savedScroll = viewHistoryData.scrollValues || {};
78-
79-
// If they want plain overflow scrolling, add that as a class
80-
if(attr.overflowScroll === "true") {
81-
$element.addClass('overflow-scroll');
82-
return;
83-
}
70+
if(attr.overflowScroll === "true") {
71+
$element.addClass('overflow-scroll');
72+
return;
73+
}
8474

85-
// Otherwise, use our scroll system
86-
var hasBouncing = $scope.$eval($scope.hasBouncing);
87-
var enableBouncing = (!ionic.Platform.isAndroid() && hasBouncing !== false) || hasBouncing === true;
88-
// No bouncing by default for Android users, lest they take up pitchforks
89-
// to our bouncing goodness
90-
sv = new ionic.views.Scroll({
75+
scrollCtrl = $controller('$ionicScroll', {
76+
$scope: $scope,
77+
scrollViewOptions: {
9178
el: $element[0],
92-
bouncing: enableBouncing,
93-
startX: $scope.$eval($scope.startX) || savedScroll.left || 0,
94-
startY: $scope.$eval($scope.startY) || savedScroll.top || 0,
79+
bouncing: $scope.$eval($scope.hasBouncing),
80+
startX: $scope.$eval($scope.startX) || 0,
81+
startY: $scope.$eval($scope.startY) || 0,
9582
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
9683
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
9784
scrollingX: $scope.$eval($scope.hasScrollX) === true,
@@ -103,54 +90,36 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
10390
scrollLeft: this.__scrollLeft
10491
});
10592
}
106-
});
107-
108-
//Save scroll onto viewHistoryData when scope is destroyed
109-
$scope.$on('$destroy', function() {
110-
viewHistoryData.scrollValues = sv.getValues();
111-
});
112-
113-
var refresher = $element[0].querySelector('.scroll-refresher');
114-
var refresherHeight = refresher && refresher.clientHeight || 0;
115-
116-
if(attr.refreshComplete) {
117-
$scope.refreshComplete = function() {
118-
if($scope.scrollView) {
119-
refresher && refresher.classList.remove('active');
120-
$scope.scrollView.finishPullToRefresh();
121-
$scope.$parent.$broadcast('scroll.onRefreshComplete');
122-
}
123-
};
12493
}
94+
});
95+
//Publish scrollView to parent so children can access it
96+
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
12597

126-
// Activate pull-to-refresh
127-
if(refresher) {
128-
sv.activatePullToRefresh(50, function() {
129-
refresher.classList.add('active');
130-
}, function() {
131-
refresher.classList.remove('refreshing');
132-
refresher.classList.remove('active');
133-
}, function() {
134-
refresher.classList.add('refreshing');
135-
$scope.onRefresh();
136-
$scope.$parent.$broadcast('scroll.onRefresh');
137-
});
98+
$scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
99+
viewHistoryData || (viewHistoryData = {});
100+
if (viewHistoryData.scrollValues) {
101+
scrollView.scrollTo(viewHistoryData.scrollValues);
138102
}
139103

140-
// Register for scroll delegate event handling
141-
$ionicScrollDelegate.register($scope, $element);
142-
143-
// Let child scopes access this
144-
$scope.$parent.scrollView = sv;
145-
146-
$timeout(function() {
147-
// Give child containers a chance to build and size themselves
148-
sv.run();
104+
//Save scroll onto viewHistoryData when scope is destroyed
105+
$scope.$on('$destroy', function() {
106+
viewHistoryData.scrollValues = scrollView.getValues();
149107
});
150-
151-
return sv;
108+
});
109+
110+
if(attr.refreshComplete) {
111+
$scope.refreshComplete = function() {
112+
if($scope.scrollView) {
113+
scrollCtrl.refresher && scrollCtrl.refresher.classList.remove('active');
114+
scrollView.finishPullToRefresh();
115+
$scope.$parent.$broadcast('scroll.onRefreshComplete');
116+
}
117+
};
152118
}
153119

120+
// Register for scroll delegate event handling
121+
$ionicScrollDelegate.register($scope, $element);
122+
154123
// Check if this supports infinite scrolling and listen for scroll events
155124
// to trigger the infinite scrolling
156125
// TODO(ajoslin): move functionality out of this function and make testable
@@ -163,20 +132,20 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
163132
if(distance.indexOf('%')) {
164133
// It's a multiplier
165134
maxScroll = function() {
166-
return sv.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
135+
return scrollView.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
167136
};
168137
} else {
169138
// It's a pixel value
170139
maxScroll = function() {
171-
return sv.getScrollMax().top - parseInt(distance, 10);
140+
return scrollView.getScrollMax().top - parseInt(distance, 10);
172141
};
173142
}
174143
$element.bind('scroll', function(e) {
175-
if( sv && !infiniteStarted && (sv.getValues().top > maxScroll() ) ) {
144+
if( scrollView && !infiniteStarted && (scrollView.getValues().top > maxScroll() ) ) {
176145
infiniteStarted = true;
177146
infiniteScroll.addClass('active');
178147
var cb = function() {
179-
sv.resize();
148+
scrollView.resize();
180149
infiniteStarted = false;
181150
infiniteScroll.removeClass('active');
182151
};

Diff for: js/ext/angular/src/directive/ionicList.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
3737

3838
link: function($scope, $element, $attr, list) {
3939
if(!list) return;
40-
40+
4141
var $parentScope = list.scope;
4242
var $parentAttrs = list.attrs;
4343

@@ -54,7 +54,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
5454

5555
$scope.itemClass = $scope.itemType;
5656

57-
// Decide if this item can do stuff, and follow a certain priority
57+
// Decide if this item can do stuff, and follow a certain priority
5858
// depending on where the value comes from
5959
if(($attr.canDelete ? $scope.canDelete : $parentScope.canDelete) !== "false") {
6060
if($attr.onDelete || $parentAttrs.onDelete) {
@@ -100,7 +100,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
100100
restrict: 'E',
101101
replace: true,
102102
transclude: true,
103-
103+
require: '^?$ionicScroll',
104104
scope: {
105105
itemType: '@',
106106
canDelete: '@',
@@ -122,10 +122,12 @@ angular.module('ionic.ui.list', ['ngAnimate'])
122122
this.attrs = $attrs;
123123
}],
124124

125-
link: function($scope, $element, $attr) {
125+
link: function($scope, $element, $attr, ionicScrollCtrl) {
126126
$scope.listView = new ionic.views.ListView({
127127
el: $element[0],
128128
listEl: $element[0].children[0],
129+
scrollEl: ionicScrollCtrl && ionicScrollCtrl.element,
130+
scrollView: ionicScrollCtrl && ionicScrollCtrl.scrollView,
129131
onReorder: function(el, oldIndex, newIndex) {
130132
$scope.$apply(function() {
131133
$scope.onReorder({el: el, start: oldIndex, end: newIndex});

0 commit comments

Comments
 (0)