angular ng-bind-html y directiva dentro de él

96

Enlace Plunker

Tengo un elemento al que me gustaría vincularle html.

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

Eso funciona. Ahora, junto con él, también tengo una directiva que está vinculada al html enlazado:

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

Pero la directiva uppercon div y anchor no evalúa. ¿Cómo lo hago funcionar?

Amitava
fuente
3
Mira mi respuesta aquí stackoverflow.com/questions/17343696/…
Chandermani
@Chandermani no usa exactamente la directiva dentro de ng-bind-html-unsafe pero usa filter. Pero servirá, acabo de crear un filtro y pasar a la directiva. ¡Gracias!
Amitava
@SamSerious, ¿puedes mostrar cómo hiciste lo que hiciste con los filtros?
CMCDragonkai
las soluciones anteriores no manejan múltiples cambios del valor una mejor solución stackoverflow.com/a/25516311/3343425
fghibellini

Respuestas:

188

También estaba enfrentando este problema y, después de horas buscando en Internet, leí el comentario de @ Chandermani, que resultó ser la solución. Necesita llamar a una directiva de 'compilación' con este patrón:

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);
            }
        );
    };
}])

Puedes ver un violín de trabajo aquí.

vkammerer
fuente
1
En la línea # 2, es decir. function(scope, element, attrs), ¿de dónde sacaste esos tres argumentos, alcance , elemento y atributos ?
spaffy
1
@spaffy: son parte de la firma de Angular framework para la linkpropiedad. Se pasarán automáticamente cada vez que linkel framework Angular los llame. Siempre estarán disponibles.
Ben
1
Bien hecho. Me salvaste esas mismas horas de búsqueda. Estoy extrayendo contenido de la API REST de la vista de SharePoint, que a su vez contiene marcado angular como ng-repeat. Su directiva hizo que todo funcionara. ¡Gracias!
Phil Nicholas
Gracias por su directiva, solucionó los problemas que estaba teniendo. Ahora el código angular se compila pero demasiadas veces. Una repetición ng con 3 objetos se convierte en los mismos valores solo 3 veces cada uno. ¿Qué va mal aquí?
Jason
2
Si ha estado utilizando $sce.trustAsHtmldesde otra función para crear el HTML que se "compilará" con esta directiva, debe eliminarlo. Gracias a @apoplexy
Burak Tokak
36

Gracias por la gran respuesta vkammerer. Una optimización que recomendaría es dejar de mirar después de que la compilación se ejecute una vez. El $ eval dentro de la expresión de observación podría tener implicaciones en el rendimiento.

    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();
            }
        );
    };
}]);

Aquí hay un violín bifurcado y actualizado.

usuario3075469
fuente
¿Puedo tener lo contrario?
Sanyam Jain
esto no es un trabajo en respuesta a ajax, pero el trabajo de respuesta aceptado
foozhan
1
Advertencia: El violín para esta respuesta funciona, pero el .directive()código en el código publicado en la respuesta no.
Phil Nicholas
este funcionó para mí. la respuesta elegida desencadenaría "Error: $ rootScope: infdig Infinite $ digest Loop"
Gabriel Andrei
No debería necesitar el explícito $eval, puede usarlo attrs.compiledirectamente en lugar de la función anónima observada. Si solo proporciona una expresión de cadena, angular la llamará $evalde todos modos.
Dan King
28

Agregue esta directiva 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);
      });
    }
  };
}]);

Úselo así:

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

Realmente fácil :)

Joël
fuente
1
Tenga cuidado si pasa algo como esto: "$ scope.loadContent = function () {return $ sce.trustAsHtml (require ('html / main-content.html'));};" a él puede obtener un bucle de resumen infinito. Sin el trustAsHtml funciona.
Lakatos Gyula
13

Lamentablemente no tengo suficiente reputación para comentar.

No pude hacer que esto funcionara durante años. Modifiqué mi ng-bind-htmlcódigo para usar esta directiva personalizada, pero no pude eliminar la $scope.html = $sce.trustAsHtml($scope.html)que se requería para que ng-bind-html funcionara. Tan pronto como eliminé esto, la función de compilación comenzó a funcionar.

apoplejía
fuente
6

Para cualquiera que se ocupe de contenido que ya se ha revisado, $sce.trustAsHtmlaquí es lo que tuve que hacer de manera diferente

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();
        });
}

Esta es solo la linkparte de la directiva, ya que estoy usando un diseño diferente. Deberá inyectar el $sceservicio también $compile.

MStrutt
fuente
-2

¡La mejor solución que he encontrado! Lo copié y funciona exactamente como lo necesitaba. Gracias Gracias gracias ...

en la función de enlace de directiva tengo

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

y en plantilla de directiva:

<span compile="renderingElement"></span>
yustme
fuente