144 votes

Comment compter le nombre total de montres sur une page ?

Existe-t-il un moyen, en JavaScript, de compter le nombre de montres angulaires sur l'ensemble de la page ?

Nous utilisons Batarang mais elle ne répond pas toujours à nos besoins. Notre application est grande et nous souhaitons utiliser des tests automatisés pour vérifier si le nombre de montres augmente trop.

Il serait également utile de compter les montres sur une base par contrôleur.

Modifier Voici ma tentative. Il compte les montres dans tout ce qui a la classe ng-scope.

(function () {
    var elts = document.getElementsByClassName('ng-scope');
    var watches = [];
    var visited_ids = {};
    for (var i=0; i < elts.length; i++) {
       var scope = angular.element(elts[i]).scope();
       if (scope.$id in visited_ids) 
         continue;
       visited_ids[scope.$id] = true;
       watches.push.apply(watches, scope.$$watchers);
    }
    return watches.length;
})();

0 votes

Par contrôleur, c'est facile. Chaque $scope a un tableau $$watchers avec le nombre de surveillants sur ce contrôleur (bien, si vous avez un ng-repeat ou quelque chose qui crée une autre portée, cela ne fonctionne pas si bien). Mais je pense qu'il n'y a aucun moyen de voir tous les observateurs dans l'application entière.

220voto

Words Like Jared Points 3290

(Vous devrez peut-être changer body a html ou partout où vous mettez votre ng-app )

(function () { 
    var root = angular.element(document.getElementsByTagName('body'));

    var watchers = [];

    var f = function (element) {
        angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) { 
            if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
                angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
                    watchers.push(watcher);
                });
            }
        });

        angular.forEach(element.children(), function (childElement) {
            f(angular.element(childElement));
        });
    };

    f(root);

    // Remove duplicate watchers
    var watchersWithoutDuplicates = [];
    angular.forEach(watchers, function(item) {
        if(watchersWithoutDuplicates.indexOf(item) < 0) {
             watchersWithoutDuplicates.push(item);
        }
    });

    console.log(watchersWithoutDuplicates.length);
})();
  • Merci à erilem d'avoir signalé que cette réponse ne comportait pas le champ $isolateScope la recherche et les observateurs étant potentiellement dupliqués dans leur réponse/commentaire.

  • Merci à Ben2307 d'avoir signalé que l'option 'body' devra peut-être être modifié.


Original

J'ai fait la même chose, sauf que j'ai vérifié l'attribut data de l'élément HTML plutôt que sa classe. J'ai analysé le vôtre ici :

http://fluid.ie/

Et j'ai eu 83. J'ai testé le mien et j'ai eu 121.

(function () { 
    var root = $(document.getElementsByTagName('body'));
    var watchers = [];

    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            angular.forEach(element.data().$scope.$$watchers, function (watcher) {
                watchers.push(watcher);
            });
        }

        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };

    f(root);

    console.log(watchers.length);
})();

J'ai aussi mis ça dans le mien :

for (var i = 0; i < watchers.length; i++) {
    for (var j = 0; j < watchers.length; j++) {
        if (i !== j && watchers[i] === watchers[j]) {
            console.log('here');
        }
    }
}

Et rien ne s'est imprimé, donc je suppose que la mienne est meilleure (dans la mesure où elle a trouvé plus de montres) - mais je n'ai pas de connaissances angulaires approfondies pour savoir avec certitude que la mienne n'est pas un sous-ensemble approprié de l'ensemble des solutions.

2 votes

J'ai essayé de faire quelque chose de similaire, et j'ai trouvé cet extrait très utile. Une chose qui, je pense, mérite d'être clarifiée est que 'Root' doit être défini comme l'élément qui possède l'attribut 'ng-app', car c'est là que le $rootScope est conservé. Dans mon application, il se trouve sur la balise 'html'. L'exécution de votre script tel quel a manqué les $watchers dans $rootScope dans mon application.

0 votes

J'ai trouvé un problème avec le code ci-dessus : si un élément enfant a son propre scope, mais maintenant des watchers, est-ce que $scope.$$watchers ne renverrait pas les watchers de son scope parent ? Je pense que vous devriez ajouter avant le push quelque chose comme ceci (j'utilise lodash) : if (!_.contains(watchers,watcher)){ watchers.push(watcher) ; }

2 votes

Cela permet de récupérer tous les observateurs qui sont attachés aux éléments du DOM. Si vous supprimez un élément du DOM, est-ce que le nettoyage de la surveillance associée $scope et $$watcher automatique, ou est-ce qu'ils subissent une perte de performance ?

16voto

DTFagus Points 61

Je pense que les approches mentionnées sont inexactes car elles comptent les observateurs dans le même double scope. Voici ma version d'un bookmarklet :

https://gist.github.com/DTFagus/3966db108a578f2eb00d

Il montre également quelques détails supplémentaires pour l'analyse des observateurs.

13voto

plantian Points 596

Voici une solution bricolée que j'ai mise au point en inspectant les structures de la portée. Elle "semble" fonctionner. Je ne suis pas sûr de la précision de cette solution et elle dépend certainement d'une API interne. J'utilise angularjs 1.0.5.

    $rootScope.countWatchers = function () {
        var q = [$rootScope], watchers = 0, scope;
        while (q.length > 0) {
            scope = q.pop();
            if (scope.$$watchers) {
                watchers += scope.$$watchers.length;
            }
            if (scope.$$childHead) {
                q.push(scope.$$childHead);
            }
            if (scope.$$nextSibling) {
                q.push(scope.$$nextSibling);
            }
        }
        window.console.log(watchers);
    };

0 votes

Cette solution est similaire à ma solution originale (voir l'historique des modifications). J'ai opté pour une approche différente car je pense que parcourir la hiérarchie des scopes ne permettrait pas d'isoler les scopes.

3 votes

Si je crée une portée isolée avec $rootScope.$new(true) ou $scope.$new(true)$scope est pour le contrôleur, alors en parcourant la hiérarchie, on trouve toujours cette portée. Je pense que cela signifie que les prototypes ne sont pas connectés et non que la portée n'est pas dans la hiérarchie.

0 votes

Oui, tous Les scopes descendent du $rootScope, seule l'hérédité est "isolée" dans les scopes isolés. Les scopes isolés sont souvent utilisés dans les directives - ici vous ne voulez pas que les variables d'application des parents interfèrent.

10voto

Nick Steele Points 1

Il existe un nouveau plugin pour chrome qui affiche automatiquement le nombre total actuel de spectateurs et le dernier changement (+/-) à tout moment dans votre application... c'est génial.

https://chrome.google.com/webstore/detail/angular-watchers/nlmjblobloedpmkmmckeehnbfalnjnjk

9voto

Miraage Points 1179

Amélioration mineure pour Des mots comme la réponse de Jared .

(function () {
    var root = $(document.getElementsByTagName('body'));
    var watchers = 0;

    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            watchers += (element.data().$scope.$$watchers || []).length;
        }

        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };

    f(root);

    return watchers;
})();

2 votes

Comme vous n'utilisez pas de sélecteurs jQuery, vous pouvez utiliser angular.element() au lieu de $().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X