web-dev-qa-db-fra.com

Comment gérer le gel du navigateur à cause de ng-repeat imbriqué

J'ai créé un arbre imbriqué pouvant contenir entre 1 et 5 000 éléments. Je peux le faire fonctionner, mais le navigateur\Loading spinner se bloque pendant quelques secondes juste avant d'afficher l'arbre.

Comment puis-je faire en sorte que le navigateur ne gèle jamais?

Comment puis-je savoir quand angularjs a fini de créer ou de rendre ou de calculer (Word n'est pas sûr de dire vrai) toute la liste afin de pouvoir supprimer le chargement de spinner, comme vous pouvez le voir avec scope ng-repeat et idem pour la portée. $ parent. $ last 

Voici le plunker que j'ai créé mais avec des données de démonstration - 

http://plnkr.co/edit/GSZEpHjt5YVxqpg386k5?p=preview

Exemple de jeu de données - http://Pastebin.com/YggqE2MK

Ce n'est pas si mal dans cet exemple, mais par moments, mon navigateur se bloque pendant plus de 10 secondes pour environ 4000 éléments de ma propre configuration avec tous les autres composants.

Ce que j'ai déjà considéré

  • bibliothèque bindonce.js utilisée mais sans grand succès
  • utilisé "::" reliure simple sans grand succès
  • chargez uniquement le premier niveau, puis rendez le niveau suivant lorsque l'utilisateur développe une catégorie, mais mon arbre peut être très aléatoire, peut-être que je n'ai qu'un seul nœud racine avec des nœuds enfants de 100
  • la pagination n'est pas idéale pour le scénario

HTML

  <script type="text/ng-template" id="tree_item">
    <div ng-init="category.expanded=true">
      <div ng-class="{'selected': category.ID==selectedCategoryID}">
        <div class="icon icon16" ng-if="category.Children.length>0" ng-click="$parent.category.expanded=!$parent.category.expanded" ng-class="$parent.category.expanded?'col':'exp'"></div>
        <div class="icon icon-category" ng-class="'icon-category-'+category.TypeType" ng-style="{'border-color':category.Colour}" ng-attr-title="{{category.Status?Res['wpOPT_Status'+category.Status]:''}}"></div>
        <a ng-href="#id={{category.ID}}" ng-class="{'pending-text': category.PendingChange}">{{category.Name}}</a>
      </div>
      <ul class="emlist" ng-show="category.expanded">
        <li ng-repeat="category in category.Children | orderBy:'Name'" ng-include="'tree_item'" ng-class="{'selected': category.ID==selectedCategoryID}" e2-tree-item is-selected="category.ID==selectedCategoryID">
        </li>
      </ul>
    </div>
  </script>

  <div id="CategoryListContainer" class="dragmenu-container initial-el-height">
    <div class="spinner" data-ng-show="status=='loading'"></div>
    <ul id="CategoryList" class="dragmenu-list ng-cloak" ng-show="status=='loaded'">
      <li ng-repeat="category in tree | orderBy:'Name'" ng-include="'tree_item'" e2-tree-item is-selected="category.ID==selectedCategoryID"></li>
    </ul>
  </div>

JS

var app = angular.module('recursionDemo', []);

app.controller("TreeController", function($scope, $timeout) {
  $scope.status = "loading";

  $timeout(function() {
        var result = {
      "GetCategoryTreeResult": [{ // too big data set so I pasted it here http://Pastebin.com/YggqE2MK }];
    $scope.tree = result.GetCategoryTreeResult;

    $scope.status = "loaded";
  }, 3000);
});

app.directive('e2TreeItem', function($timeout) {
  function link(scope, element, ngModel) {
    scope.$watch('isSelected', function(oldVal, newVal) {
      if (scope.isSelected === true) {
        element.parentsUntil('#CategoryListContainer', 'li').each(function(index, item) {
          angular.element(item).scope().category.expanded = true;
        });
      }
    });

    // not working
    //if (scope.$parent.$last) {
    //  console.log("last has been caught");
    //  var appElement = document.querySelector('[ng-app=recursionDemo]');
    //  angular.element(appElement).scope().status = "loaded";
    //}
  }
  return {
    link: link,
    scope: {
      isSelected: '=?'
    }
  };
});
13
Mathematics

METTRE À JOUR:

DEMO: http://plnkr.co/edit/bHMIU8Mx5grht9nod1CW?p=preview

Vous pouvez charger vos données en douceur en modifiant le L10901 de app.js comme ci-dessous. Mais vous devriez remarquer que si vous voulez toujours le commander par son nom et le garder stable, vous devez simplement trier les données source, mais pas de manière angulaire avec "ordre par". (Voulez-vous du code pour trier les données source?)

    function getDataGradually(data, childKey, gap, count, updateCb, finishCb) {
        const lastPositon = [];
        const linearData = [];
        const ret = [];


        function getLinearData(arr, position) {
            arr.forEach(function (obj, index) {
                const pos = position.concat([index]);
                if (obj[childKey] && obj[childKey].length) {
                    var children = obj[childKey];
                    obj[childKey] = [];
                    linearData.Push({
                        obj,
                        pos
                    });
                    getLinearData(children, pos);
                } else {
                    linearData.Push({
                        obj,
                        pos
                    });
                }
            });
        }
        getLinearData(data, []);

        function insertData({
            obj,
            pos
        }) {

            let target = ret;
            pos.forEach(function (i, index) {


                if (index === pos.length - 1) {
                    target[i] = obj;

                } else {
                    target = target[i][childKey];
                }
            });

        }
        let handled = 0;

        function doInsert() {
            let start = handled;
            let end;
            if (handled + count > linearData.length) {
                end = linearData.length;
            } else {
                end = handled + count;
            }
            for (let i = start; i < end; i++) {
                insertData(linearData[i]);
            }
            handled += count;
            if (handled < linearData.length) {
                setTimeout(function () {
                    doInsert();
                    updateCb && updateCb();
                }, gap);
            } else {
                finishCb && finishCb();
            }
        }
        doInsert();



        return ret;
    }

    $scope.tree = getDataGradually(result.GetCategoryTreeResult, "Children", 0, 30, function () {
      $scope.$digest();
    }, function () {
      //finished 
    });
    //$scope.tree = result.GetCategoryTreeResult;
    $scope.status = "loaded";
  }, 3000);

Ancienne réponse:

Vous pouvez obtenir le temps de rendu en utilisant $ timeout. Essayez de changer le L10901 de app.js comme ci-dessous.

        $scope.tree = result.GetCategoryTreeResult;
        console.time("A")
        $timeout(function(){
          console.timeEnd("A");
        });
        $scope.status = "loaded";
      }, 3000);

BTW, je ai "1931.881ms" sur mon PC.

3
blackmiaool

Etant donné que vous liez des données volumineuses au HTML, il génère en interne autant d'observateurs… .. Cela réduira définitivement les performances et risque de geler les systèmes à configuration basse.

Créez cette directive en tant qu'élément plutôt qu'en tant qu'attribut. Effectuez ensuite la manipulation DOM dans la directive .Cela résoudra définitivement le problème.

0
Kalyan

Selon mon expérience:

Aussi, c’est vieux, mais bon article sur les performances angularjs avec ng-repeat Optimiser AngularJS: 1200 ms à 35 ms

0
Matej Marconak

La façon dont je gère cela est:

Dans le contrôleur:

// Consider results to be my main array, which contains 5000 items
var results = [{ ... }, { ... }, ..., ..., { ... }];

$scope.bindResults = results.slice(0, 5);

// Here you can start loading
$scope.status = "loading";

// Inject $interval in controller
var interval = $interval(function() {
    $scope.bindResults = results.slice(0, $scope.bindResults.length + 5);

    if($scope.bindResults.length >= results.length) {
         $interval.cancel(interval);

        // Here you can end loading
        $scope.status = "loading";
    }
}, 10);

En HTML:

<span ng-repeat="x in bindResults"></span>

Cela ne raccrochera pas le navigateur et continuera à remplir les données toutes les 10 ms. Vous pouvez augmenter ce nombre si nécessaire.

0
Aks1357