138 votes

existe-t-il un rappel après rendu pour la directive Angular JS?

J'ai juste pris mon directive de tirer dans un modèle à ajouter à son élément comme ceci:

# CoffeeScript
.directive 'dashboardTable', ->
  controller: lineItemIndexCtrl
  templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
  (scope, element, attrs) ->
    element.parent('table#line_items').dataTable()
    console.log 'Just to make sure this is run'

# HTML
<table id="line_items">
    <tbody dashboard-table>
    </tbody>
</table>

Je suis également en utilisant un Plugin jQuery appelé DataTables. L'usage général de il est comme ceci: $('table#some_id').dataTable(). Vous pouvez passer dans les données JSON dans le dataTable() appel de fournir les données de la table OU vous pouvez avoir les données déjà sur la page et il fera le reste.. je suis en train de faire celui-ci, ayant les lignes déjà sur la page HTML.

Mais le problème c'est que je dois appeler la dataTable() sur le tableau n ° line_items APRÈS DOM prêt. Mon directive ci-dessus appelle la dataTable() la méthode AVANT le modèle est annexé à la directive de l'élément. Est-il possible que je peux appeler des fonctions APRÈS l'ajouter?

Merci pour votre aide!

Mise à JOUR 1 après Andy réponse:

Je veux faire en sorte que le lien de la méthode n'est seulement appelée APRÈS, tout est sur la page donc j'ai modifié la directive pour un petit test:

# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.find('#sayboo').html('boo')

      controller: lineItemIndexCtrl
      template: "<div id='sayboo'></div>"

    }

Et je ne m'en fait voir "bouh" dans le div#sayboo.

Alors je tente ma jquery datatables appel

.directive 'dashboardTable',  ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.parent('table').dataTable() # NEW LINE

      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

Pas de chance, il y

Ensuite, j'ai essayer d'ajouter du temps :

.directive 'dashboardTable', ($timeout) ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        $timeout -> # NEW LINE
          element.parent('table').dataTable()
        ,5000
      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

Et qui fonctionne. Donc, je me demande ce qui ne va pas dans le non-minuterie version du code?

213voto

parliament Points 1816

Si le second paramètre, "delay", n'est pas fourni, le comportement par défaut consiste à exécuter la fonction une fois le rendu du DOM terminé. Donc, au lieu de setTimeout, utilisez $ timeout:

 $timeout(function () {
    //DOM has finished rendering
});
 

14voto

Roy Truelove Points 6532

J'ai eu le même problème et je crois que la réponse est vraiment pas. Voir Miško commentaire et certains de discussion dans le groupe.

Angulaire pouvez suivre tous les appels de fonction, il permet de manipuler le DOM sont complètes, mais puisque ces fonctions pourraient déclencher async logique qui est encore la mise à jour du DOM après leur retour, Angulaire ne pouvais pas attendre pour le savoir. Tout rappel Angulaire donne peut marcher parfois, mais ne serait pas sûr de compter sur.

Nous avons résolu ce problème de manière heuristique avec un setTimeout, comme vous l'avez fait.

(Veuillez garder à l'esprit que tout le monde est d'accord avec moi, vous devriez lire les commentaires sur les liens ci-dessus et voir ce que vous en pensez.)

7voto

conceptdeluxe Points 733

Bien que ma réponse n'est pas liée à des datatables il aborde la question de la manipulation du DOM et, par exemple, le plugin jQuery initialisation pour les directives utilisées sur des éléments qui ont leur contenu mis à jour de manière asynchrone.

Au lieu de la mise en œuvre d'un délai d'attente on pourrait juste ajouter une montre qui sera à l'écoute des changements de contenu (ou même des déclencheurs externes).

Dans mon cas, j'ai utilisé cette solution pour l'initialisation d'un plugin jQuery, une fois la ng-repeat a été faite, qui a créé mon intérieur DOM - dans un autre cas, je l'ai utilisé juste pour manipuler le DOM, après la portée de la propriété a été modifié au contrôleur. Voici comment j'ai fait ...

HTML:

<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>

JS:

app.directive('myDirective', [ function(){
    return {
        restrict : 'A',
        scope : {
            myDirectiveWatch : '='
        },
        compile : function(){
            return {
                post : function(scope, element, attributes){

                    scope.$watch('myDirectiveWatch', function(newVal, oldVal){
                        if (newVal !== oldVal) {
                            // Do stuff ...
                        }
                    });

                }
            }
        }
    }
}]);

Remarque: au Lieu de simplement jeter le myContent variable bool à mon-directive-montre attribut, on pourrait imaginer n'importe quelle expression.

Remarque: Isoler le champ d'application, comme dans l'exemple ci-dessus ne peut être fait une fois par l'élément d'essayer de faire cela avec plusieurs directives sur le même élément va entraîner une $compiler:multidir Erreur - voir: https://docs.angularjs.org/error/$compiler/multidir

7voto

Andy Joslin Points 23231

Vous pouvez utiliser la fonction 'link', également appelée postLink, qui s'exécute après la mise en place du modèle.

 app.directive('myDirective', function() {
  return {
    link: function(scope, elm, attrs) { /*I run after template is put in */ },
    template: '<b>Hello</b>'
  }
});
 

Si vous envisagez de faire des directives, donnez-lui une lecture, c'est une aide précieuse : http://docs.angularjs.org/guide/directive

3voto

JJ Zabkar Points 510

J'ai eu le même problème, mais en utilisant Angulaire + DataTable avec un fnDrawCallback + ligne de regroupement + $compilé imbriqués les directives. J'ai placé le $timeout dans mon fnDrawCallback de la fonction de correction de la pagination de rendu.

Avant d'exemple, sur la base row_grouping source:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  for(var i=0; i<nTrs.length; i++){
     //1. group rows per row_grouping example
     //2. $compile html templates to hook datatable into Angular lifecycle
  }
}

Après exemple:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  $timeout(function requiredRenderTimeoutDelay(){
    for(var i=0; i<nTrs.length; i++){
       //1. group rows per row_grouping example
       //2. $compile html templates to hook datatable into Angular lifecycle
    }
  ,50); //end $timeout
}

Même un court délai d'attente délai était suffisant pour permettre Angulaire de rendre mon compilé Angulaire directives.

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