web-dev-qa-db-fra.com

ng-repeat finish event

Je veux appeler une fonction jQuery ciblant div avec table. Cette table est remplie avec ng-repeat.

Quand je l'appelle

$(document).ready()

Je n'ai aucun résultat.

Aussi

$scope.$on('$viewContentLoaded', myFunc);

n'aide pas.

Est-il possible d'exécuter la fonction juste après la fin de la population ng-repeat? J'ai lu un conseil sur l'utilisation de custom directive, mais je ne sais pas comment l'utiliser avec ng-repeat et ma div ...

203
ChruS

En effet, vous devriez utiliser des directives, et il n'y a pas d'événement lié à la fin d'une boucle ng-Repeat (chaque élément est construit individuellement et a son propre événement). Mais a) utiliser des directives pourrait suffire et b) il existe quelques propriétés spécifiques à ng-Repeat que vous pouvez utiliser pour créer votre événement "on ngRepeat completed".

Plus précisément, si vous souhaitez simplement styler/ajouter des événements à l’ensemble de la table, vous pouvez le faire en utilisant une directive qui englobe tous les éléments ngRepeat. D'autre part, si vous souhaitez traiter chaque élément de manière spécifique, vous pouvez utiliser une directive dans ngRepeat, qui agira sur chaque élément après sa création.

Ensuite, il y a les propriétés $index, $first, $middle et $last que vous pouvez utiliser pour déclencher des événements. Donc pour ce HTML:

<div ng-controller="Ctrl" my-main-directive>
  <div ng-repeat="thing in things" my-repeat-directive>
    thing {{thing}}
  </div>
</div>

Vous pouvez utiliser des directives comme ceci:

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('color','blue');
    if (scope.$last){
      window.alert("im the last!");
    }
  };
})
.directive('myMainDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('border','5px solid red');
  };
});

Voyez-le en action dans ce Plunker . J'espère que ça aide!

238
Tiago Roldão

Si vous voulez simplement exécuter du code à la fin de la boucle, voici une variante un peu plus simple qui ne nécessite pas de gestion d'événements supplémentaire:

<div ng-controller="Ctrl">
  <div class="thing" ng-repeat="thing in things" my-post-repeat-directive>
    thing {{thing}}
  </div>
</div>
function Ctrl($scope) {
  $scope.things = [
    'A', 'B', 'C'  
  ];
}

angular.module('myApp', [])
.directive('myPostRepeatDirective', function() {
  return function(scope, element, attrs) {
    if (scope.$last){
      // iteration is complete, do whatever post-processing
      // is necessary
      element.parent().css('border', '1px solid black');
    }
  };
});

Voir une démonstration en direct.

67
karlgold

Il n'est pas nécessaire de créer une directive, en particulier pour avoir un événement ng-repeat complete.

ng-init fait la magie pour vous.

  <div ng-repeat="thing in things" ng-init="$last && finished()">

le $last s'assure que finished ne se déclenche que lorsque le dernier élément a été rendu dans le DOM.

N'oubliez pas de créer l'événement $scope.finished.

Bonne codage !!

EDIT: 23 Oct 2016

Si vous souhaitez également appeler la fonction finished lorsqu'il n'y a aucun élément dans le tableau, vous pouvez utiliser la solution suivante

<div style="display:none" ng-init="things.length < 1 && finished()"></div>
//or
<div ng-if="things.length > 0" ng-init="finished()"></div>

Ajoutez simplement la ligne ci-dessus en haut de l’élément ng-repeat. Il vérifiera si le tableau n'a aucune valeur et appellera la fonction en conséquence.

Par exemple.

<div ng-if="things.length > 0" ng-init="finished()"></div>
<div ng-repeat="thing in things" ng-init="$last && finished()">
58
Vikas Bansal

Voici une directive repeat-done qui appelle une fonction spécifiée lorsque la valeur est true. J'ai trouvé que la fonction appelée doit utiliser $ timeout avec intervalle = 0 avant de manipuler DOM, telle que l'initialisation des info-bulles sur les éléments rendus. jsFiddle: http://jsfiddle.net/tQw6w/

Dans $ scope.layoutDone, essayez de commenter la ligne $ timeout et de ne pas commenter "NOT CORRECT!" ligne pour voir la différence dans les info-bulles.

<ul>
    <li ng-repeat="feed in feedList" repeat-done="layoutDone()" ng-cloak>
    <a href="{{feed}}" title="view at {{feed | hostName}}" data-toggle="tooltip">{{feed | strip_http}}</a>
    </li>
</ul>

JS:

angular.module('Repeat_Demo', [])

    .directive('repeatDone', function() {
        return function(scope, element, attrs) {
            if (scope.$last) { // all are rendered
                scope.$eval(attrs.repeatDone);
            }
        }
    })

    .filter('strip_http', function() {
        return function(str) {
            var http = "http://";
            return (str.indexOf(http) == 0) ? str.substr(http.length) : str;
        }
    })

    .filter('hostName', function() {
        return function(str) {
            var urlParser = document.createElement('a');
            urlParser.href = str;
            return urlParser.hostname;
        }
    })

    .controller('AppCtrl', function($scope, $timeout) {

        $scope.feedList = [
            'http://feeds.feedburner.com/TEDTalks_video',
            'http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/',
            'http://sfbay.craigslist.org/eng/index.rss',
            'http://www.slate.com/blogs/trending.fulltext.all.10.rss',
            'http://feeds.current.com/homepage/en_US.rss',
            'http://feeds.current.com/items/popular.rss',
            'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
        ];

        $scope.layoutDone = function() {
            //$('a[data-toggle="tooltip"]').tooltip(); // NOT CORRECT!
            $timeout(function() { $('a[data-toggle="tooltip"]').tooltip(); }, 0); // wait...
        }

    })
41
Joseph Oster

Voici une approche simple utilisant ng-init qui ne nécessite même pas de directive personnalisée. Cela a bien fonctionné pour moi dans certains scénarios, par exemple. ayant besoin de faire défiler automatiquement un élément de plusieurs éléments répétés jusqu'à un élément particulier lors du chargement de la page, la fonction de défilement doit donc attendre que le ng-repeat ait terminé le rendu dans le DOM avant de pouvoir se déclencher.

<div ng-controller="MyCtrl">
    <div ng-repeat="thing in things">
        thing: {{ thing }}
    </div>
    <div ng-init="fireEvent()"></div>
</div>

myModule.controller('MyCtrl', function($scope, $timeout){
    $scope.things = ['A', 'B', 'C'];

    $scope.fireEvent = function(){

        // This will only run after the ng-repeat has rendered its things to the DOM
        $timeout(function(){
            $scope.$broadcast('thingsRendered');
        }, 0);

    };
});

Notez que cela n’est utile que pour les fonctions que vous devez appeler une fois après le rendu initial de ng-repeat. Si vous avez besoin d'appeler une fonction chaque fois que le contenu de ng-repeat est mis à jour, vous devrez utiliser l'une des autres réponses de ce fil avec une directive personnalisée.

29
dshap

En complément de la réponse de Pavel , il serait plus facile de comprendre et de comprendre:

<ul>
    <li ng-repeat="item in items" 
        ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>

Pourquoi pensez-vous que angular.noop existe-t-il en premier lieu ...?

Avantages:

Vous n'êtes pas obligé d'écrire une directive pour cela ...

27
deostroll

Peut-être une approche un peu plus simple avec ngInit et la méthode debounce de Lodash sans la nécessité d'une directive personnalisée:

Manette:

$scope.items = [1, 2, 3, 4];

$scope.refresh = _.debounce(function() {
    // Debounce has timeout and prevents multiple calls, so this will be called 
    // once the iteration finishes
    console.log('we are done');
}, 0);

Modèle:

<ul>
    <li ng-repeat="item in items" ng-init="refresh()">{{item}}</li>
</ul>

Mise à jour

Il existe même une solution AngularJS pure plus simple utilisant un opérateur ternaire:

Modèle:

<ul>
    <li ng-repeat="item in items" ng-init="$last ? doSomething() : null">{{item}}</li>
</ul>

Sachez que ngInit utilise la phase de compilation préalable à la liaison - c’est-à-dire que l’expression est appelée avant le traitement des directives enfant. Cela signifie qu'un traitement asynchrone pourrait être requis.

20
Pavel Horal

Lorsque vous vérifiez la variable scope.$last, vous devrez peut-être également envelopper votre déclencheur avec un setTimeout(someFn, 0). Un setTimeout 0 est une technique acceptée en javascript et il était impératif que mon directive fonctionne correctement.

4
Cody
<div ng-repeat="i in items">
        <label>{{i.Name}}</label>            
        <div ng-if="$last" ng-init="ngRepeatFinished()"></div>            
</div>

Ma solution a été d'ajouter un div pour appeler une fonction si l'élément était le dernier d'une répétition.

3
scotty3

Il s’agit d’une amélioration des idées exprimées dans d’autres réponses afin de montrer comment accéder aux propriétés ngRepeat ($ index, $ premier, $ moyen, $ dernier, $ pair, $ impair) lorsque vous utilisez la syntaxe déclarative et isolate scope ( Google a recommandé la meilleure pratique) avec une directive élément. Notez la différence principale: scope.$parent.$last.

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return {
    restrict: 'E',
    scope: {
      someAttr: '='
    },
    link: function(scope, element, attrs) {
      angular.element(element).css('color','blue');
      if (scope.$parent.$last){
        window.alert("im the last!");
      }
    }
  };
});
3
JoshuaDavid

je voudrais ajouter une autre réponse, car la réponse précédente indique que le code nécessaire pour s'exécuter après l'exécution de ngRepeat est un code angular qui, dans ce cas, donne une solution simple et efficace, Certains sont plus génériques que d'autres, et si c'est important le stade du cycle de vie, vous pouvez jeter un oeil à blog de Ben Nadel , à l'exception de l'utilisation de $ parse au lieu de $ eval.

mais selon mon expérience, comme l'OP l'indique, il exécute généralement des plugins ou des méthodes JQuery sur le DOM compilé, ce qui m'a amené à penser que la solution la plus simple consiste à créer une directive avec setTimeout, car la fonction setTimeout est poussée jusqu’à la fin de la file d’attente du navigateur, c’est toujours juste après que tout soit terminé en mode angulaire, généralement ngReapet, qui se poursuit après la fonction postLinking de ses parents

angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
  return function(scope, element, attrs) {        
    setTimeout(function doWork(){
      //jquery code and plugins
    }, 0);        
  };
});

pour ceux qui se demandent dans ce cas pourquoi ne pas utiliser $ timeout, cela provoque un autre cycle de digestion complètement inutile

2
bresleveloper

Je l'ai fait de cette façon.

Créer la directive

function finRepeat() {
    return function(scope, element, attrs) {
        if (scope.$last){
            // Here is where already executes the jquery
            $(document).ready(function(){
                $('.materialboxed').materialbox();
                $('.tooltipped').tooltip({delay: 50});
            });
        }
    }
}

angular
    .module("app")
    .directive("finRepeat", finRepeat);

Après que vous l'ajoutiez sur l'étiquette où ce ng-repeat

<ul>
    <li ng-repeat="(key, value) in data" fin-repeat> {{ value }} </li>
</ul>

Et prêt à être exécuté à la fin de la répétition.

2
Mvochoa

Je devais rendre des formules en utilisant MathJax après la fin de ng-repeat, aucune des réponses ci-dessus ne résolut mon problème, alors je fis comme ci-dessous. C'est pas une solution intéressante, mais a fonctionné pour moi ...

<div ng-repeat="formula in controller.formulas">
    <div>{{formula.string}}</div>
    {{$last ? controller.render_formulas() : ""}}
</div>
1
João Paulo

J'ai trouvé une réponse ici bien pratiqué, mais il fallait encore ajouter un délai

Créez la directive suivante:

angular.module('MyApp').directive('emitLastRepeaterElement', function() {
return function(scope) {
    if (scope.$last){
        scope.$emit('LastRepeaterElement');
    }
}; });

Ajoutez-le à votre répéteur en tant qu'attribut, comme ceci:

<div ng-repeat="item in items" emit-last-repeater-element></div>

Selon Radu:

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {mycode});

Pour moi cela fonctionne, mais je dois encore ajouter un setTimeout

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {
setTimeout(function() { 
    mycode
}, 100); });
0
Blomersi