起因
最近在整理公司的前端库避免出现重复造轮子的情况,这里是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,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,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++; }); } } } } })();