web-dev-qa-db-fra.com

Compilation de chaînes HTML dynamiques à partir d'une base de données

La situation

Notre application Angular est imbriquée dans une directive appelée Page, soutenue par un contrôleur, qui contient un div avec un attribut ng-bind-html-unsafe. Ceci est assigné à une variable $ scope appelée 'pageContent'. Cette variable reçoit du code HTML généré dynamiquement à partir d'une base de données. Lorsque l'utilisateur passe à la page suivante, la base de données est appelée et la propriété pageContent var est définie sur ce nouveau code HTML, qui est affiché à l'écran via ng-bind-html-unsafe. Voici le code:

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

modèle de directive de page ("page.html" de la propriété templateUrl ci-dessus)

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

contrôleur de 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;
        });

});

Ça marche. Nous voyons le code HTML de la page à partir de la base de données bien rendu dans le navigateur. Lorsque l'utilisateur passe à la page suivante, nous voyons le contenu de la page suivante, etc. Jusqu'ici tout va bien.

Le problème

Le problème ici est que nous voulons avoir un contenu interactif à l'intérieur du contenu d'une page. Par exemple, le code HTML peut contenir une image miniature dans laquelle, lorsque l'utilisateur clique dessus, Angular devrait faire quelque chose de génial, tel que l'affichage d'une fenêtre modale contextuelle. J'ai placé Angular appels de méthode (ng-click) dans les chaînes HTML de notre base de données, mais bien sûr Angular ne reconnaîtra ni les appels de méthode, ni les directives, à moins que analyse la chaîne HTML, les reconnaît et les compile.

Dans notre DB

Contenu pour 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>

Contenu pour 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 page, nous ajoutons ensuite la fonction $ scope correspondante:

contrôleur de page

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

Je n'arrive pas à comprendre comment appeler cette méthode 'doSomethingAwesome' à partir de la chaîne HTML à partir de la base de données. Je réalise que Angular doit analyser la chaîne HTML d'une manière ou d'une autre, mais comment? J'ai lu de vagues rumeurs sur le service $ compile et copié/collé quelques exemples, mais rien ne fonctionne. De plus, la plupart des exemples montrent que le contenu dynamique n'est défini que pendant la phase de liaison de la directive. Nous voudrions que Page reste en vie tout au long de la vie de l'application. Il reçoit, compile et affiche en permanence de nouveaux contenus au fur et à mesure que l'utilisateur navigue dans les pages.

Dans un sens abstrait, vous pouvez dire que nous essayons d’emboîter de manière dynamique des morceaux de Angular au sein d’une application Angular et que nous devons pouvoir les échanger.

J'ai lu plusieurs parties de la documentation Angular plusieurs fois, ainsi que toutes sortes d'articles de blogs et de JS Fiddled avec le code des personnes. Je ne sais pas si je me trompe complètement d'Angular ou si je manque quelque chose de simple, ou peut-être que je suis lent. En tout cas, je pourrais utiliser un conseil.

131
giraffe_sense

ng-bind-html-unsafe ne restitue que le contenu en HTML. Il ne lie pas Angular scope au DOM résultant. Vous devez utiliser le service $compile à cette fin. J'ai créé this plunker pour montrer comment utiliser $compile pour créer une directive rendant le code HTML dynamique entré par les utilisateurs et se liant à la portée du contrôleur. La source est affichée ci-dessous.

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>';
}
247
Buu Nguyen

Dans angular 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);
        });
      }
    };
  });
19

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

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

Vous pouvez utiliser

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

directive pour lier le HTML dynamiquement. Cependant, vous devez obtenir les données via le service $ sce.

S'il vous plaît voir la démo en direct à 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>
3
Rupesh Kumar Tiwari

Essayez ce code ci-dessous pour la liaison HTML par attr

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

Essayez cet élément.html (scope.dynamic); que element.html (attr.dynamic);

1
Ramesh M