Kompilasi string HTML dinamis dari basis data

132

Situasi

Bersarang di dalam aplikasi Angular kami adalah arahan yang disebut Page, didukung oleh controller, yang berisi div dengan atribut ng-bind-html-unsafe. Ini ditugaskan ke $ scope var yang disebut 'pageContent'. Var ini akan diberikan HTML yang dihasilkan secara dinamis dari database. Ketika pengguna membalik ke halaman berikutnya, dipanggil ke DB dibuat, dan pageContent var diatur ke HTML baru ini, yang akan ditampilkan di layar melalui ng-bind-html-tidak aman. Berikut kodenya:

Arahan halaman

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

Templat direktif halaman ("page.html" dari properti templateUrl di atas)

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

Pengontrol halaman

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

});

Itu bekerja. Kami melihat HTML laman dari DB yang ditampilkan dengan baik di peramban. Ketika pengguna membalik ke halaman berikutnya, kami melihat konten halaman berikutnya, dan seterusnya. Sejauh ini baik.

Masalah

Masalahnya di sini adalah bahwa kami ingin memiliki konten interaktif di dalam konten halaman. Misalnya, HTML dapat berisi gambar mini di mana, ketika pengguna mengkliknya, Angular harus melakukan sesuatu yang luar biasa, seperti menampilkan jendela modal pop-up. Saya telah menempatkan panggilan metode Angular (ng-klik) di string HTML di database kami, tapi tentu saja Angular tidak akan mengenali panggilan metode atau arahan kecuali entah bagaimana mem-parsing string HTML, mengenali mereka dan mengkompilasi mereka.

Dalam DB kami

Konten untuk Halaman 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>

Konten untuk Halaman 2:

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

Kembali ke pengontrol halaman, kami kemudian menambahkan fungsi $ scope yang sesuai:

Pengontrol halaman

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

Saya tidak tahu bagaimana memanggil metode 'doSomethingAwesome' dari dalam string HTML dari DB. Saya menyadari Angular harus mengurai string HTML, tetapi bagaimana caranya? Saya telah membaca gumaman samar tentang layanan $ compile, dan menyalin dan menempel beberapa contoh, tetapi tidak ada yang berhasil. Juga, sebagian besar contoh menunjukkan konten dinamis hanya diatur selama fase penautan arahan. Kami ingin Page tetap hidup sepanjang masa aplikasi. Terus menerima, mengkompilasi dan menampilkan konten baru saat pengguna membalik-balik halaman.

Dalam arti abstrak, saya kira Anda bisa mengatakan kami mencoba untuk secara dinamis memotong sarang Angular dalam aplikasi Angular, dan harus dapat menukar mereka masuk dan keluar.

Saya telah membaca berbagai bit dokumentasi Angular beberapa kali, dan juga segala macam posting blog, dan JS Mengotak-atik kode orang. Saya tidak tahu apakah saya benar-benar salah paham tentang Angular, atau hanya melewatkan sesuatu yang sederhana, atau mungkin saya lambat. Bagaimanapun, saya bisa menggunakan beberapa saran.

giraffe_sense
sumber
2
$ compile dan blog docs yang mengelilinginya membuat saya merasa bahwa saya juga lambat - walaupun saya merasa js saya cukup kuat - saya pikir jika saya bisa mengatasi ini saya akan membuat blog gaya idiot - itulah spesialisasi saya!
mendarat pada

Jawaban:

248

ng-bind-html-unsafehanya menjadikan konten sebagai HTML. Itu tidak mengikat lingkup sudut ke DOM yang dihasilkan. Anda harus menggunakan $compilelayanan untuk tujuan itu. Saya membuat plunker ini untuk mendemonstrasikan cara menggunakan $compileuntuk membuat directive rendering dynamic HTML yang dimasukkan oleh pengguna dan mengikat ke lingkup controller. Sumber diposting di bawah ini.

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>';
}
Buu Nguyen
sumber
6
Terima kasih banyak, Buu! Membuat atribut directive dan menambahkan fungsi scope watch adalah dua hal yang saya lewatkan. Sekarang ini berfungsi, saya kira saya akan membaca lagi tentang arahan dan $ kompilasi, untuk lebih memahami apa yang terjadi di bawah tenda.
giraffe_sense
11
Saya juga! Tim Angular benar-benar dapat melakukan dengan memperbaiki dokumen tentang hal ini.
Craig Morgan
$compile(ele.contents())(scope);- baris ini memecahkan masalah saya tidak mengkompilasi komponen sudut yang ditambahkan secara dinamis. Terima kasih.
Mital Pritmani
@ BuuNguyen di dalam teplateURL misalkan jika Anda memasukkan beberapa halaman htmnl dinamis menggunakan ng-bind-html, kemudian menggunakan kompilasi tidak berfungsi memberikan kesalahan dari beberapa konten yang tidak aman di sisi lain menggunakan trustAsHTml hanya menghapus kesalahan tidak aman tidak dikompilasi, ada saran?
anam
1
Saya suka contoh ini tetapi itu tidak membuat saya bekerja. Saya memiliki pernyataan beralih yang terjadi karena pilihan pengguna sehingga dinamis. Bergantung pada itu saya ingin memasukkan html yang berisi arahan. Arahan berfungsi jika saya menempatkannya dalam fase bootstrap alami. Namun saya punya ini yang tidak memecat --- case 'info': $ scope.htmlString = $ sce.trustAsHtml ('<div dynamic = "htmlString"> dddzzz </div>'); istirahat; --- ketika saya ingin melakukan sesuatu seperti --- $ compile ($ sce.trustAsHtml ('<div dynamic = "htmlString"> dddzzz </div>')); Ada ide tentang penyelesaian masalah dll ...
mendarat pada
19

Di sudut 1.2.10 baris scope.$watch(attrs.dynamic, function(html) {mengembalikan kesalahan karakter yang tidak valid karena mencoba untuk melihat nilai attrs.dynamicyang merupakan teks html.

Saya memperbaikinya dengan mengambil atribut dari properti scope

 scope: { dynamic: '=dynamic'}, 

Contoh saya

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);
        });
      }
    };
  });
Alexandros Spyropoulos
sumber
Halo, Jika saya menggunakan element.html itu mengembalikan saya TypeError: Tidak dapat memanggil metode 'insertBefore' dari nol. Jadi setelah beberapa googling tentang itu saya menemukan bahwa saya harus menggunakan element.append Tapi Jika saya menggunakan arahan itu di beberapa tempat - itu menghasilkan HTML multiplikat. Jadi 2 arahan menghasilkan 4 kode HTML yang sama. Terima kasih atas jawaban anda.
DzeryCZ
Saya tidak akan menggunakan menambahkan di tempat Anda, saya akan melihatnya malam ini dan saya akan kembali kepada Anda. Sejujurnya, saya menggunakan arahan ini di beberapa tempat di halaman tanpa masalah. Saya akan mencoba mereproduksi masalahnya dan saya akan membalasnya.
Alexandros Spyropoulos
1
@AlexandrosSpyropoulos Saya baru saja menguji dan melihat bahwa kode saya berjalan dengan baik bahkan dengan 1.2.12. Saya pikir Anda mungkin melewatkan deklarasi <div dynamic = "html"> di HTML? (Dengan deklarasi itu, $ watch memperhatikan properti 'html' dalam cakupan, bukan HTML yang sebenarnya seperti yang Anda sebutkan, jadi seharusnya tidak ada kesalahan karakter yang tidak valid.) Jika tidak, kirimi saya plunkr yang menunjukkan itu tidak berfungsi, saya akan lihat apa yang salah.
Buu Nguyen
Mungkin Anda benar. Saya sudah mengharapkan saat itu, bahwa html sebenarnya adalah variabel yang berisi html: P. Adalah ide yang baik untuk menetapkan ruang lingkup pada Arahan Anda. berumur.io/...
Alexandros Spyropoulos
$compile(ele.contents())(scope);- baris ini memecahkan masalah saya tidak mengkompilasi komponen sudut yang ditambahkan secara dinamis. Terima kasih.
Mital Pritmani
5

Ditemukan dalam grup diskusi google. Bekerja untukku.

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

Kamu bisa memakai

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

arahan untuk mengikat html secara dinamis. Namun Anda harus mendapatkan data melalui layanan $ sce.

Silakan lihat demo langsung di 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>
Rupesh Kumar Tiwari
sumber
Terima kasih! Ini membantu saya. Namun, Anda harus memasukkan ngSanitize dan angular-sanitize.js:var myApp = angular.module('myApp', ['ngSanitize']);
jaggedsoft
yang bekerja untuk saya juga selama mengikat ikon bootstrap ke elemen md-list span
material
1

Coba kode di bawah ini untuk mengikat html melalui 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);
        });
      }
    };
  });

Coba element.html ini (scope.dynamic); dari element.html (attr.dynamic);

Ramesh M
sumber