132 votes

AngularJS - compilation des chaînes HTML dynamiques de base de données

La Situation

Imbriquée dans notre Angulaire de l'app est une directive appelée Page, soutenu par un contrôleur, qui contient un div avec un ng-bind-html-dangereux attribut. Il est affecté à un $portée var appelé "au contenu de la page'. Cette variable est affectée HTML générées dynamiquement à partir d'une base de données. Lorsque l'utilisateur retourne à la page suivante, a appelé à la DB est faite, et le au contenu de la page var est définie à ce nouveau format HTML, ce qui est rendu à l'écran par le biais de ng-bind-html-dangereux. Voici le code:

La directive de Page

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

La directive de Page du modèle ("page.html" à partir de la templateUrl de la propriété ci-dessus)

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

Contrôleur de la Page

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

});

Qui fonctionne. Nous voyons le HTML de la page à partir de la DB rendu bien dans le navigateur. Lorsque l'utilisateur retourne à la page suivante, nous voyons le prochain contenu de la page, et ainsi de suite. So far So good.

Le Problème

Le problème ici est que nous voulons avoir du contenu interactif à l'intérieur du contenu d'une page. Par exemple, le code HTML peut contenir une image miniature où, lorsque l'utilisateur clique dessus, Angulaire doit faire quelque chose de génial, comme l'affichage d'une pop-up fenêtre modale. J'ai placé Angulaire des appels de méthode (ng-click) dans le code HTML de chaînes dans notre base de données, mais bien sûr Angulaire ne va pas le reconnaître soit les appels de méthode ou de directives, à moins que de toute façon il analyse le code HTML de la chaîne, les reconnaît et les compile.

Dans notre DB

Le contenu de la Page 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>

Le contenu de la Page 2:

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

De retour dans le contrôleur de la Page, puis on lui ajoute le montant correspondant de la portée de la fonction:

Contrôleur de la Page

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

Je ne peux pas comprendre comment l'appeler 'doSomethingAwesome' méthode à partir de la chaîne HTML à partir de la DB. Je me rends compte Angulaire doit analyser la chaîne HTML en quelque sorte, mais comment? J'ai lu vague mumblings sur le $compiler service, et copié et collé quelques exemples, mais rien ne fonctionne. Aussi, la plupart des exemples afficher du contenu dynamique que se déroule pendant la liaison de la phase de la directive. Nous voulons la Page pour rester en vie tout au long de la durée de vie de l'application. Il reçoit constamment, compile et affiche le nouveau contenu que l'utilisateur feuillette les pages.

Dans un sens abstrait, je suppose que vous pourriez dire que nous essayons de façon dynamique nid morceaux Angulaire dans Angulaire de l'app, et doivent être en mesure de swap et sortir.

J'ai lu les différents bits de l'Angulaire de la documentation à plusieurs reprises, ainsi que toutes sortes de messages de blog, et JS Jouait avec les gens du code. Je ne sais pas si je suis complètement malentendu Angulaire, ou manque juste quelque chose de simple, ou peut-être que je suis lent. En tout cas, je pourrais utiliser un peu des conseils.

249voto

Buu Nguyen Points 19391

ng-bind-html-unsafe seulement rend le contenu au format HTML. Il ne se lient pas Angulaire de la portée pour les DOM. Vous devez utiliser $compile service à cette fin. J'ai créé ce plunker pour montrer comment utiliser $compile pour créer une directive de rendu HTML dynamique saisies par les utilisateurs et la liaison à l'automate. La source est affiché en dessous.

demo.html

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

  <head>
    <script data-require="angular.js@1.0.7" 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>';
}

19voto

Dans la valeur angulaire 1.2.10, la ligne scope.$watch(attrs.dynamic, function(html) { renvoyait une erreur de caractère non valide car elle essayait de surveiller la valeur de attrs.dynamic qui était du texte html.

J'ai corrigé cela en récupérant l'attribut depuis la propriété scope

  scope: { dynamic: '=dynamic'}, 
 

Mon exemple

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

5voto

kwerle Points 441

Trouvé dans un groupe de discussion google. Travaille pour moi.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X