0

I am developing MEAN application. I implement lazy loading scroll to pagination. When page scroll and reach to end sends 2 times post requests to the server. I don't know what is the reason? why does it happen?

Following is my code. Please review and guide me. Hope below code help you to resolve.

firebug snapshot for requests

enter image description here

Controller:

(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]);
}());

Common services

(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;
                    },
            }
        ]);
}());

Lazy loading directive (scroll to pagination)

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>
Dipak
  • 2,086
  • 4
  • 17
  • 48

0 Answers0