angulaire ng-bind-html et directive qu'il contient


96

Lien Plunker

J'ai un élément que je voudrais lier html.

<div ng-bind-html="details" upper></div>

Ça marche. Maintenant, avec cela, j'ai également une directive qui est liée au html lié:

$scope.details = 'Success! <a href="#/details/12" upper>details</a>'

Mais la directive upperavec le div et l'ancre n'évalue pas. Comment le faire fonctionner?



@Chandermani n'utilise pas exactement la directive à l'intérieur de ng-bind-html-unsafe mais utilise un filtre. Mais ça va faire, je viens de créer un filtre et de passer à la directive. Merci!
Amitava

@SamSerious pouvez-vous montrer comment vous avez fait ce que vous avez fait pour les filtres?
CMCDragonkai

les solutions ci-dessus ne gèrent pas plusieurs changements de valeur une meilleure solution stackoverflow.com/a/25516311/3343425
fghibellini

Réponses:


188

J'étais également confronté à ce problème et après des heures de recherche sur Internet, j'ai lu le commentaire de @ Chandermani, qui s'est avéré être la solution. Vous devez appeler une directive 'compile' avec ce modèle:

HTML:

<div compile="details"></div>

JS:

.directive('compile', ['$compile', function ($compile) {
    return function(scope, element, attrs) {
        scope.$watch(
            function(scope) {
                // watch the 'compile' expression for changes
                return scope.$eval(attrs.compile);
            },
            function(value) {
                // when the 'compile' expression changes
                // assign it into the current DOM
                element.html(value);

                // compile the new DOM and link it to the current
                // scope.
                // NOTE: we only compile .childNodes so that
                // we don't get into infinite loop compiling ourselves
                $compile(element.contents())(scope);
            }
        );
    };
}])

Vous pouvez voir un violon qui fonctionne ici


1
Dans la ligne n ° 2, ie. function(scope, element, attrs), d'où avez-vous tiré ces trois arguments, portée , élément et attrs ?
spaffy

1
@spaffy - ils font partie de la signature du framework Angular pour la linkpropriété. Ils seront transmis automatiquement à chaque fois qu'il linksera appelé par le framework Angular. Ils seront toujours disponibles.
Ben

1
Bien joué. Vous m'avez sauvé ces mêmes heures de recherche. Je tire le contenu de l'API REST de la vue SharePoint, qui contient elle-même un balisage angulaire tel que ng-repeat. Votre directive a tout fait fonctionner. Merci!
Phil Nicholas

Merci pour votre directive, elle a résolu les problèmes que j'avais. Maintenant, le code angulaire est compilé mais trop de fois. Un ng-repeat avec 3 objets se transforme en les mêmes valeurs seulement 3x chacune. Qu'est-ce qui ne va pas ici?
Jason

2
Si vous avez utilisé à $sce.trustAsHtmlpartir d'une autre fonction pour créer le HTML qui sera "compilé" avec cette directive, vous devez le supprimer. Merci à @apoplexy
Burak Tokak

36

Merci pour l'excellente réponse vkammerer. Une optimisation que je recommanderais est de ne plus regarder après la compilation une fois. Le $ eval dans l'expression watch pourrait avoir des implications sur les performances.

    angular.module('vkApp')
  .directive('compile', ['$compile', function ($compile) {
      return function(scope, element, attrs) {
          var ensureCompileRunsOnce = scope.$watch(
            function(scope) {
               // watch the 'compile' expression for changes
              return scope.$eval(attrs.compile);
            },
            function(value) {
              // when the 'compile' expression changes
              // assign it into the current DOM
              element.html(value);

              // compile the new DOM and link it to the current
              // scope.
              // NOTE: we only compile .childNodes so that
              // we don't get into infinite loop compiling ourselves
              $compile(element.contents())(scope);

              // Use un-watch feature to ensure compilation happens only once.
              ensureCompileRunsOnce();
            }
        );
    };
}]);

Voici un violon fourchu et mis à jour.


Puis-je avoir l'inverse pour cela?
Sanyam Jain

ce n'est pas un travail en réponse à ajax mais un travail de réponse accepté
foozhan

1
Attention: le violon de cette réponse fonctionne, mais le .directive()code du code affiché dans la réponse ne fonctionne pas.
Phil Nicholas

celui-ci a fonctionné pour moi. la réponse choisie déclencherait "Erreur: $ rootScope: infdig Infinite $ digest Loop"
Gabriel Andrei

Vous ne devriez pas avoir besoin de l'explict $eval- vous pouvez simplement l'utiliser attrs.compiledirectement à la place de la fonction anonyme surveillée. Si vous fournissez simplement une expression de chaîne, angular l'appellera $evalquand même.
Dan King

28

Ajoutez cette directive angular-bind-html-compile

.directive('bindHtmlCompile', ['$compile', function ($compile) {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      scope.$watch(function () {
        return scope.$eval(attrs.bindHtmlCompile);
      }, function (value) {
        // Incase value is a TrustedValueHolderType, sometimes it
        // needs to be explicitly called into a string in order to
        // get the HTML string.
        element.html(value && value.toString());
        // If scope is provided use it, otherwise use parent scope
        var compileScope = scope;
        if (attrs.bindHtmlScope) {
          compileScope = scope.$eval(attrs.bindHtmlScope);
        }
        $compile(element.contents())(compileScope);
      });
    }
  };
}]);

Utilisez-le comme ceci:

<div bind-html-compile="data.content"></div>

Vraiment facile :)


1
Faites attention, si vous passez quelque chose comme ceci: "$ scope.loadContent = function () {return $ sce.trustAsHtml (require ('html / main-content.html'));};" à cela, vous pouvez obtenir une boucle de résumé infinie. Sans trustAsHtml, cela fonctionne.
Lakatos Gyula

13

Malheureusement, je n'ai pas assez de réputation pour commenter.

Je n'ai pas pu faire fonctionner cela pendant des lustres. J'ai modifié mon ng-bind-htmlcode pour utiliser cette directive personnalisée, mais je n'ai pas réussi à supprimer le $scope.html = $sce.trustAsHtml($scope.html)qui était requis pour que ng-bind-html fonctionne. Dès que j'ai supprimé cela, la fonction de compilation a commencé à fonctionner.


6

Pour toute personne traitant du contenu qui a déjà été parcouru, $sce.trustAsHtmlvoici ce que je devais faire différemment

function(scope, element, attrs) {
    var ensureCompileRunsOnce = scope.$watch(function(scope) {
            return $sce.parseAsHtml(attrs.compile)(scope);
        },
        function(value) {
            // when the parsed expression changes assign it into the current DOM
            element.html(value);

            // compile the new DOM and link it to the current scope.
            $compile(element.contents())(scope);

            // Use un-watch feature to ensure compilation happens only once.
            ensureCompileRunsOnce();
        });
}

Ce n'est que la linkpartie de la directive car j'utilise une mise en page différente. Vous devrez injecter le $sceservice ainsi que $compile.


-2

La meilleure solution que j'ai trouvée! Je l'ai copié et ça marche exactement comme j'avais besoin. Merci, merci, merci ...

dans la fonction de liaison directive que j'ai

app.directive('element',function($compile){
  .
  .
     var addXml = function(){
     var el = $compile('<xml-definitions definitions="definitions" />')($scope);
     $scope.renderingElement = el.html();
     }
  .
  .

et dans le modèle de directive:

<span compile="renderingElement"></span>
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.