web-dev-qa-db-fra.com

Comment re-rendre un modèle dans une directive AngularJS?

J'ai créé une directive qui génère des boutons Twitter. Étant donné que les variables de portée de ces boutons peuvent changer, je dois reconstruire le bouton quand cela se produit. Actuellement, j'utilise jQuery pour empty() l'élément lié et pour reconstruire le bouton.

app.directive 'twitterShare', ($timeout, $window) ->
    restrict: 'E'
    template: '<a href="https://Twitter.com/share" class="Twitter-share-button" data-text="{{ text }}" data-url="{{ url }}">Twitter</a>'
    scope:
        text: '@'
        url: '@'
    link: (scope, el, attrs) ->
        scope.$watch 'text', -> rebuild()
        scope.$watch 'url' , -> rebuild()

        rebuild = ->
            $(".Twitter-share-button").remove()
            Tweet = $ '<a>'
            .attr 'href', 'https://Twitter.com/share'
            .attr 'id', 'Tweet'
            .attr 'class', 'Twitter-share-button'
            .attr 'data-lang', 'en'
            .attr 'data-count', 'none'
            .text 'Tweet'

            el.prepend Tweet
            Tweet.attr 'data-text', scope.text
            Tweet.attr 'data-url', scope.url
            $window.twttr.widgets.load()

Existe-t-il un moyen de faire en sorte que la directive refasse complètement le modèle?

24
Soviut

Voici une directive réutilisable que vous pouvez utiliser pour reconstruire le contenu inclus chaque fois qu'un événement est envoyé:

app.directive('relinkEvent', function($rootScope) {
    return {
        transclude: 'element',
        restrict: 'A',
        link: function(scope, element, attr, ctrl, transclude) {
            var previousContent = null;

            var triggerRelink = function() {
                if (previousContent) {
                    previousContent.remove();
                    previousContent = null;
                }

                transclude(function (clone) {
                    element.parent().append(clone);
                    previousContent = clone;
                });

            };

            triggerRelink();                
            $rootScope.$on(attr.relinkEvent, triggerRelink);

        }
    };

});

Voici un jsFiddle montrant comment cela fonctionne: http://jsfiddle.net/robianmcd/ZQeU5/

Remarquez comment le contenu de la zone de saisie est réinitialisé chaque fois que vous cliquez sur le bouton "Déclencher le lien". En effet, la zone de saisie est en cours de suppression et ajoutée au DOM chaque fois que l'événement est déclenché. 

Vous pouvez utiliser cette directive telle quelle ou la modifier de sorte qu'elle soit déclenchée par scope.$watch() au lieu d'un événement.

46
rob

Quelques conseils:

  1. Utilisez le modèle de directive et liez les variables à la portée plutôt que de créer HTML manuellement. Dans ce cas, vous n'avez pas besoin de re-rendre le modèle. Angular le mettra à jour lui-même lorsque les propriétés de la portée changeront.

  2. Utilisez attrs. $ Observe function pour exécuter du code sur le changement de valeur d'attribut

5
Alexander Puchkov

Pour ce faire, vous pouvez également utiliser ng-if.

Par exemple: <myDirective ng-if="renderdirective"></myDirective>

La directive ne sera pas créée tant que votre renderdirective n'est pas vraie. Cela fonctionnera également dans l'autre sens si vous souhaitez supprimer la directive et la laisser être recréée à l'aide des nouvelles valeurs d'attribut.

1
Muthukrishnan

Ce que vous essayez de faire correspond à la fonction de compilation dans les directives, il restitue le code HTML. Avez-vous essayé ça? 

C'est en quelque sorte une façon hacky de le faire, mais en insérant une variable de vérité ng-if dans la directive, et lorsque vous voulez afficher, définissez ou non la variable de vérité:

angularjs: forcer le rendu/rafraîchir un modèle de directive

1
js_gandalf

Petite variation sur la réponse @rob:

import * as angular from 'angular';

class ReRenderDirective implements angular.IDirective {

  public restrict = 'A';
  public replace = false;
  public transclude = true;
  constructor( private $rootScope: angular.IRootScopeService, private $compile: angular.ICompileService ) {

  }

  public link = (
    scope: angular.IScope,
    element: angular.IAugmentedJQuery,
    attr: any,
    modelCtrl: any,
    transclude: angular.ITranscludeFunction ) => {

    let previousContent = null;

    let triggerRelink = () => {
      if ( previousContent ) {
        previousContent.remove();
        previousContent = null;
      }

      transclude(( clone ) => {
        element.append( clone );
        previousContent = clone;

        element.html( attr.compile );
        this.$compile( element.contents() )( scope );
      } );

    };

    triggerRelink();
    this.$rootScope.$on( attr.reRender, triggerRelink );

  }

}

export function reRenderFactory(): angular.IDirectiveFactory {

  var directive = ( $rootScope: angular.IRootScopeService, $compile: angular.ICompileService ) => new ReRenderDirective( $rootScope, $compile );
  directive.$inject = [ '$rootScope', '$compile' ];
  return directive;
}

Utilisez ceci avec:

<div re-render="responsive">
  <header-component/>
</div>

et combinez-le avec une diffusion $ quelque part dans votre code:

this.$rootScope.$broadcast( 'responsive' );

Ce que j’ai fait, c’est écouter le redimensionnement de la page, ce qui déclenchera ensuite la diffusion . Je peux ensuite changer le modèle d’un composant de bureau à mobile. Dans la mesure où le header-component dans l'exemple est inclus, il est rendu et recompilé.

Cela fonctionne comme un charme pour moi.

Merci Rob de m'avoir mis sur la bonne voie.

0
Mattijs