Kompilieren dynamischer HTML-Strings aus der Datenbank

Lesezeit: 8 Minuten

Kompilieren dynamischer HTML Strings aus der Datenbank
giraffe_sense

Die Situation

In unsere Angular-App ist eine Anweisung namens Page eingebettet, die von einem Controller unterstützt wird, der ein div mit einem ng-bind-html-unsafe-Attribut enthält. Dies wird einer $scope-Variable namens „pageContent“ zugewiesen. Dieser Variable wird dynamisch generiertes HTML aus einer Datenbank zugewiesen. Wenn der Benutzer zur nächsten Seite blättert, wird die Datenbank aufgerufen, und die pageContent-Variable wird auf dieses neue HTML gesetzt, das auf dem Bildschirm durch ng-bind-html-unsafe gerendert wird. Hier ist der Code:

Page-Direktive

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

Vorlage der Seitendirektive (“page.html” aus der Eigenschaft templateUrl oben)

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

Page-Controller

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

});

Das funktioniert. Wir sehen den HTML-Code der Seite aus der DB gut im Browser gerendert. Wenn der Benutzer zur nächsten Seite blättert, sehen wir den Inhalt der nächsten Seite und so weiter. So weit, ist es gut.

Das Problem

Das Problem hier ist, dass wir interaktive Inhalte innerhalb des Inhalts einer Seite haben wollen. Beispielsweise kann der HTML-Code ein Miniaturbild enthalten, auf das Angular etwas Großartiges tun sollte, wenn der Benutzer darauf klickt, z. B. das Anzeigen eines modalen Popup-Fensters. Ich habe Angular-Methodenaufrufe (ng-click) in die HTML-Strings in unserer Datenbank eingefügt, aber natürlich wird Angular weder Methodenaufrufe noch Direktiven erkennen, es sei denn, es parst den HTML-String irgendwie, erkennt sie und kompiliert sie.

In unserer DB

Inhalt für Seite 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>

Inhalt für Seite 2:

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

Zurück im Page-Controller fügen wir dann die entsprechende $scope-Funktion hinzu:

Page-Controller

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

Ich kann nicht herausfinden, wie man diese ‘doSomethingAwesome’-Methode innerhalb der HTML-Zeichenfolge aus der DB aufruft. Mir ist klar, dass Angular den HTML-String irgendwie analysieren muss, aber wie? Ich habe vages Gemurmel über den $compile-Dienst gelesen und einige Beispiele kopiert und eingefügt, aber nichts funktioniert. Außerdem zeigen die meisten Beispiele, dass dynamische Inhalte nur während der Verknüpfungsphase der Richtlinie festgelegt werden. Wir möchten, dass Page während der gesamten Lebensdauer der App am Leben bleibt. Es empfängt, kompiliert und zeigt ständig neue Inhalte, während der Benutzer durch die Seiten blättert.

In einem abstrakten Sinne könnte man wohl sagen, dass wir versuchen, Angular-Blöcke innerhalb einer Angular-App dynamisch zu verschachteln, und in der Lage sein müssen, sie ein- und auszulagern.

Ich habe verschiedene Teile der Angular-Dokumentation mehrmals gelesen, sowie alle möglichen Blog-Posts und JS mit dem Code von Leuten herumgespielt. Ich weiß nicht, ob ich Angular völlig falsch verstehe oder nur etwas Einfaches übersehe, oder ob ich vielleicht langsam bin. Jedenfalls könnte ich einen Rat gebrauchen.

  • $compile und die ihn umgebenden Docs-Blogs geben mir das Gefühl, dass ich auch langsam bin – obwohl ich das Gefühl habe, dass mein js ziemlich stark ist – ich denke, wenn ich mich damit auseinandersetze, werde ich einen Blog im Idiotenstil erstellen – das ist meine Spezialität!

    – gelandet

    4. Juni 2015 um 9:10 Uhr

ng-bind-html-unsafe rendert nur den Inhalt als HTML. Es bindet den Angular-Bereich nicht an das resultierende DOM. Sie müssen verwenden $compile Dienst für diesen Zweck. ich erschuf dieser Plunker zu demonstrieren, wie man es benutzt $compile um eine Direktive zu erstellen, die von Benutzern eingegebenes dynamisches HTML wiedergibt und an den Geltungsbereich des Controllers bindet. Die Quelle ist unten angegeben.

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

  • Vielen Dank, Buu! Das Erstellen der Attributdirektive und das Hinzufügen der Scope-Watch-Funktion waren die beiden Dinge, die mir gefehlt haben. Jetzt, da dies funktioniert, werde ich mich wohl noch einmal über Direktiven und $compile informieren, um besser zu verstehen, was unter der Haube vor sich geht.

    – giraffe_sense

    12. August 2013 um 18:29 Uhr

  • Ich auch! Das Angular-Team könnte wirklich eine Verbesserung der Dokumentation dazu gebrauchen.

    – Craig Morgan

    19. November 2013 um 8:46 Uhr

  • $compile(ele.contents())(scope); – Diese Zeile löste mein Problem, Winkelkomponenten nicht zu kompilieren, die dynamisch hinzugefügt werden. Danke.

    – Mital Pritmani

    10. April 2014 um 12:39 Uhr

  • @BuuNguyen in teplateURL Angenommen, wenn Sie eine dynamische HTML-Seite mit ng-bind-html einbinden, dann funktioniert die Verwendung von Compiler nicht und gibt einen Fehler von unsicherem Inhalt.

    – anam

    7. Mai 2014 um 10:45 Uhr

  • Ich mag dieses Beispiel, aber es bringt meins nicht zum Laufen. Ich habe eine Switch-Anweisung, die aufgrund der Benutzerauswahl erfolgt, also ist sie dynamisch. Abhängig davon möchte ich eine HTML-enthaltende Direktive einfügen. Die Direktive funktioniert, wenn ich sie in die natürliche Bootstrap-Phase platziere. Allerdings habe ich das, das einfach nicht ausgelöst wird — case ‘info’: $scope.htmlString = $sce.trustAsHtml(‘

    dddzzz

    ‘); brechen; — wenn ich etwas tun möchte wie — $compile($sce.trustAsHtml(‘

    dddzzz

    ‘)); Irgendwelche Ideen für Problemumgehungen usw. …

    – gelandet

    4. Juni 2015 um 9:59 Uhr


Kompilieren dynamischer HTML Strings aus der Datenbank
Alexandros Spyropoulos

In Winkel 1.2.10 die Linie scope.$watch(attrs.dynamic, function(html) { einen ungültigen Zeichenfehler zurückgegeben, weil versucht wurde, den Wert von zu beobachten attrs.dynamic das war HTML-Text.

Ich habe das behoben, indem ich das Attribut aus der Bereichseigenschaft abgerufen habe

 scope: { dynamic: '=dynamic'}, 

Mein Beispiel

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

  • Hallo, wenn ich benutze element.html es gibt mir TypeError zurück: Methode ‘insertBefore’ von null kann nicht aufgerufen werden. Also nach einigem Googeln finde ich, dass ich es verwenden muss element.append Aber wenn ich diese Anweisung an mehreren Stellen verwende, generiert sie multipliziertes HTML. Also erzeugen 2 Direktiven 4 denselben HTML-Code. Danke für deine Antwort.

    – DzeryCZ

    27. Februar 2014 um 23:57 Uhr


  • Ich würde append an Ihrer Stelle nicht verwenden, ich werde mir das heute Abend ansehen und mich bei Ihnen melden. Um ehrlich zu sein, habe ich diese Direktive an einigen Stellen auf einer Seite ohne Probleme verwendet. Ich versuche das Problem zu reproduzieren und melde mich bei Ihnen.

    – Alexandros Spyropoulos

    6. März 2014 um 15:34 Uhr


  • @AlexandrosSpyropoulos Ich teste nur und sehe, dass mein Code auch mit 1.2.12 in Ordnung läuft. Ich denke, Sie haben wahrscheinlich die Deklaration

    im HTML verpasst? (Mit dieser Deklaration überwacht $watch die ‘html’-Eigenschaft im Gültigkeitsbereich, nicht den eigentlichen HTML-Code, wie Sie erwähnt haben, daher sollte es keinen ungültigen Zeichenfehler geben.) Wenn nicht, senden Sie mir den Plunkr, der zeigt, dass es nicht funktioniert, I werde sehen, was falsch ist.

    – Buu Nguyen

    31. März 2014 um 18:46 Uhr

  • Du hast wahrscheinlich Recht. Ich habe damals erwartet, dass html eigentlich eine Variable ist, die html enthält :P. Es ist jedoch eine gute Idee, einen Geltungsbereich für Ihre Direktiven festzulegen. umur.io/…

    – Alexandros Spyropoulos

    4. April 2014 um 0:17 Uhr


  • $compile(ele.contents())(scope); – Diese Zeile löste mein Problem, Winkelkomponenten nicht zu kompilieren, die dynamisch hinzugefügt werden. Danke.

    – Mital Pritmani

    10. April 2014 um 12:40 Uhr

Gefunden in einer Google-Diskussionsgruppe. Funktioniert bei mir.

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

1646316131 446 Kompilieren dynamischer HTML Strings aus der Datenbank
Rupesh Kumar Tiwari

Sie können verwenden

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

Anweisung zum dynamischen Binden von HTML. Allerdings müssen Sie die Daten über den $sce-Dienst abrufen.

Bitte sehen Sie sich die Live-Demo unter an 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>

Probieren Sie diesen folgenden Code zum Binden von HTML über attr aus

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

Probieren Sie diese element.html(scope.dynamic); als element.html(attr.dynamisch);

924110cookie-checkKompilieren dynamischer HTML-Strings aus der Datenbank

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy