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?

Amitava
la source
3
Regardez ma réponse ici stackoverflow.com/questions/17343696/…
Chandermani
@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

vkammerer
la source
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.

user3075469
la source
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 :)

Joël
la source
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.

apoplexie
la source
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.

MStrutt
la source
-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>
yustme
la source