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?



@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í.


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.


¿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 :)


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.


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.


-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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.