我正在开发MEAN应用程序。我实现了延迟加载滚动到分页。页面滚动和到达结束时发送2次请求到服务器。我不知道是什么原因?为什么会这样?
以下是我的代码。请查看并指导我。希望下面的代码可以帮助您解决。
请求的firebug快照
控制器:
(function () {
'use strict';
/**
* Photo Controller for photo features
* @param {type} $rootScope
* @param {type} $scope
* @param {type} $state
* @param {type} $timeout
* @param {type} HzServices
* @param {type} HzPhotoService
* @param {type} Upload * @returns {undefined}
*/
function PhotoCtrl($rootScope, $scope, $state, $window, $routeParams, $timeout, HzServices, HzPhotoService, Upload, HzSocket, notifications) {
/*
* Initiliaze custom scrollbar for following features
* rightside - my album list
* add/edit album - emoji list
*/
//Data loads when $viewContentLoaded.
$scope.$on('$viewContentLoaded', function () {
$scope.counter = 0;
$scope.currentTab = 'all-photos';
$scope.init();
$scope.doAlbumList('', '', '', '', 0, 0);
});
// Scroll to top, when page loads
angular.element("html, body").animate({
scrollTop: 0
}, 0);
/*
*Initialize select2 options for email ids
*/
$scope.init = function () {
$scope.select2Options = {
multiple: true,
'simple_tags': true,
tokenSeparators: [','],
tags: [], // Can be empty list.
selectOnBlur: true,
formatNoMatches: function () {
return '';
},
dropdownCss: {display: 'none'}
};
$scope.myAlbumListConfig = HzServices.initNgScrollbarsConfig();
$scope.myAlbumEmojiConfig = HzServices.initNgScrollbarsConfig();
$scope.frmPhoto = null;
$rootScope.photoIndex = 0;
// Prevent to sent request to server by continuously click on "All photos"
$scope.defaultAlbum = null;
$scope.fullWidthClass = null;
$scope.albums = {};
$scope.albumsCount = null;
$scope.myAlbums = {};
$scope.myAlbumsCount = null;
$scope.photos = {};
$scope.photosByDate = {};
$scope.photosCount = null;
//set model variable for photo pagination
$scope.photoRecordsOffset = 0;
$scope.photoRecordsTotal = 0;
$scope.photoYears = [];
$scope.albumModel = {album_name: "", album_description: "", album_emoji: "", is_auto_generate: 0};
$scope.myAlbumIsAutoGenerated = 1;
$scope.photoModel = {};
$scope.browsePhotoModel = {};
// Set default global variables of edit album
$scope.deletedPhotoTotal = 0;
$scope.deleteFlag = false;
$rootScope.emojis = {};
$scope.arrPhoto = [];
$scope.modelChkBrowsePhoto = {};
$scope.selectedBrowsePhotoIden = [];
$scope.selectedBrowsePhotoIndex = [];
$scope.deSelectedBrowsePhotoIden = [];
$scope.obsoleteBrowsePhotoIden = [];
$scope.checkAllFlag = false;
$scope.overBox = {};
$scope.deleteBox = {};
$scope.moveBox = {};
$scope.overBoxFlag = false;
$scope.deleteBoxFlag = false;
$scope.moveBoxFlag = false;
$scope.currentSelectedPhotoCount = 0;
$scope.currentMovedPhotoCount = 0;
$scope.currentDeletedPhotoCount = 0;
$scope.filesLen = 0;
$scope.counter = 0;
$scope.flag = false;
$scope.allowViewMore = null;
$scope.searchTag = null;
};
/**
* Lists available albums of user
* @param {String} pagination
* @param {String} album
* @param {Number} recordsPerPage
* @param {String} selectedTab
* @param {Boolean} includeMovedPhotos
* @returns {undefined}
*/
$scope.doAlbumList = function (pagination, album, recordsPerPage, selectedTab, includeMovedPhotos, includeDeletedPhotos) {
var includeMovedPhotos = (!!includeMovedPhotos) ? includeMovedPhotos : 0;
var includeDeletedPhotos = (!!includeDeletedPhotos) ? includeDeletedPhotos : 0;
HzServices.showActiveElementTab();
angular.element("ul.error-log li").remove();
//$rootScope.arrData = [];
/*
* Pagination params
*/
var arrPhotoLen = $scope.arrPhoto.length;
var photosLen = $scope.photos.length;
$scope.allowViewMore = photosLen < $scope.photoRecordsTotal;
if (!!pagination && pagination === "VIEWMORE") {
$scope.photoRecordsOffset = $scope.photoRecordsOffset + $rootScope.recordsPerPage;
}
/*
* Following condition is affected specficially when move photo to album and it requires
* to display and update photos for those particular photos. There is not requires to update
* other photos of an album
* When $scope.arrPhoto is blank so it consider, default behaviour of listing, otherwise it lists only
* those photos which are listed in edit photos option.
*/
var data = {};
if (arrPhotoLen === 0) {
data = {
q: $state.params.pid, // Userid
album: album, // album Id
// custom records offset used in move album section and update pagination used both recordsPerPage and recordsOffset
// records per page
recordsPerPage: (!!recordsPerPage) ? recordsPerPage : $rootScope.recordsPerPage,
recordsOffset: $scope.photoRecordsOffset,
// It includes photos which are moved from source album to destination album but source album reference is also present,
// It helps to know which photo is moved from current album to destination album in move album tab.
includeMovedPhotos: includeMovedPhotos,
includeDeletedPhotos: includeDeletedPhotos,
tag: $scope.searchTag
};
} else {
data = {
pid: $scope.arrPhoto
};
}
console.log("data passed to server >>> >>>");
console.log(data);
/*
* Pagination
*/
console.time("album");
var deferred = HzServices.deferred("/api/album/list", 'POST', data);
deferred.then(
function (res) {
$scope.flag = true;
/*
* success in repsonse
* Share common photo & album data across all controllers, directives by angular custom service.
*/
var data = {album: {count: res.data.count.album, data: res.data.album}, albumWithDeletedPhoto: {count: res.data.count.albumWithDeletedPhoto, data: res.data.albumWithDeletedPhoto}, myAlbum: {count: res.data.count.myAlbum, data: res.data.myAlbum}, myAlbumWithDeletedPhoto: {count: res.data.count.myAlbumWithDeletedPhoto, data: res.data.myAlbumWithDeletedPhoto}, photo: {count: res.data.count.photo, data: res.data.photo}};
HzPhotoService.setSharedData(data);
if (false === !!$scope.myAlbum && false === !!$scope.myAlbumId) {
$scope.myAlbum = "All photos";
$scope.myAlbumId = "";
$scope.myAlbumIsAutoGenerated = 1;
}
//Create an array of magnific inline popup content
var sharedData = HzPhotoService.getSharedData();
//Get all albums list
$scope.albums = sharedData.album.data;
$scope.albumsCount = sharedData.album.count;
//Get all albums with deleted records list
$scope.albumsWithDeletedPhoto = sharedData.albumWithDeletedPhoto.data;
$scope.albumsWithDeletedPhotoCount = sharedData.albumWithDeletedPhoto.count;
//Get my custom albums list
$scope.myAlbums = sharedData.myAlbum.data;
$scope.myAlbumsCount = sharedData.myAlbum.count;
//Get my custom album with deleted records list
$scope.myAlbumWithDeletedPhoto = sharedData.myAlbumWithDeletedPhoto.data;
$scope.myAlbumWithDeletedPhotoCount = sharedData.myAlbumWithDeletedPhoto.count;
// Pagination if required
if (pagination === "VIEWMORE") {
$scope.photos = $scope.photos.concat(sharedData.photo.data);
} else {
if ($rootScope.recordsOffset === 0) {
$scope.photos = sharedData.photo.data;
}
}
$scope.photosByDate = HzPhotoService.setSharedDataByDate((pagination === "VIEWMORE") ? true : false);
//BrowsePhotoModel to pass all photos lists to directive
$scope.browsePhotoModel = $scope.photos;
//$scope.uploadResults = $scope.photos;
$scope.photosCount = sharedData.photo.count;
if (includeMovedPhotos === 0 || includeDeletedPhotos === 0) {
$scope.condition = "if";
$scope.photoRecordsTotal = $scope.photosCount;
} else {
$scope.condition = "else";
}
if (selectedTab === "edit_album") {
$scope.doToggleSelectAll($scope.checkAllFlag, $scope.photoRecordsOffset);
}
// Get Emoji list
// if (!!HzPhotoService.getSharedEmojis()) {
if (0 === Object.keys($rootScope.emojis).length) {
$scope.doEmojiList();
}
console.timeEnd("albumprocess");
},
function (res) {
$scope.flag = false;
/*
* Error hading in repsonse
*/
var data = {album: {count: $scope.albumsCount, data: $scope.albums}, albumsWithDeletedPhoto: {count: $scope.albumsWithDeletedPhotoCount, data: $scope.albumsWithDeletedPhoto}, myAlbum: {count: $scope.myAlbumsCount, data: $scope.myAlbums}, myAlbumWithDeletedPhoto: {count: $scope.myAlbumWithDeletedPhotoCount, data: $scope.myAlbumWithDeletedPhoto}, photo: {count: $scope.photosCount, data: $scope.photos}};
HzPhotoService.setSharedData(data);
notifications.showSuccess({
message: "Oops! something went wrong, please try again later.",
hideDelay: 3000, //miliseconds
hide: true // boolean
});
})
.catch(function (response) {
console.error('request error', response.status, response.data);
})
.finally(function () {
console.log("finally finished request");
});
};
}
angular
.module("AppDream")
.controller('PhotoCtrl', ['$rootScope', '$scope', '$state', '$window', '$routeParams', '$timeout', 'HzServices', 'HzPhotoService', 'Upload', 'HzSocket', 'notifications', PhotoCtrl]);
}());
公共服务
(function () {
'use strict';
angular
.module("AppDream")
.factory("HzServices", ['$rootScope', '$http', '$cacheFactory', '$q', '$location', 'notifications',
function ($rootScope, $http, $cacheFactory, $q, $location, notifications) {
return {
/**
* Common $q reponse
* @param {type} url
* @param {type} method
* @param {type} data
* @returns {object}
* @description Common method to get ajax response via $q api
*/
deferred: function (url, method, data) {
var req = {
method: method,
url: url,
headers: {
'Content-Type': "application/json"
},
cache: false
};
if ("GET" === method.toUpperCase()) {
req.params = data;
} else if ("POST" === method.toUpperCase()) {
req.data = data;
} else {
req.data = "";
}
var deferred = $q.defer();
$http(req)
.success(function (data, status, headers, config) {
deferred.resolve(data);
})
.error(function (data, status) {
deferred.reject(data);
});
return deferred.promise;
},
}
]);
}());
延迟加载指令(滚动到分页)
angular
.app("AppDream",
.directive('hzLazyScroll', [
'$rootScope',
'$window',
'$timeout',
function ($rootScope, $window, $timeout) {
return {
link: function (scope, elem, attrs) {
var scrollEnabled, loadData, scrollTrigger = .90, scrollEnabled = true;
$window = angular.element($window);
if (attrs.lazyNoScroll !== null) {
scope.$watch(attrs.lazyNoScroll, function (value) {
scrollEnabled = (value === true) ? false : true;
});
}
if ((attrs.lazyScrollTrigger !== undefined) && (attrs.lazyScrollTrigger > 0 && attrs.lazyScrollTrigger < 100)) {
scrollTrigger = attrs.lazyScrollTrigger / 100;
}
loadData = function () {
var wintop = window.pageYOffset;
var docHeight = window.document.body.clientHeight;
var windowHeight = window.innerHeight//$window.height();
var triggered = (wintop / (docHeight - windowHeight));
if ((scrollEnabled) && (triggered >= scrollTrigger) && (triggered >= scrollTrigger)) {
console.log("Lazy load applies");
console.log("triggers>>>" + triggered);
console.log("scrollTrigger>>>" + scrollTrigger);
return scope.$apply(attrs.lazyScroll);
}
};
$window.on('scroll', loadData);
scope.$on('$destroy', function () {
return $window.off('scroll', loadData);
});
}
};
}
]);
的index.html
<!DOCTYPE html>
<!--[if lt IE 7]>
<html class="lt-ie9 lt-ie8 lt-ie7" lang="en">
<![endif]-->
<!--[if IE 7]>
<html class="lt-ie9 lt-ie8" lang="en">
<![endif]-->
<!--[if IE 8]>
<html class="lt-ie9" lang="en">
<![endif]-->
<!--[if gt IE 8]>
<!-->
<html lang="en" ng-app="AppDream" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="MobileOptimized" content="320" />
<meta name="description" content="Dream" />
<meta http-equiv="Cache-control" content="public"/>
<link rel="shortcut icon" href="/favicon.ico"/>
<!-- All css dependencies -->
</head>
<body ng-controller="hzAppCtrl">
<!-- <hz-loader></hz-loader> -->
<!-- Site Template -->
<notifications-bar class="notifications notification_position_set"></notifications-bar>
<!-- Header section -->
<header id="ccr-header" ui-view="globalHeaderLine1"></header>
<!-- <section id="ccr-site-title" ui-view="globalHeaderLine2"></section> -->
<div class="clear"></div>
<div ng-class="remove_site_scroll_body_sticky === true ? 'padding_top5' : 'site_scroll_body'">
<div class="container_video" ui-view="globalHeaderLine3"></div>
<div class='fake-container' ui-view='globalHeaderLine4'></div>
<div class="container" ui-view="globalHeaderLine5"></div>
<div class="container" ui-view="globalHeaderLine6"></div>
<div class="container" ui-view="globalHeaderLine7"></div>
<div ui-view="featureBar"></div>
<div ui-view="content"></div>
<footer class="clear" ui-view="footer"></footer>
<hz-scroll-top></hz-scroll-top>
</div>
<!-- All javascript depdendencies -->
</body>
</html>
app.routes.js
(function () {
/**
* Authentication interceptor intercepts authentication requests and validate requests.
* @param {type} $window
* @param {type} $cookies
* @returns {Object}
*/
function authInterceptor($window, $cookies) {
return{
request: function (config) {
//console.log("Interceptor called");
if ("" !== $cookies.get('hz-token') && null !== $cookies.get("hz-token") && undefined !== $cookies.get("hz-token")) {
config.headers.Authorization = 'Bearer ' + $cookies.get('hz-token');
}
return config;
},
responseError: function (rejection) {
console.log("rejection");
console.log(rejection);
// do something on error
var errorCode = rejection.status;
if (401 === parseInt(errorCode)) {
$window.location.href = "/";
} else if (parseInt(rejection.status) === -1 || parseInt(rejection.status) === -2) {
return rejection;
} else if (200 !== parseInt(errorCode)) {
$window.location.href = "/#/error/" + errorCode;
///$state.go("app.error", {code: errorCode});
}
}
};
}
/*
* Default Route of application
* @param {$stateProvider} Object
* @param {$routeProvider} Object
* @return
*/
function config($stateProvider, $urlRouterProvider, $httpProvider, $provide) {
$urlRouterProvider.otherwise('home');
$httpProvider.defaults.withCredentials = true;
$provide.factory('GlobalAjaxInterceptor', ['$q', '$rootScope', globalAjaxInterceptor]);
$provide.factory('AuthInterceptor', ['$window', '$cookies', authInterceptor]);
$httpProvider.interceptors.push('AuthInterceptor');
$stateProvider
.state('app', {
url: '/',
views: {
'globalHeaderLine1': {templateUrl: '/partials/headerLine1.html', controller: 'SigninCtrl', controllerAs: 'Signin'},
}
})
/*
* Error page
*/
.state("app.error", {
url: "error/:code",
views: {
'content@': {templateUrl: '/partials/error.html', controller: 'ErrorCtrl', controllerAs: 'Error'}
}
})
.state('app.home', {
url: 'home',
views: {
'globalHeaderLine3@': {templateUrl: '/partials/headerLine3.html', controller: 'HomeCtrl', controllerAs: 'Home'},
'globalHeaderLine4@': {templateUrl: '/partials/headerLine4.html'},
'globalHeaderLine5@': {templateUrl: '/partials/headerLine5.html'},
'globalHeaderLine6@': {templateUrl: '/partials/headerLine6.html'},
'globalHeaderLine7@': {templateUrl: '/partials/headerLine7.html'},
'content@': {templateUrl: '/views/home/home.html', controller: 'HomeCtrl', controllerAs: 'Home'}
}
})
.state("app.photos", {
url: ":pid/photos",
params: {
pid: 'me'
},
views: {
'content@': {templateUrl: '/views/photo/photo.html', controller: 'PhotoCtrl', controllerAs: 'Photo'}
}
});
}
angular
.module('AppDream')
.factory('authInterceptor', ['$window', '$cookies', authInterceptor])
.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$provide', config])
.run(["$rootScope", "$http", "$location", "$state", "$stateParams", "HzServices", "UserService", function ($rootScope, $http, $location, $state, $stateParams, HzServices, UserService) {
HzServices.getDirectories();
$rootScope.recordsPerPage = 20;
$rootScope.recordsOffset = 0;
$rootScope.recordsTotal = 0;
}]);
}());
Photo.html
<div class="container">
<div class="container_main">
<hz-left-side></hz-left-side>
<div class="containarea_right">
<div class="tab_top_tab">
<div ng-include="'/views/photo/settings.html'"></div>
</div>
<div class="middle-first-row-tab-out" ng-class="{newsfeedtab: (pid === 'me' || pid !== 'me' && photosCount > 0)}">
<div class="tabs-container" id="tab-container">
<div class="tabs-frame-content tab_content innerpagetab" id="all-photos" ng-if = "currentTab === 'all-photos'">
<div ng-include="'/views/photo/allPhotos.html'"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="contail_area"></div>
<div class="clear"></div>
allPhotos.html
<div class="tab_left" >
<div class="keyword margin_top margin_bottom float_left fullwidth">
<div ng-if="photos.length > 0 && currentTab === 'all-photos'" hz-lazy-scroll lazy-scroll="doAlbumList('VIEWMORE', myAlbumId)" lazy-scroll-trigger="60" lazy-no-scroll="photos.length > 0 && photoRecordsTotal > 0 && photos.length === photoRecordsTotal">
<hz-browse-photo
photos="photosByDate"
message=""
myAlbums="albums"
ng-model="photoModel"
photo-options="0"
current-tab="currentTab">
</hz-browse-photo>
</div>
<div hz-no-photos-message photos-count="photoRecordsTotal" flag="flag" album="myAlbum" my-album-auto-generated='myAlbumIsAutoGenerated'></div>
</div>