起因
最近在整理公司的前端库避免出现重复造轮子的情况,这里是angularjs滚动条到底部后自动加载数据的demo
infinite-scroll.directive.js
底部自动加载是根据 ngInfiniteScroll 更改的,由于Angular Style Guide提倡手动注入解耦,因此ngInfiniteScroll原来的js文件直接集成不了,这里新建directive。因为是多人开发,要考虑到代码合并后变量冲突因此每个模块应该使用闭包。更改后的ngInfiniteScroll:
(function(){
'user strict' //声明使用js严格模式
angular
.module('app')
.directive('infiniteScroll', infiniteScroll)
.value('THROTTLE_MILLISECONDS', null);
//通过$inject来手动注入依赖从而避免重复注入和其他angular找不到变量的原因,增加了可读性。
infiniteScroll.$inject = ['$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS'];
function infiniteScroll ($rootScope, $window, $interval, THROTTLE_MILLISECONDS) {
var directive = {
scope: {
infiniteScroll: '&',
infiniteScrollContainer: '=',
infiniteScrollDistance: '=',
infiniteScrollDisabled: '=',
infiniteScrollUseDocumentBottom: '=',
infiniteScrollListenForEvent: '@'
},
link: link,
restrict: 'EA'
};
return directive;
function link(scope, element, attrs) {
/* */
var changeContainer, checkInterval, checkWhenEnabled, container, handleInfiniteScrollContainer, handleInfiniteScrollDisabled, handleInfiniteScrollDistance, handleInfiniteScrollUseDocumentBottom, handler, height, immediateCheck, offsetTop, pageYOffset, scrollDistance, scrollEnabled, throttle, unregisterEventListener, useDocumentBottom, windowElement;
windowElement = angular.element($window);
scrollDistance = null;
scrollEnabled = null;
checkWhenEnabled = null;
container = null;
immediateCheck = true;
useDocumentBottom = false;
unregisterEventListener = null;
checkInterval = false;
height = function(element) {
element = element[0] || element;
if (isNaN(element.offsetHeight)) {
return element.document.documentElement.clientHeight;
} else {
return element.offsetHeight;
}
};
offsetTop = function(element) {
if (!element[0].getBoundingClientRect || element.css('none')) {
return;
}
return element[0].getBoundingClientRect().top + pageYOffset(element);
};
pageYOffset = function(element) {
element = element[0] || element;
if (isNaN(window.pageYOffset)) {
return element.document.documentElement.scrollTop;
} else {
return element.ownerDocument.defaultView.pageYOffset;
}
};
handler = function() {
var containerBottom, containerTopOffset, elementBottom, remaining, shouldScroll;
if (container === windowElement) {
containerBottom = height(container) + pageYOffset(container[0].document.documentElement);
elementBottom = offsetTop(element) + height(element);
} else {
containerBottom = height(container);
containerTopOffset = 0;
if (offsetTop(container) !== void 0) {
containerTopOffset = offsetTop(container);
}
elementBottom = offsetTop(element) - containerTopOffset + height(element);
}
if (useDocumentBottom) {
elementBottom = height((element[0].ownerDocument || element[0].document).documentElement);
}
remaining = elementBottom - containerBottom;
shouldScroll = remaining <= height(container) * scrollDistance + 1;
if (shouldScroll) {
checkWhenEnabled = true;
if (scrollEnabled) {
if (scope.$$phase || $rootScope.$$phase) {
return scope.infiniteScroll();
} else {
return scope.$apply(scope.infiniteScroll);
}
}
} else {
if (checkInterval) {
$interval.cancel(checkInterval);
}
return checkWhenEnabled = false;
}
};
throttle = function(func, wait) {
var later, previous, timeout;
timeout = null;
previous = 0;
later = function() {
previous = new Date().getTime();
$interval.cancel(timeout);
timeout = null;
return func.call();
};
return function() {
var now, remaining;
now = new Date().getTime();
remaining = wait - (now - previous);
if (remaining <= 0) {
$interval.cancel(timeout);
timeout = null;
previous = now;
return func.call();
} else {
if (!timeout) {
return timeout = $interval(later, remaining, 1);
}
}
};
};
if (THROTTLE_MILLISECONDS != null) {
handler = throttle(handler, THROTTLE_MILLISECONDS);
}
scope.$on('$destroy', function() {
container.unbind('scroll', handler);
if (unregisterEventListener != null) {
unregisterEventListener();
return unregisterEventListener = null;
}
});
handleInfiniteScrollDistance = function(v) {
return scrollDistance = parseFloat(v) || 0;
};
scope.$watch('infiniteScrollDistance', handleInfiniteScrollDistance);
handleInfiniteScrollDistance(scope.infiniteScrollDistance);
handleInfiniteScrollDisabled = function(v) {
scrollEnabled = !v;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
};
scope.$watch('infiniteScrollDisabled', handleInfiniteScrollDisabled);
handleInfiniteScrollDisabled(scope.infiniteScrollDisabled);
handleInfiniteScrollUseDocumentBottom = function(v) {
return useDocumentBottom = v;
};
scope.$watch('infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom);
handleInfiniteScrollUseDocumentBottom(scope.infiniteScrollUseDocumentBottom);
changeContainer = function(newContainer) {
if (container != null) {
container.unbind('scroll', handler);
}
container = newContainer;
if (newContainer != null) {
return container.bind('scroll', handler);
}
};
changeContainer(windowElement);
if (scope.infiniteScrollListenForEvent) {
unregisterEventListener = $rootScope.$on(scope.infiniteScrollListenForEvent, handler);
}
handleInfiniteScrollContainer = function(newContainer) {
if ((newContainer == null) || newContainer.length === 0) {
return;
}
if (newContainer.nodeType && newContainer.nodeType === 1) {
newContainer = angular.element(newContainer);
} else if (typeof newContainer.append === 'function') {
newContainer = angular.element(newContainer[newContainer.length - 1]);
} else if (typeof newContainer === 'string') {
newContainer = angular.element(document.querySelector(newContainer));
}
if (newContainer != null) {
return changeContainer(newContainer);
} else {
throw new Error("invalid infinite-scroll-container attribute.");
}
};
scope.$watch('infiniteScrollContainer', handleInfiniteScrollContainer);
handleInfiniteScrollContainer(scope.infiniteScrollContainer || []);
if (attrs.infiniteScrollParent != null) {
changeContainer(angular.element(element.parent()));
}
if (attrs.infiniteScrollImmediateCheck != null) {
immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck);
}
return checkInterval = $interval((function() {
if (immediateCheck) {
handler();
}
return $interval.cancel(checkInterval);
}));
}
};
})()
directive的引用方式还是和之前相同,这里注意给directive传值的参数名由于每个大写字母被认为是一个独立的词,而每个词之前是以一个连字符分隔的,infiniteScroll对应的是infinite-scroll。
newGoodsList.controller.js
这里是用商品列表做的例子。
(function (){
'use strict'
angular
.module('app')
.controller('newgoodsListController',newgoodsListController);
newgoodsListController.$inject = ['$stateParams', 'goodsListService'];
function newgoodsListController($stateParams, goodsListService){
var vm = this;
vm.return = true;
vm.cateid = $stateParams.cateid;
//定义默认参数
var keyword = '';
//当前页
var pageIndex = 1;
//每页记录数
var pageSize = 7;
//推荐状态 -1(全部)0(不推荐)1(推荐)
var recommendStatus = -1;
//自定义分类ID(热门标签)
var custom_cateId = '';
//品牌ID
var brand_id = '';
//分类ID
var cate_id = '';
//tag_id 标签ID
var tag_id = '';
//minPrice 最小价格
var minPrice = '';
//maxPrice 最大价格
var maxPrice = '';
// 排序 1:时间 2:价格 3:销量
var order = 1;
//true(升序)false(降序).
var isAsc = false;
//声明自动加载的锁,当自动加载的数据在请求的时候不会发起新请求
var busy = false;
getGoodsList();
//排序选择
vm.choose = function (num){
if(order == num){
isAsc = !isAsc;
vm.isAsc = isAsc;
}else{
order = num;
vm.filter_num = num;
}
console.log(order+':'+num+':'+isAsc);
getGoodsList();
}
//商品数据请求
function getGoodsList(){
return goodsListService.getGoodsList(keyword, pageIndex, pageSize, recommendStatus, custom_cateId, brand_id, cate_id, tag_id, minPrice, maxPrice, order, isAsc)
.then(function (data){
console.log(data);
vm.goodsList = data.data;
load();//确保自动加载功能在第一次请求成功后再执行
});
}
function load() {
//读取总页数,并且向上取整
var pageTotal = Math.ceil(vm.goodsList.data.rowCount/pageSize);
//页数自加
pageIndex ++;
//自动加载
vm.loadmore = function(){
console.log('jiazai');
//当请求进行中时不发起新请求
if(busy) return;
//发起请求并且阻止新请求
busy=true;
//当请求的页码不大于总页数时
if(pageIndex<=pageTotal){
return goodsListService.getGoodsList(keyword, pageIndex, pageSize, recommendStatus, custom_cateId, brand_id, cate_id, tag_id, minPrice, maxPrice, order, isAsc)
.then(function (data){
console.log(data.data.data.rows);
//将新请求和数据和原来的数据合并
Array.prototype.push.apply(vm.goodsList.data.rows,data.data.data.rows);
busy=false;
pageIndex++;
});
}
}
}
}
})();
下拉到底部自动加载OK!