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.