angular ng-bind-html e diretiva dentro dele

96

Plunker Link

Eu tenho um elemento que gostaria de vincular html a ele.

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

Isso funciona. Agora, junto com ele, também tenho uma diretiva que está vinculada ao html vinculado:

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

Mas a diretiva uppercom div e âncora não avalia. Como faço isso funcionar?

Amitava
fonte
3
Veja minha resposta aqui stackoverflow.com/questions/17343696/…
Chandermani
@Chandermani não exatamente usando a diretiva dentro de ng-bind-html-unsafe, mas usando o filtro. Mas vai servir, acabei de criar um filtro e passei para a diretiva. Obrigado!
Amitava
@SamSerious, você consegue mostrar como fez o que fez com os filtros?
CMCDragonkai
as soluções acima não lidam com várias alterações de valor, uma solução melhor stackoverflow.com/a/25516311/3343425
fghibellini

Respostas:

188

Eu também estava enfrentando esse problema e depois de horas pesquisando na internet li o comentário de @ Chandermani, que se mostrou a solução. Você precisa chamar uma diretiva 'compilar' com este padrão:

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

Você pode ver um violino funcionando aqui

vkammerer
fonte
1
Na linha 2, ou seja, function(scope, element, attrs), de onde você tirou esses três argumentos, escopo , elemento e atributos ?
spaffy de
1
@spaffy - eles fazem parte da assinatura da estrutura Angular para a linkpropriedade. Eles serão passados ​​automaticamente sempre que linkfor chamado pelo framework Angular. Eles sempre estarão disponíveis.
Ben
1
Bem feito. Você me salvou essas mesmas horas de pesquisa. Estou puxando conteúdo da API REST do modo de exibição do SharePoint, que contém marcação angular como ng-repeat. Sua diretriz fez tudo funcionar. Obrigado!
Phil Nicholas
Obrigado pela sua diretiva, ela corrigiu os problemas que eu estava tendo. Agora o código angular é compilado, mas muitas vezes. Uma repetição de ng com 3 objetos se transforma nos mesmos valores, apenas 3x cada. O que está acontecendo de errado aqui?
Jason
2
Se você estiver usando $sce.trustAsHtmlde outra função para criar o HTML que será "compilado" com esta diretiva, você deve removê-lo. Agradecimentos a @apoplexy
Burak Tokak
36

Obrigado pela ótima resposta vkammerer. Uma otimização que eu recomendaria é desassistir depois que a compilação é executada uma vez. O $ eval na expressão watch pode ter implicações de desempenho.

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

Aqui está um violino bifurcado e atualizado.

user3075469
fonte
Posso ter o vice-versa para isso?
Sanyam Jain
isso não é trabalho em resposta a ajax, mas trabalho de resposta aceito
foozhan
1
Aviso: O violão para esta resposta funciona, mas o .directive()código no código postado na resposta não.
Phil Nicholas
este funcionou para mim. a resposta escolhida acionaria "Erro: $ rootScope: infdig Infinite $ digest Loop"
Gabriel Andrei
Você não deve precisar do explícito $eval- você pode apenas usar attrs.compilediretamente no lugar da função anônima observada. Se você apenas fornecer uma expressão de string, o angular a chamará $evalde qualquer maneira.
Dan King
28

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

Use-o assim:

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

Muito fácil :)

Joël
fonte
1
Tenha cuidado, se você passar algo assim: "$ scope.loadContent = function () {return $ sce.trustAsHtml (require ('html / main-content.html'));};" para ele você pode obter um loop de resumo infinito. Sem o trustAsHtml ele funciona.
Lakatos Gyula
13

Infelizmente não tenho reputação suficiente para comentar.

Eu não consegui fazer isso funcionar por muito tempo. Modifiquei meu ng-bind-htmlcódigo para usar essa diretiva personalizada, mas não consegui remover o $scope.html = $sce.trustAsHtml($scope.html)que era necessário para que o ng-bind-html funcionasse. Assim que removi isso, a função de compilação começou a funcionar.

apoplexia
fonte
6

Para qualquer pessoa que lida com conteúdo que já foi analisado $sce.trustAsHtmlaqui é o que eu tinha que fazer de forma 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 é apenas a linkparte da diretiva, pois estou usando um layout diferente. Você também precisará injetar o $sceserviço $compile.

MStrutt
fonte
-2

Melhor solução que encontrei! Copiei e funcionou exatamente como eu precisava. Obrigado, obrigado, obrigado ...

na função de ligação diretiva que tenho

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

e no modelo de diretiva:

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