diff --git a/misc/tutorial/212_infinite_scroll.ngdoc b/misc/tutorial/212_infinite_scroll.ngdoc index d8f184d9f4..44d26ed239 100644 --- a/misc/tutorial/212_infinite_scroll.ngdoc +++ b/misc/tutorial/212_infinite_scroll.ngdoc @@ -2,85 +2,104 @@ @name Tutorial: 212 Infinite scroll @description -The infinite scroll feature allows the user to lazy load their data to gridOptions.data +The infinite scroll feature allows the user to lazy load their data to gridOptions.data. -Specify percentage when lazy load should trigger: -
-  $scope.gridOptions.infiniteScroll = 20;
-
+Once you reach the top (or bottom) of your real data set, you can notify that no more pages exist +up (or down), and infinite scroll will stop triggering events in that direction. You can also +optionally tell us up-front that there are no more pages up through `infiniteScrollUp = true` or down through +`infiniteScrollDown = true`, and we will never trigger +pages in that direction. By default we assume you have pages down but not up. + +You can specify the percentage of the grid at which the infinite scroll will trigger a request for +more data `infiniteScrollPercentage = 20`. By default we trigger when you are 20% away from the end of +the grid (in either direction). + +We will raise a `needMoreData` or `needMoreDataTop` event, which you must listen to and respond to if +you have told us that you have more data available. Once you have retrieved the data and added it to your +data array (at the top if the event was `needMoreDataTop`), you need to call `dataLoaded` to tell us +that you have loaded your data. Optionally, you can tell us that there is no more data, and we won't trigger +further requests for more data in that direction. + +When you have loaded your data we will attempt to adjust the grid scroll to give the appearance of continuous +scrolling. This is a little jumpy at present, largely because we work of percentages instead of a number of +rows from the end. We basically assume that your user will have reached the end of the scroll (upwards or downwards) +by the time the data comes back, and scroll the user to the beginning of the newly added data to reflect that. If +your user has already scrolled a lot of pages, then they may not be at the end of the data (20% can be a long way). +Ideally the API would change to a number of rows. + +Finally, we suppress the normal grid behaviour of propagating the scroll to the parent container when you reach the end +if infinite scroll is enabled and there is still data in that direction. @example +In this example we have a data set that starts at page 2 of a 5 page data set. Each page is 100 records, so we start at +record 200, and we can scroll up 2 pages, and scroll down 2 pages. You should see smooth scrolling as you move up, when +you hit record zero a touchpad scroll should propagate to the parent page. You should also see smooth scrolling as you +move down, and when you hit record 499 a touchpad scroll should propagate to the parent page. + var app = angular.module('app', ['ngTouch', 'ui.grid', 'ui.grid.infiniteScroll']); - app.controller('MainCtrl', ['$scope', '$http', '$log', function ($scope, $http, $log) { - $scope.gridOptions = {}; - - /** - * @ngdoc property - * @name infiniteScrollPercentage - * @propertyOf ui.grid.class:GridOptions - * @description This setting controls at what percentage of the scroll more data - * is requested by the infinite scroll - */ - $scope.gridOptions.infiniteScrollPercentage = 15; - - $scope.gridOptions.columnDefs = [ - { name:'id'}, - { name:'name' }, - { name:'age' } - ]; - var page = 0; - var pageUp = 0; - var getData = function(data, page) { - var res = []; - for (var i = (page * 100); i < (page + 1) * 100 && i < data.length; ++i) { - res.push(data[i]); + app.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) { + $scope.gridOptions = { + infiniteScrollPercentage: 15, + infiniteScrollUp: true, + infiniteScrollDown: true, + columnDefs: [ + { name:'id'}, + { name:'name' }, + { name:'age' } + ], + data: 'data', + onRegisterApi: function(gridApi){ + gridApi.infiniteScroll.on.needLoadMoreData($scope, $scope.getDataDown); + gridApi.infiniteScroll.on.needLoadMoreDataTop($scope, $scope.getDataUp); + $scope.gridApi = gridApi; } - return res; }; - var getDataUp = function(data, page) { - var res = []; - for (var i = data.length - (page * 100) - 1; (data.length - i) < ((page + 1) * 100) && (data.length - i) > 0; --i) { - data[i].id = -(data.length - data[i].id) - res.push(data[i]); - } - return res; + $scope.data = []; + + var firstPage = 2; + var lastPage = 1; + + $scope.getDataDown = function() { + $http.get('/data/10000_complex.json') + .success(function(data) { + lastPage++; + var newData = $scope.getPage(data, lastPage); + $scope.data = $scope.data.concat(newData); + $scope.gridApi.infiniteScroll.dataLoaded(null, lastPage === 4); + }) + .error(function(error) { + $scope.gridApi.infiniteScroll.dataLoaded(); + }); }; - $http.get('/data/10000_complex.json') + $scope.getDataUp = function() { + $http.get('/data/10000_complex.json') .success(function(data) { - $scope.gridOptions.data = getData(data, page); - ++page; + firstPage--; + var newData = $scope.getPage(data, firstPage); + $scope.data = newData.concat($scope.data); + $scope.gridApi.infiniteScroll.dataLoaded(firstPage === 0, null); + }) + .error(function(error) { + $scope.gridApi.infiniteScroll.dataLoaded(); }); + }; - $scope.gridOptions.onRegisterApi = function(gridApi){ - gridApi.infiniteScroll.on.needLoadMoreData($scope,function(){ - $http.get('/data/10000_complex.json') - .success(function(data) { - $scope.gridOptions.data = $scope.gridOptions.data.concat(getData(data, page)); - ++page; - gridApi.infiniteScroll.dataLoaded(); - }) - .error(function() { - gridApi.infiniteScroll.dataLoaded(); - }); - }); - gridApi.infiniteScroll.on.needLoadMoreDataTop($scope,function(){ - $http.get('/data/10000_complex.json') - .success(function(data) { - $scope.gridOptions.data = getDataUp(data, pageUp).reverse().concat($scope.gridOptions.data); - ++pageUp; - gridApi.infiniteScroll.dataLoaded(); - }) - .error(function() { - gridApi.infiniteScroll.dataLoaded(); - }); - }); + + $scope.getPage = function(data, page) { + var res = []; + for (var i = (page * 100); i < (page + 1) * 100 && i < data.length; ++i) { + res.push(data[i]); + } + return res; }; + + $scope.getDataDown(); }]); diff --git a/src/features/infinite-scroll/js/infinite-scroll.js b/src/features/infinite-scroll/js/infinite-scroll.js index bd90ae8747..06ba5b6e3a 100644 --- a/src/features/infinite-scroll/js/infinite-scroll.js +++ b/src/features/infinite-scroll/js/infinite-scroll.js @@ -17,7 +17,7 @@ * * @description Service for infinite scroll features */ - module.service('uiGridInfiniteScrollService', ['gridUtil', '$compile', '$timeout', 'uiGridConstants', function (gridUtil, $compile, $timeout, uiGridConstants) { + module.service('uiGridInfiniteScrollService', ['gridUtil', '$compile', '$timeout', 'uiGridConstants', 'ScrollEvent', function (gridUtil, $compile, $timeout, uiGridConstants, ScrollEvent) { var service = { @@ -28,8 +28,24 @@ * @description This method register events and methods into grid public API */ - initializeGrid: function(grid) { + initializeGrid: function(grid, $scope) { service.defaultGridOptions(grid.options); + grid.infiniteScroll = { dataLoading: false, scrollUp: grid.options.infiniteScrollUp, scrollDown: grid.options.infiniteScrollDown }; + + if ( grid.options.infiniteScrollUp){ + grid.suppressParentScrollUp = true; + } + + if ( grid.options.infiniteScrollDown){ + grid.suppressParentScrollDown = true; + } + + if (grid.options.enableInfiniteScroll) { + grid.api.core.on.scrollEvent($scope, service.handleScroll); + } + + // tweak the scroll for infinite scroll up (if enabled) + service.adjustScroll(grid); /** * @ngdoc object @@ -45,7 +61,7 @@ * @ngdoc event * @name needLoadMoreData * @eventOf ui.grid.infiniteScroll.api:PublicAPI - * @description This event fires when scroll reached bottom percentage of grid + * @description This event fires when scroll reaches bottom percentage of grid * and needs to load data */ @@ -56,7 +72,7 @@ * @ngdoc event * @name needLoadMoreDataTop * @eventOf ui.grid.infiniteScroll.api:PublicAPI - * @description This event fires when scroll reached top percentage of grid + * @description This event fires when scroll reaches top percentage of grid * and needs to load data */ @@ -71,20 +87,40 @@ * @ngdoc function * @name dataLoaded * @methodOf ui.grid.infiniteScroll.api:PublicAPI - * @description This function is used as a promise when data finished loading. - * See infinite_scroll ngdoc for example of usage + * @description Call this function when you have loaded the additional data + * requested. You can set noMoreDataTop or noMoreDataBottom to indicate + * that we've reached the end of your data set, we won't fire any more events + * for scroll in that direction. + * See infinite_scroll tutorial for example of usage + * @param {boolean} noMoreDataTop flag that there are no more pages upwards, so don't fire + * any more infinite scroll events upward + * @param {boolean} noMoreDataBottom flag that there are no more pages downwards, so don't + * fire any more infinite scroll events downward */ - dataLoaded: function() { - grid.options.loadTimout = false; + dataLoaded: function( noMoreDataTop, noMoreDataBottom ) { + grid.infiniteScroll.dataLoading = false; + + if ( noMoreDataTop === true ){ + grid.infiniteScroll.scrollUp = false; + grid.suppressParentScrollUp = false; + } + + if ( noMoreDataBottom === true ){ + grid.infiniteScroll.scrollDown = false; + grid.suppressParentScrollDown = false; + } + + service.adjustScroll(grid); } } } }; - grid.options.loadTimout = false; grid.api.registerEventsFromObject(publicApi.events); grid.api.registerMethodsFromObject(publicApi.methods); }, + + defaultGridOptions: function (gridOptions) { //default option to true unless it was explicitly set to false /** @@ -103,6 +139,73 @@ *
Defaults to true */ gridOptions.enableInfiniteScroll = gridOptions.enableInfiniteScroll !== false; + + /** + * @ngdoc property + * @name infiniteScrollPercentage + * @propertyOf ui.grid.class:GridOptions + * @description This setting controls at what percentage remaining more data + * is requested by the infinite scroll, whether scrolling up or down. + * + * TODO: it would be nice if this were percentage of a page, not percentage of the + * total scroll - as you get more and more data, the needMoreData event is triggered + * further and further away from the end (in terms of number of rows) + *
Defaults to 20 + */ + gridOptions.infiniteScrollPercentage = gridOptions.infiniteScrollPercentage || 20; + + /** + * @ngdoc property + * @name infiniteScrollUp + * @propertyOf ui.grid.class:GridOptions + * @description Whether you allow infinite scroll up, implying that the first page of data + * you have displayed is in the middle of your data set. If set to true then we trigger the + * needMoreDataTop event when the user hits the top of the scrollbar. + *
Defaults to false + */ + gridOptions.infiniteScrollUp = gridOptions.infiniteScrollUp === true; + + /** + * @ngdoc property + * @name infiniteScrollDown + * @propertyOf ui.grid.class:GridOptions + * @description Whether you allow infinite scroll down, implying that the first page of data + * you have displayed is in the middle of your data set. If set to true then we trigger the + * needMoreData event when the user hits the bottom of the scrollbar. + *
Defaults to true + */ + gridOptions.infiniteScrollDown = gridOptions.infiniteScrollDown !== false; + }, + + + /** + * @ngdoc function + * @name handleScroll + * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService + * @description Called whenever the grid scrolls, determines whether the scroll should + * trigger an infinite scroll request for more data + * @param {object} args the args from the event + */ + handleScroll: function (args) { + // don't request data if already waiting for data, or if source is coming from ui.grid.adjustInfiniteScrollPosition() function + if ( args.grid.infiniteScroll && args.grid.infiniteScroll.dataLoading || args.source === 'ui.grid.adjustInfiniteScrollPosition' ){ + return; + } + + if (args.y) { + var percentage; + if (args.grid.scrollDirection === uiGridConstants.scrollDirection.UP ) { + percentage = args.y.percentage; + if (percentage <= args.grid.options.infiniteScrollPercentage / 100){ + service.loadData(args.grid); + } + } else if (args.grid.scrollDirection === uiGridConstants.scrollDirection.DOWN) { + percentage = 1 - args.y.percentage; + if (percentage <= args.grid.options.infiniteScrollPercentage / 100){ + service.loadData(args.grid); + } + } + } }, @@ -111,43 +214,92 @@ * @name loadData * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService * @description This function fires 'needLoadMoreData' or 'needLoadMoreDataTop' event based on scrollDirection + * and whether there are more pages upwards or downwards + * @param {Grid} grid the grid we're working on */ - loadData: function (grid) { - grid.options.loadTimout = true; - if (grid.scrollDirection === uiGridConstants.scrollDirection.UP) { + // save number of currently visible rows to calculate new scroll position later - we know that we want + // to be at approximately the row we're currently at + grid.infiniteScroll.previousVisibleRows = grid.renderContainers.body.visibleRowCache.length; + grid.infiniteScroll.direction = grid.scrollDirection; + + if (grid.scrollDirection === uiGridConstants.scrollDirection.UP && grid.infiniteScroll.scrollUp ) { + grid.infiniteScroll.dataLoading = true; grid.api.infiniteScroll.raise.needLoadMoreDataTop(); - return; + } else if (grid.scrollDirection === uiGridConstants.scrollDirection.DOWN && grid.infiniteScroll.scrollDown ) { + grid.infiniteScroll.dataLoading = true; + grid.api.infiniteScroll.raise.needLoadMoreData(); } - grid.api.infiniteScroll.raise.needLoadMoreData(); }, - + + /** * @ngdoc function - * @name checkScroll + * @name adjustScroll * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService - * @description This function checks scroll position inside grid and - * calls 'loadData' function when scroll reaches 'infiniteScrollPercentage' + * @description Once we are informed that data has been loaded, adjust the scroll position to account for that + * addition and to make things look clean. + * + * If we're scrolling up we scroll to the first row of the old data set - + * so we're assuming that you would have gotten to the top of the grid (from the 20% need more data trigger) by + * the time the data comes back. If we're scrolling down we scoll to the last row of the old data set - so we're + * assuming that you would have gotten to the bottom of the grid (from the 80% need more data trigger) by the time + * the data comes back. + * + * Neither of these are good assumptions, but making this a smoother experience really requires + * that trigger to not be a percentage, and to be much closer to the end of the data (say, 5 rows off the end). Even then + * it'd be better still to actually run into the end. But if the data takes a while to come back, they may have scrolled + * somewhere else in the mean-time, in which case they'll get a jump back to the new data. Anyway, this will do for + * now, until someone wants to do better. + * @param {Grid} grid the grid we're working on */ + adjustScroll: function(grid){ + $timeout(function () { + var percentage; + + if ( grid.infiniteScroll.direction === undefined ){ + // called from initialize, tweak our scroll up a little + service.adjustInfiniteScrollPosition(grid, 0); + } - checkScroll: function(grid, scrollTop) { + var newVisibleRows = grid.renderContainers.body.visibleRowCache.length; + if ( grid.infiniteScroll.direction === uiGridConstants.scrollDirection.UP ){ + percentage = ( newVisibleRows - grid.infiniteScroll.previousVisibleRows ) / newVisibleRows; + service.adjustInfiniteScrollPosition(grid, percentage); + } - /* Take infiniteScrollPercentage value or use 20% as default */ - var infiniteScrollPercentage = grid.options.infiniteScrollPercentage ? grid.options.infiniteScrollPercentage : 20; + if ( grid.infiniteScroll.direction === uiGridConstants.scrollDirection.DOWN ){ + percentage = grid.infiniteScroll.previousVisibleRows / newVisibleRows; + service.adjustInfiniteScrollPosition(grid, percentage); + } + }, 0); + }, + + + /** + * @ngdoc function + * @name adjustInfiniteScrollPosition + * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService + * @description This function fires 'needLoadMoreData' or 'needLoadMoreDataTop' event based on scrollDirection + * @param {Grid} grid the grid we're working on + * @param {number} percentage the percentage through the grid that we want to scroll to + */ + adjustInfiniteScrollPosition: function (grid, percentage) { + var scrollEvent = new ScrollEvent(grid, null, null, 'ui.grid.adjustInfiniteScrollPosition'); - if (!grid.options.loadTimout && scrollTop <= infiniteScrollPercentage) { - this.loadData(grid); - return true; + //for infinite scroll, if there are pages upwards then never allow it to be at the zero position so the up button can be active + if ( percentage === 0 && grid.infiniteScroll.scrollUp ) { + scrollEvent.y = {pixels: 1}; } - return false; + else { + scrollEvent.y = {percentage: percentage}; + } + scrollEvent.fireScrollingEvent(); } - /** - * @ngdoc property - * @name infiniteScrollPercentage - * @propertyOf ui.grid.class:GridOptions - * @description This setting controls at what percentage of the scroll more data - * is requested by the infinite scroll - */ + + + + }; return service; }]); @@ -193,7 +345,7 @@ compile: function($scope, $elm, $attr){ return { pre: function($scope, $elm, $attr, uiGridCtrl) { - uiGridInfiniteScrollService.initializeGrid(uiGridCtrl.grid); + uiGridInfiniteScrollService.initializeGrid(uiGridCtrl.grid, $scope); }, post: function($scope, $elm, $attr) { } @@ -202,26 +354,4 @@ }; }]); - module.directive('uiGridViewport', - ['$compile', 'gridUtil', 'uiGridInfiniteScrollService', 'uiGridConstants', - function ($compile, gridUtil, uiGridInfiniteScrollService, uiGridConstants) { - return { - priority: -200, - scope: false, - link: function ($scope, $elm, $attr){ - if ($scope.grid.options.enableInfiniteScroll) { - $scope.grid.api.core.on.scrollEvent($scope, function (args) { - //Prevent circular scroll references, if source is coming from ui.grid.adjustInfiniteScrollPosition() function - if (args.y && (args.source !== 'ui.grid.adjustInfiniteScrollPosition')) { - var percentage = 100 - (args.y.percentage * 100); - if ($scope.grid.scrollDirection === uiGridConstants.scrollDirection.UP) { - percentage = (args.y.percentage * 100); - } - uiGridInfiniteScrollService.checkScroll($scope.grid, percentage); - } - }); - } - } - }; - }]); })(); \ No newline at end of file diff --git a/src/features/infinite-scroll/test/infiniteScroll.spec.js b/src/features/infinite-scroll/test/infiniteScroll.spec.js index dd9af5c09e..b978ad5f99 100644 --- a/src/features/infinite-scroll/test/infiniteScroll.spec.js +++ b/src/features/infinite-scroll/test/infiniteScroll.spec.js @@ -1,74 +1,112 @@ /* global _ */ (function () { - 'use strict'; - describe('ui.grid.infiniteScroll uiGridInfiniteScrollService', function () { + 'use strict'; + describe('ui.grid.infiniteScroll uiGridInfiniteScrollService', function () { - var uiGridInfiniteScrollService; - var grid; - var gridClassFactory; + var uiGridInfiniteScrollService; + var grid; + var gridClassFactory; var uiGridConstants; + var $rootScope; + var $scope; - beforeEach(module('ui.grid.infiniteScroll')); + beforeEach(module('ui.grid.infiniteScroll')); - beforeEach(inject(function (_uiGridInfiniteScrollService_, _gridClassFactory_, _uiGridConstants_) { - uiGridInfiniteScrollService = _uiGridInfiniteScrollService_; - gridClassFactory = _gridClassFactory_; + beforeEach(inject(function (_uiGridInfiniteScrollService_, _gridClassFactory_, _uiGridConstants_, _$rootScope_) { + uiGridInfiniteScrollService = _uiGridInfiniteScrollService_; + gridClassFactory = _gridClassFactory_; uiGridConstants = _uiGridConstants_; - - grid = gridClassFactory.createGrid({}); - - grid.options.columnDefs = [ - {field: 'col1'} - ]; - grid.options.infiniteScroll = 20; - - grid.options.onRegisterApi = function (gridApi) { - gridApi.infiniteScroll.on.needLoadMoreData(function(){ - return []; - }); - gridApi.infiniteScroll.on.needLoadMoreDataTop(function(){ - return []; - }); - - }; - - uiGridInfiniteScrollService.initializeGrid(grid); + $rootScope = _$rootScope_; + $scope = $rootScope.$new(); + + grid = gridClassFactory.createGrid({}); + + grid.options.columnDefs = [ + {field: 'col1'} + ]; + grid.options.infiniteScrollPercentage = 20; + + uiGridInfiniteScrollService.initializeGrid(grid, $scope); spyOn(grid.api.infiniteScroll.raise, 'needLoadMoreData'); spyOn(grid.api.infiniteScroll.raise, 'needLoadMoreDataTop'); - grid.options.data = [{col1:'a'},{col1:'b'}]; + grid.options.data = [{col1:'a'},{col1:'b'}]; - grid.buildColumns(); + grid.buildColumns(); - })); + })); - describe('event handling', function () { - it('should return false if scrollTop is positioned more than 20% of scrollHeight', function() { - var scrollHeight = 100; - var scrollTop = 80; - var callResult = uiGridInfiniteScrollService.checkScroll(grid, scrollTop); - expect(callResult).toBe(false); - }); + describe('event handling', function () { + beforeEach(function() { + spyOn(uiGridInfiniteScrollService, 'loadData').andCallFake(function() {}); + }); + + it('should not request more data if scroll up to 21%', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.UP; + uiGridInfiniteScrollService.handleScroll( { grid: grid, y: { percentage: 0.21 }}); + expect(uiGridInfiniteScrollService.loadData).not.toHaveBeenCalled(); + }); - it('should return false if scrollTop is positioned less than 20% of scrollHeight', function() { - var scrollHeight = 100; - var scrollTop = 19; - var callResult = uiGridInfiniteScrollService.checkScroll(grid, scrollTop); - expect(callResult).toBe(true); - }); + it('should request more data if scroll up to 20%', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.UP; + uiGridInfiniteScrollService.handleScroll( { grid: grid, y: { percentage: 0.20 }}); + expect(uiGridInfiniteScrollService.loadData).toHaveBeenCalled(); + }); - it('should call load data function on grid event raise', function () { - uiGridInfiniteScrollService.loadData(grid); - expect(grid.api.infiniteScroll.raise.needLoadMoreData).toHaveBeenCalled(); - }); + it('should not request more data if scroll down to 79%', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.DOWN; + uiGridInfiniteScrollService.handleScroll( {grid: grid, y: { percentage: 0.79 }}); + expect(uiGridInfiniteScrollService.loadData).not.toHaveBeenCalled(); + }); - it('should call load data top function on grid event raise', function () { + it('should request more data if scroll down to 80%', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.DOWN; + uiGridInfiniteScrollService.handleScroll( { grid: grid, y: { percentage: 0.80 }}); + expect(uiGridInfiniteScrollService.loadData).toHaveBeenCalled(); + }); + }); + + describe('loadData', function() { + it('scroll up and there is data up', function() { grid.scrollDirection = uiGridConstants.scrollDirection.UP; + grid.infiniteScroll.scrollUp = true; + uiGridInfiniteScrollService.loadData(grid); + expect(grid.api.infiniteScroll.raise.needLoadMoreDataTop).toHaveBeenCalled(); + expect(grid.infiniteScroll.previousVisibleRows).toEqual(0); + expect(grid.infiniteScroll.direction).toEqual(uiGridConstants.scrollDirection.UP); }); + it('scroll up and there isn\'t data up', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.UP; + grid.infiniteScroll.scrollUp = false; + + uiGridInfiniteScrollService.loadData(grid); + + expect(grid.api.infiniteScroll.raise.needLoadMoreDataTop).not.toHaveBeenCalled(); + }); + + it('scroll down and there is data down', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.DOWN; + grid.infiniteScroll.scrollDown = true; + + uiGridInfiniteScrollService.loadData(grid); + + expect(grid.api.infiniteScroll.raise.needLoadMoreData).toHaveBeenCalled(); + expect(grid.infiniteScroll.previousVisibleRows).toEqual(0); + expect(grid.infiniteScroll.direction).toEqual(uiGridConstants.scrollDirection.DOWN); + }); + + it('scroll down and there isn\'t data down', function() { + grid.scrollDirection = uiGridConstants.scrollDirection.DOWN; + grid.infiniteScroll.scrollDown = false; + + uiGridInfiniteScrollService.loadData(grid); + + expect(grid.api.infiniteScroll.raise.needLoadMoreData).not.toHaveBeenCalled(); + }); }); - }); + }); })(); \ No newline at end of file diff --git a/src/js/core/directives/ui-grid.js b/src/js/core/directives/ui-grid.js index 4608370b2c..a6cf821597 100644 --- a/src/js/core/directives/ui-grid.js +++ b/src/js/core/directives/ui-grid.js @@ -2,9 +2,9 @@ 'use strict'; angular.module('ui.grid').controller('uiGridController', ['$scope', '$element', '$attrs', 'gridUtil', '$q', 'uiGridConstants', - '$templateCache', 'gridClassFactory', '$timeout', '$parse', '$compile', 'ScrollEvent', + '$templateCache', 'gridClassFactory', '$timeout', '$parse', '$compile', function ($scope, $elm, $attrs, gridUtil, $q, uiGridConstants, - $templateCache, gridClassFactory, $timeout, $parse, $compile, ScrollEvent) { + $templateCache, gridClassFactory, $timeout, $parse, $compile) { // gridUtil.logDebug('ui-grid controller'); var self = this; @@ -59,23 +59,6 @@ } } - function adjustInfiniteScrollPosition (scrollToRow) { - - var scrollEvent = new ScrollEvent(self.grid, null, null, 'ui.grid.adjustInfiniteScrollPosition'); - var totalRows = self.grid.renderContainers.body.visibleRowCache.length; - var percentage = ( scrollToRow + ( scrollToRow / ( totalRows - 1 ) ) ) / totalRows; - - //for infinite scroll, never allow it to be at the zero position so the up button can be active - if ( percentage === 0 ) { - scrollEvent.y = {pixels: 1}; - } - else { - scrollEvent.y = {percentage: percentage}; - } - scrollEvent.fireScrollingEvent(); - - } - function dataWatchFunction(newData) { // gridUtil.logDebug('dataWatch fired'); var promises = []; @@ -114,20 +97,6 @@ $scope.$evalAsync(function() { self.grid.refreshCanvas(true); self.grid.callDataChangeCallbacks(uiGridConstants.dataChange.ROW); - - $timeout(function () { - //Process post load scroll events if using infinite scroll - if ( self.grid.options.enableInfiniteScroll ) { - //If first load, seed the scrollbar down a little to activate the button - if ( self.grid.renderContainers.body.prevRowScrollIndex === 0 ) { - adjustInfiniteScrollPosition(0); - } - //If we are scrolling up, we need to reseed the grid. - if (self.grid.scrollDirection === uiGridConstants.scrollDirection.UP) { - adjustInfiniteScrollPosition(self.grid.renderContainers.body.prevRowScrollIndex + 1 + self.grid.options.excessRows); - } - } - }, 0); }); }); }); diff --git a/src/js/core/factories/GridRenderContainer.js b/src/js/core/factories/GridRenderContainer.js index 495b3eeaa4..7c39c73545 100644 --- a/src/js/core/factories/GridRenderContainer.js +++ b/src/js/core/factories/GridRenderContainer.js @@ -364,48 +364,19 @@ angular.module('ui.grid') if (rowCache.length > self.grid.options.virtualizationThreshold) { if (!(typeof(scrollTop) === 'undefined' || scrollTop === null)) { // Have we hit the threshold going down? - if (!self.grid.options.enableInfiniteScroll && self.prevScrollTop < scrollTop && rowIndex < self.prevRowScrollIndex + self.grid.options.scrollThreshold && rowIndex < maxRowIndex) { + if ( !self.grid.suppressParentScrollDown && self.prevScrollTop < scrollTop && rowIndex < self.prevRowScrollIndex + self.grid.options.scrollThreshold && rowIndex < maxRowIndex) { return; } //Have we hit the threshold going up? - if (!self.grid.options.enableInfiniteScroll && self.prevScrollTop > scrollTop && rowIndex > self.prevRowScrollIndex - self.grid.options.scrollThreshold && rowIndex < maxRowIndex) { + if ( !self.grid.suppressParentScrollUp && self.prevScrollTop > scrollTop && rowIndex > self.prevRowScrollIndex - self.grid.options.scrollThreshold && rowIndex < maxRowIndex) { return; } } var rangeStart = {}; var rangeEnd = {}; - //If infinite scroll is enabled, and we loaded more data coming from redrawInPlace, then recalculate the range and set rowIndex to proper place to scroll to - if ( self.grid.options.enableInfiniteScroll && self.grid.scrollDirection !== uiGridConstants.scrollDirection.NONE && postDataLoaded ) { - var findIndex = null; - var i = null; - if ( self.grid.scrollDirection === uiGridConstants.scrollDirection.UP ) { - findIndex = rowIndex > 0 ? self.grid.options.excessRows : 0; - for ( i = 0; i < rowCache.length; i++) { - if (self.grid.options.rowIdentity(rowCache[i].entity) === self.grid.options.rowIdentity(self.renderedRows[findIndex].entity)) { - rowIndex = i; - break; - } - } - rangeStart = Math.max(0, rowIndex); - rangeEnd = Math.min(rowCache.length, rangeStart + self.grid.options.excessRows + minRows); - } - else if ( self.grid.scrollDirection === uiGridConstants.scrollDirection.DOWN ) { - findIndex = minRows; - for ( i = 0; i < rowCache.length; i++) { - if (self.grid.options.rowIdentity(rowCache[i].entity) === self.grid.options.rowIdentity(self.renderedRows[findIndex].entity)) { - rowIndex = i; - break; - } - } - rangeStart = Math.max(0, rowIndex - self.grid.options.excessRows - minRows); - rangeEnd = Math.min(rowCache.length, rowIndex + minRows + self.grid.options.excessRows); - } - } - else { - rangeStart = Math.max(0, rowIndex - self.grid.options.excessRows); - rangeEnd = Math.min(rowCache.length, rowIndex + minRows + self.grid.options.excessRows); - } + rangeStart = Math.max(0, rowIndex - self.grid.options.excessRows); + rangeEnd = Math.min(rowCache.length, rowIndex + minRows + self.grid.options.excessRows); newRange = [rangeStart, rangeEnd]; }