Подгрузка новых данных при скролле в AngularJS
Нужно показывать достаточно длинный (но не бесконечный) список фотографий, о котором известно, что в зависимости от разрешения фото могут показываться по одной или по две в ряд.
Делается это только на одном экране, а не во всем приложении (т.е. надо снимать обработчики событий и навешивать их снова при заходе на нужный экран).
Да, и еще — у нас нет jQuery, только AngularJS 1.0.8.
Сначала было решено делать все через сервис навешивания обработчиков событий, который снимает все обработчики при смене урла.
Но т.к. AngularJS-ное подобие не поддерживает нэймспэйсы для событий, то будут сниматься все обработчики на элементе, что неправильно.
Поэтому было решено сделать так:
app.js
app.run(['$rootScope', function($rootScope) {
/**
* To proxy global window events to angular's
*/
(function ()
{
"use strict";
var rotateEvent = 'onorientationchange' in window ? 'orientationchange' : 'resize';
angular.element($window).
bind('scroll', function () {
$rootScope.$broadcast('scrolled.window');
}).
bind(rotateEvent, function () {
$rootScope.$broadcast('rotated.window');
});
}());
}]);
Когда уходим на другой контроллер, $scope уничтожится, и обработчик вместе с ним.
Шаблон:
<div> <ul> <li ng-repeat="user in users"> <a ng-href="#/users/[[user.id]]"><img ng-src-preload="[[ user | avatar ]]" alt="" class="js-top100-photo" /></a> </li> </ul> </div>
Сервисы:
.factory('Throttle', ['$rootScope', function throttle($rootScope) {/*будет показан отдельно*/}])
.factory('HelpersService', ['$rootScope', function HelpersService($rootScope) {
var docEl = document.documentElement, docBody = document.body;
return {
/**
* Window height
*/
getDocumentHeight: function getDocumentHeight()
{
return window.innerHeight || docEl.clientHeight || docBody.clientHeight || docEl.offsetHeight || docBody.offsetHeight;
},
/**
* Scrolled distance
*/
getDocumentScrollTop: function getDocumentScrollTop()
{
return window.pageYOffset || window.scrollY || docEl.scrollTop || docBody.scrollTop;
}
};
}]);
Контроллер
controller('Top100ListCtrl', ['$scope', '$routeParams', 'HelpersService', 'Throttle',
function Top100ListCtrl($scope, $routeParams, HelpersService, throttle) {
...
function loadMore()
{
// ...
}
/**
* Everything below is for infinite scroll-like behaviour.
* When user scrolls close to the bottom, more data loaded from server.
* Js needs to know about DOM:
* the size of one picture (that's why we need selector 'js-top100-photo');
* the size of the window;
* that there is only one or two pictures in a row.
*/
/**
* Getting all photos from DOM by selectors.
* Made only once.
*/
var photo1,
photo2,
photo3;
function getPhotos()
{
if (! photo1)
{
var rawPhotos = document.getElementsByClassName('js-top100-photo');
photo1 = rawPhotos[0] ? angular.element(rawPhotos[0]) : null;
photo2 = rawPhotos[0] ? angular.element(rawPhotos[1]) : null;
photo3 = rawPhotos[0] ? angular.element(rawPhotos[2]) : null;
}
return {photo1: photo1, photo2: photo2, photo3: photo3};
}
/**
* Calculating and storing all heights.
* Made on screen load and on screen rotation.
*/
var twoInARow, rowHeight, winHeight;
function calcHeights(photos)
{
var rect1 = photos.photo1[0].getBoundingClientRect(),
rect2 = photos.photo2[0].getBoundingClientRect(),
rect3 = photos.photo3[0].getBoundingClientRect(),
photoHeight = rect1.height;
twoInARow = (rect1.top + photoHeight) > rect2.top;
rowHeight = twoInARow ? rect3.top - rect1.top : rect2.top - rect1.top;
winHeight = HelpersService.getDocumentHeight();
}
/**
* Invoked as a callback on scroll (throttled) and on device rotation.
* @param {Boolean} isWindowChanged if device rotated (need recalc heights)
*/
function onScroll(isWindowChanged) {
var photos = getPhotos();
if (photos.photo1 && photos.photo2)
{
if (isWindowChanged || ! rowHeight) {
calcHeights(photos);
}
if (HelpersService.getDocumentScrollTop() > (($scope.users.length - (twoInARow ? 4 : 2)) * rowHeight - winHeight)) {
loadMore();
}
}
}
// throttling invoсation of callback for scrolling events
var throttledOnScroll = throttle(200, $scope, function () { onScroll(); });
$scope.$on('scrolled.window', throttledOnScroll);
$scope.$on('rotated.window', function () { onScroll(true); });
}]).
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.
2 комментария so far.