对NgRepeat 1000个项目列表中的按钮单击反应很慢

时间:2015-07-27 15:08:57

标签: angularjs button ng-repeat

我有一个1000多个项目的列表,我在Angular 1.3中使用NgRepeat显示。该列表填充了按钮。我注意到一旦它的大小增加,就会在列表中的click事件上出现重大延迟。当列表只有5-10个项目时,点击是即时的。当列表为1000时,实际处理按钮点击之前大约有2-5秒的延迟。

现在我无法判断这是否是浏览器问题,但我怀疑它与在某处使用过多的侦听器有关,导致浏览器检查它们。

以下是隐藏在其中的罪魁祸首的代码示例:

<div id="side" class="animated" style="min-height: 250px;" 
    data-ng-class="{'fadeInRight':documentDone}" data-ng-style="settings.listCss">
    <div class="col-md-12 text-center" data-ng-style="settings.listCss"><h4>{{label}}</h4> {{inSide}} </div>
    <div data-ng-repeat="doc in ::documents track by $index" id="{{ ::doc.id }}" 
        class="document ng-hide" data-ng-show="doc.show"
        data-ng-init="docSettings = (settingslist[doc.companyid] || settings)" data-ng-style="::docSettings.listCss">
        <div class="col-md-12" data-ng-style="docSettings.listCss">
            <h4>
                <span>{{ ::$index + 1 }}</span>
                <span class="title-in-clusters">
                    {{ ::doc.title }}
                    <button type="button" 
                        class="btn btn-primary btn-xs" 
                        data-ng-click="viewJob(doc, docSettings)" 
                        data-ng-style="docSettings.buttonCss">
                        <strong>VIEW</strong>
                    </button>
                    <a href="{{ ::doc.joburl }}" class="apply" target="_blank">
                        <button type="button" class="btn btn-primary btn-xs" data-ng-click="apply(doc.jobid, doc.companyid)" 
                            data-ng-style="docSettings.buttonCss">
                            <strong>APPLY</strong>
                        </button>
                    </a>
                </span>
            </h4>
        </div>
        <div class="col-md-12" data-ng-style="docSettings.listCss">
            <span class=""><strong>ID: </strong>{{ ::doc.jobid }}</span>
            <img data-ng-if="docSettings.heading.logourl && docSettings.heading.logourl != ''" 
                data-ng-src="{{docSettings.heading.logourl}}" class="side-logo inline-block" id="">
        </div>
        <div class="col-md-12" data-ng-style="docSettings.listCss">
            <strong>Location: </strong><span class="">{{ ::doc.location }}</span>
        </div>                  
        <div class="col-md-12" data-ng-style="docSettings.listCss">
            <strong>Updated Date: </strong><span class="">{{ ::doc.updateddate }}</span>
        </div>
        <div class="col-md-12" data-ng-style="docSettings.listCss">
            <hr data-ng-style="docSettings.listCss">
        </div>
    </div>
</div>

按下按钮时调用的其他功能没有任何冒犯性。

var modalInstance;
$scope.viewJob = function(modalDoc, docSettings) {
    $scope.modalDoc = modalDoc;
    $scope.docSettings = docSettings;
    //the trusAsHtml takes string creates an object, so this will in essence convert string to object
    //make sure you check if it is a string since it could be called multiple times by user (close and reopen same modal)
    if (modalDoc.overview && typeof modalDoc.overview === 'string') {
        $scope.modalDoc.overview = $sce.trustAsHtml(modalDoc.overview);
    }
    if (modalDoc.qualifications && typeof modalDoc.qualifications === 'string') {
        $scope.modalDoc.qualifications = $sce.trustAsHtml(modalDoc.qualifications);
    }
    if (modalDoc.responsibilities && typeof modalDoc.responsibilities === 'string') {
        $scope.modalDoc.responsibilities = $sce.trustAsHtml(modalDoc.responsibilities);
    }

    modalInstance = $modal.open({
      templateUrl: 'app/modal/job_preview.html',
      //templateUrl: 'myModalContent.html',
      scope: $scope
    });
  };

我想优化这段代码,以便它可以切断最多1500个列表,但我不能为我的生活找到罪魁祸首。

我也会采取任何解决方案来减少负荷。就像现在一样,我想我可能会将DOM元素的数量限制为10,并且如果它会导致更好的用户体验,则可以将用户滚动的角度旋转。

更新:

已经尝试了许多方法,从使用bind-once到更复杂的解决方案retard some of the watchers哪些是enat但需要大量的数学来估计哪些项目是可见的等等。

我最终决定了一个最容易做到的解决方案:我列出了我希望显示的项目,并且在鼠标向上或向下滚动时我编辑了列表。

解决方案的第一部分是使用两个指令:

.directive('ngMouseWheelUp', function() {
    return function($scope, $element, $attrs) {
        $element.bind("DOMMouseScroll mousewheel onmousewheel",     
        function(event) {
                    // cross-browser wheel delta
                    var event = window.event || event; // old IE support
                    var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));

                    if(delta > 0) {
                        $scope.$apply(function(){
                            $scope.$eval($attrs.ngMouseWheelUp);
                        });

                        // for IE
                        event.returnValue = false;
                        // for Chrome and Firefox
                        if(event.preventDefault) {
                            event.preventDefault();                        
                        }

                    }
        });
    };
})
.directive('ngMouseWheelDown', function() {

    return function($scope, $element, $attrs) {

        $element.bind("DOMMouseScroll mousewheel onmousewheel", function(event) {

                    // cross-browser wheel delta
                    var event = window.event || event; // old IE support
                    var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));

                    //console.log(event);

                    if(delta < 0) {
                        $scope.$apply(function(){
                            $scope.$eval($attrs.ngMouseWheelDown);
                        });

                        // for IE
                        event.returnValue = false;
                        // for Chrome and Firefox
                        if(event.preventDefault)  {
                            event.preventDefault();
                        }

                    }
        });
    };
})

这两个使我能够禁用右侧列表中的滚动。然后我将从routeScope中的文档创建另外两个数组。每当文档更新时都会生成第一个列表(这是右侧图形用户界面发出的事件的事件监听器),此过滤器只返回将show属性设置为true的数组成员:

var showFilter = function(object) {
    return object.show;
}

这将是我的可见项目数组。从这个数组我创建了另一个显示项目的数组。我为最大尺寸7定义了一个常数,因此最多显示7个项目。当然,我将父容器的溢出设置为none以禁用滚动条。 (我可以添加滚动图形,以便用户知道他以后可以滚动此字段)

然后我将以下指令添加到side div:     data-ng-mouse-wheel-up =“listUp()”data-ng-mouse-wheel-down =“listDown()” 在控制器内部,我定义了listUp和listDown以处理索引和最大大小常量,以确定我应该将哪些元素添加到显示列表的前面或后面。

/**
 * Simulate scrolling up of list by removing bottom element and adding to top
 */ 
$scope.listUp = function() {
    $rootScope.shownDocuments.unshift(getPrev());
    $rootScope.shownDocuments.pop();
}
/**
 * Simulate scrolling down of list by removing top element and adding to bottom
 */
$scope.listDown = function() {
    $rootScope.shownDocuments.push(getNext());
    $rootScope.shownDocuments.shift();
}
/**
 * return next item in visibleDocuments array
 */
var getNext = function() {
    $rootScope.topIndex++;
    if ($rootScope.topIndex > $rootScope.visibleDocuments.length) {
        $rootScope.topIndex -= $rootScope.visibleDocuments.length;
    }
    return ($rootScope.visibleDocuments[($rootScope.topIndex+max_shown_size)%$rootScope.visibleDocuments.length]);
}
/**
 * Return previous item in visibleDocuments array
 */
var getPrev = function() {
    $rootScope.topIndex--;
    if ($rootScope.topIndex < 0) {
        $rootScope.topIndex += $rootScope.visibleDocuments.length;
    }
    return ($rootScope.visibleDocuments[$scope.topIndex]);
}

使用rootScope vs scope主要是因为如果模态不正确地被解除,模态会导致一些不良行为。

最后是视图的重置功能:

/**
 * Resets the list of documents in the visibleList (IE which are visible to client)
 */
var updateVisibleDocuments = function() {
    $rootScope.topIndex = 0;
    $rootScope.visibleDocuments = $rootScope.documents.filter(showFilter);
    //clear view
    $rootScope.shownDocuments = [];
    $rootScope.topIndex = 0;
    for (var i = 0; i < max_shown_size; i++) {
        $rootScope.shownDocuments.push(getNext());
    }
    $rootScope.topIndex = 0;
}

这个解决方案非常有效,因为即使我的列表有100k项目,我也只渲染了7个项目。这极大地限制了观察者的数量。

1 个答案:

答案 0 :(得分:0)

您可能希望尝试分页以减少角度和浏览器在任何时候都需要在屏幕上处理的内容。