dom title - Compilación de cadenas de HTML dinámicas desde la base de datos




attribute meta (5)

Encontrado en un grupo de discusión de google. Funciona para mi.

var $injector = angular.injector(['ng', 'myApp']);
$injector.invoke(function($rootScope, $compile) {
  $compile(element)($rootScope);
});

La situación

Anidado dentro de nuestra aplicación angular, hay una directiva llamada Page, respaldada por un controlador, que contiene un div con un atributo ng-bind-html-unsafe. Esto se asigna a una var $ scope llamada 'pageContent'. A esta var se le asigna HTML generado dinámicamente desde una base de datos. Cuando el usuario cambia a la siguiente página, se realiza una llamada a la base de datos, y la var pageContent se establece en este nuevo HTML, que se representa en pantalla a través de ng-bind-html-unsafe. Aquí está el código:

Directiva de la página

angular.module('myApp.directives')
    .directive('myPage', function ($compile) {

        return {
            templateUrl: 'page.html',
            restrict: 'E',
            compile: function compile(element, attrs, transclude) {
                // does nothing currently
                return {
                    pre: function preLink(scope, element, attrs, controller) {
                        // does nothing currently
                    },
                    post: function postLink(scope, element, attrs, controller) {
                        // does nothing currently
                    }
                }
            }
        };
    });

Plantilla de la directiva de página ("page.html" de la propiedad templateUrl arriba)

<div ng-controller="PageCtrl" >
   ...
   <!-- dynamic page content written into the div below -->
   <div ng-bind-html-unsafe="pageContent" >
   ...
</div>

Controlador de página

angular.module('myApp')
  .controller('PageCtrl', function ($scope) {

        $scope.pageContent = '';

        $scope.$on( "receivedPageContent", function(event, args) {
            console.log( 'new page content received after DB call' );
            $scope.pageContent = args.htmlStrFromDB;
        });

});

Eso funciona. Vemos que el HTML de la página de la base de datos se procesa correctamente en el navegador. Cuando el usuario cambia a la página siguiente, vemos el contenido de la página siguiente, y así sucesivamente. Hasta aquí todo bien.

El problema

El problema aquí es que queremos tener contenido interactivo dentro del contenido de una página. Por ejemplo, el HTML puede contener una imagen en miniatura donde, cuando el usuario hace clic en ella, Angular debería hacer algo increíble, como mostrar una ventana emergente modal. He hecho llamadas a métodos angulares (ng-click) en las cadenas HTML de nuestra base de datos, pero por supuesto Angular no reconocerá las llamadas a métodos ni las directivas a menos que analice de alguna manera la cadena HTML, las reconozca y las compile.

En nuestra base de datos

Contenido para la página 1:

<p>Here's a cool pic of a lion. <img src="lion.png" ng-click="doSomethingAwesone('lion', 'showImage')" > Click on him to see a large image.</p>

Contenido para la página 2:

<p>Here's a snake. <img src="snake.png" ng-click="doSomethingAwesone('snake', 'playSound')" >Click to make him hiss.</p>

De vuelta en el controlador de página, agregamos la función $ scope correspondiente:

Controlador de página

$scope.doSomethingAwesome = function( id, action ) {
    console.log( "Going to do " + action + " with "+ id );
}

No puedo entender cómo llamar a ese método 'doSomethingAwesome' desde la cadena HTML desde el DB. Me doy cuenta de que Angular tiene que analizar la cadena HTML de alguna manera, pero ¿cómo? He leído vagos murmullos sobre el servicio $ compile, y copié y pegué algunos ejemplos, pero nada funciona. Además, la mayoría de los ejemplos muestran que el contenido dinámico solo se establece durante la fase de vinculación de la directiva. Queremos que Page permanezca con vida durante toda la vida de la aplicación. Constantemente recibe, compila y muestra contenido nuevo a medida que el usuario hojea páginas.

En un sentido abstracto, supongo que podría decirse que estamos tratando de anidar dinámicamente trozos de Angular dentro de una aplicación Angular, y necesitamos poder intercambiarlos dentro y fuera.

He leído varias partes de la documentación angular varias veces, así como todo tipo de publicaciones en el blog, y JS violó el código de la gente. No sé si estoy malinterpretando por completo a Angular, o simplemente me estoy perdiendo algo simple, o tal vez soy lento. En cualquier caso, podría usar algunos consejos.


ng-bind-html-unsafe solo representa el contenido como HTML. No vincula el alcance angular al DOM resultante. Debe usar $compile service para ese fin. Creé este plunker para demostrar cómo usar $compile para crear una directiva que represente el HTML dinámico ingresado por los usuarios y vinculante para el alcance del controlador. La fuente se publica a continuación.

demo.html

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script data-require="[email protected]" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Compile dynamic HTML</h1>
    <div ng-controller="MyController">
      <textarea ng-model="html"></textarea>
      <div dynamic="html"></div>
    </div>
  </body>

</html>

script.js

var app = angular.module('app', []);

app.directive('dynamic', function ($compile) {
  return {
    restrict: 'A',
    replace: true,
    link: function (scope, ele, attrs) {
      scope.$watch(attrs.dynamic, function(html) {
        ele.html(html);
        $compile(ele.contents())(scope);
      });
    }
  };
});

function MyController($scope) {
  $scope.click = function(arg) {
    alert('Clicked ' + arg);
  }
  $scope.html = '<a ng-click="click(1)" href="#">Click me</a>';
}

En angular 1.2.10 el scope.$watch(attrs.dynamic, function(html) { de la línea scope.$watch(attrs.dynamic, function(html) { devolvía un error de carácter no válido porque estaba intentando ver el valor de attrs.dynamic que era texto html.

Lo arreglé obteniendo el atributo de la propiedad del alcance

 scope: { dynamic: '=dynamic'}, 

Mi ejemplo

angular.module('app')
  .directive('dynamic', function ($compile) {
    return {
      restrict: 'A',
      replace: true,
      scope: { dynamic: '=dynamic'},
      link: function postLink(scope, element, attrs) {
        scope.$watch( 'dynamic' , function(html){
          element.html(html);
          $compile(element.contents())(scope);
        });
      }
    };
  });

Puedes usar

ng-bind-html https://docs.angularjs.org/api/ng/service/ $ sce

directiva para vincular html dinámicamente. Sin embargo, debe obtener los datos a través del servicio $ sce.

Por favor, vea la demostración en vivo en http://plnkr.co/edit/k4s3Bx

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$sce) {
    $scope.getHtml=function(){
   return $sce.trustAsHtml("<b>Hi Rupesh hi <u>dfdfdfdf</u>!</b>sdafsdfsdf<button>dfdfasdf</button>");
   }
});

  <body ng-controller="MainCtrl">
<span ng-bind-html="getHtml()"></span>
  </body>

Dado que los diferentes navegadores están representando borde, relleno, margen y etc. de manera diferente. Escribí una pequeña función para recuperar las posiciones superior e izquierda de un elemento específico en cada elemento raíz que desee en una dimensión precisa:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

Para recuperar la posición izquierda debes devolver:

    return offsetRect.left - rootRect.left;




html dom dynamic angularjs compilation