Veritabanından dinamik HTML dizelerini derleme


132

Durum

Angular uygulamamızın içinde, ng-bind-html-unsafe özniteliğine sahip bir div içeren, bir denetleyici tarafından desteklenen Sayfa adlı bir yönergedir. Bu, 'pageContent' adlı bir $ kapsam değişkenine atanır. Bu değişken, bir veritabanından dinamik olarak oluşturulmuş HTML'ye atanır. Kullanıcı bir sonraki sayfaya geçtiğinde, DB'ye bir çağrı yapılır ve pageContent değişkeni, ng-bind-html-unsafe aracılığıyla ekranda işlenen bu yeni HTML'ye ayarlanır. İşte kod:

Sayfa yönergesi

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

Sayfa yönergesinin şablonu (yukarıdaki templateUrl özelliğinden "page.html")

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

Sayfa denetleyicisi

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

});

Bu işe yarıyor. Sayfanın DB'den gelen HTML'sinin tarayıcıda güzelce işlendiğini görüyoruz. Kullanıcı bir sonraki sayfaya geçtiğinde, sonraki sayfanın içeriğini görüyoruz ve bu böyle devam ediyor. Çok uzak çok iyi.

Sorun

Buradaki sorun, bir sayfanın içeriğinin içinde etkileşimli içeriğe sahip olmak istememizdir. Örneğin, HTML, kullanıcı üzerine tıkladığında, Angular'ın bir pop-up modal pencere görüntülemek gibi harika bir şey yapması gereken bir küçük resim içerebilir. Veritabanımızdaki HTML dizelerine Angular yöntem çağrıları (ng-tıklama) yerleştirdim, ancak tabii ki Angular, HTML dizesini bir şekilde ayrıştırmadığı, tanımadığı ve derlemediği sürece yöntem çağrılarını veya yönergeleri tanımayacaktır.

DB'mizde

Sayfa 1 için İçerik:

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

Sayfa 2 için İçerik:

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

Sayfa denetleyicisine geri döndükten sonra ilgili $ kapsam işlevini ekliyoruz:

Sayfa denetleyicisi

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

Bu 'doSomethingAwesome' yöntemini DB'deki HTML dizesinin içinden nasıl çağıracağımı çözemiyorum. Angular'ın HTML dizesini bir şekilde ayrıştırması gerektiğinin farkındayım, ama nasıl? $ Compile hizmeti hakkında muğlak mırıltılar okudum ve bazı örnekleri kopyalayıp yapıştırdım ama hiçbir şey işe yaramıyor. Ayrıca, çoğu örnek dinamik içeriğin yalnızca direktifin bağlanma aşamasında ayarlandığını gösterir. Page'in uygulamanın ömrü boyunca hayatta kalmasını isteriz. Kullanıcı sayfalar arasında gezinirken sürekli olarak yeni içerik alır, derler ve görüntüler.

Soyut anlamda, Angular parçalarını bir Angular uygulamasına dinamik olarak yerleştirmeye çalıştığımızı ve bunları içeri ve dışarı değiştirebilmemiz gerektiğini söyleyebiliriz.

Çeşitli Angular dokümantasyon parçalarının yanı sıra her türden blog gönderisini ve insanların koduyla JS Fiddled okudum. Angular'ı tamamen yanlış mı anladığımı yoksa basit bir şeyi mi kaçırdığımı bilmiyorum ya da belki yavaşım. Her durumda, bazı tavsiyelerde bulunabilirim.


2
$ compile ve onu çevreleyen docs blogları bana aynı zamanda yavaş olduğumu hissettiriyor - js'imin oldukça güçlü olduğunu hissetmeme rağmen - sanırım bununla başa çıkarsam aptal tarzı bir blog yapacağım - bu benim uzmanlık alanım!
indi

Yanıtlar:


248

ng-bind-html-unsafeiçeriği yalnızca HTML olarak işler. Angular kapsamı sonuçlanan DOM'a bağlamaz. Bunun için $compileservisi kullanmalısınız . Oluşturduğum bu plunker nasıl kullanılacağını göstermek için $compilebir yönerge render dinamik HTML kullanıcılar tarafından girilen ve kontrolörün kapsamına bağlanarak oluşturun. Kaynak aşağıda yayınlanmıştır.

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

6
Çok teşekkürler Buu! Öznitelik yönergesini oluşturmak ve kapsam izleme işlevini eklemek, eksik olduğum iki şeydi. Şimdi bu işe yarıyor, sanırım başlık altında neler olup bittiğini daha iyi anlamak için yönergeler ve $ compile hakkında tekrar okuyacağım.
giraffe_sense

11
Ben de! Angular ekibi bu konudaki dokümanları gerçekten iyileştirebilirdi.
Craig Morgan

$compile(ele.contents())(scope);- bu satır, dinamik olarak eklenen açısal bileşenleri derlememe sorunumu çözdü. Teşekkürler.
Mital Pritmani

TeplateURL içinde @BuuNguyen varsayalım ng-bind-html kullanarak bir dinamik htmnl sayfası eklerseniz, derleme çalışmazsa, diğer taraftan trustAsHTml kullanarak güvenli olmayan bazı içeriklerde hata verir, yalnızca güvenli olmayan hata derlenmez, herhangi bir öneri?
anam

1
Bu örneği beğendim ama benimki işe yaramıyor. Kullanıcı seçimi nedeniyle dinamik olan bir switch deyimim var. Buna bağlı olarak html içeren yönerge eklemek istiyorum. Yönerge, onu doğal önyükleme aşamasına yerleştirirsem işe yarar. Ancak, ateşlemeyen bir şeye sahibim --- case 'info': $ kapsam.htmlString = $ sce.trustAsHtml ('<div dynamic = "htmlString"> dddzzz </div>'); break; --- gibi bir şey yapmak istediğimde --- $ compile ($ sce.trustAsHtml ('<div dynamic = "htmlString"> dddzzz </div>')); Geçici çözümler vb. Hakkında herhangi bir fikir
indi

19

Açısal 1.2.10'da satır scope.$watch(attrs.dynamic, function(html) {, değeri attrs.dynamichtml metni olan izlemeye çalıştığı için geçersiz bir karakter hatası döndürüyordu .

Kapsam özelliğinden özniteliği getirerek bunu düzelttim

 scope: { dynamic: '=dynamic'}, 

Benim örneğim

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

Merhaba, element.html kullanırsam bana döndürür TypeError: null 'insertBefore' yöntemi çağrılamıyor. Bu yüzden, biraz googling yaptıktan sonra element.append'i kullanmam gerektiğini anladım.Ama bu yönergeyi birden çok yerde kullanırsam, çoklu HTML üretiyor. Yani 2 yönerge 4 aynı HTML kodunu üretir. Cevabınız için teşekkürler.
DzeryCZ

Senin yerinde append kullanmazdım, bu gece ona bir göz atacağım ve sana döneceğim. Dürüst olmak gerekirse, bu yönergeyi bir sayfada pek çok yerde sorunsuz olarak kullandım. Sorunu yeniden oluşturmaya çalışacağım ve size geri döneceğim.
Alexandros Spyropoulos

1
@AlexandrosSpyropoulos Sadece test ettim ve kodumun 1.2.12 ile bile sorunsuz çalıştığını görüyorum. HTML'deki <div dynamic = "html"> bildirimini muhtemelen kaçırdığınızı düşünüyorum. (Bu bildirimle $ watch, bahsettiğiniz gerçek HTML'yi değil, kapsamdaki 'html' özelliğini izler, bu nedenle geçersiz bir char hatası olmamalıdır.) Değilse, çalışmadığını gösteren plunkr'ı bana gönderin, ben Neyin yanlış olduğunu göreceğiz.
Buu Nguyen

Muhtemelen haklısın. O zamanlar bekliyordum, html aslında html: P içeren bir değişkendir. Yönergelerinize bir kapsam belirlemek iyi bir fikirdir. umur.io/…
Alexandros

$compile(ele.contents())(scope);- bu satır, dinamik olarak eklenen açısal bileşenleri derlememe sorunumu çözdü. Teşekkürler.
Mital Pritmani

5

Bir google tartışma grubunda bulundu. Benim için çalışıyor.

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

3

Kullanabilirsiniz

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

html'yi dinamik olarak bağlama yönergesi. Ancak verileri $ sce hizmeti aracılığıyla almanız gerekir.

Lütfen http://plnkr.co/edit/k4s3Bx adresindeki canlı demoya bakın

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>

Teşekkürler! Bu bana yardımcı oldu. Bununla birlikte, ngSanitize ve angular-sanitize.js'yi eklemeniz gerekir:var myApp = angular.module('myApp', ['ngSanitize']);
jaggedsoft

Önyükleme simgesini malzeme md-list span elemanına
bağlarken

1

Html'yi attr aracılığıyla bağlamak için aşağıdaki kodu deneyin

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

Bu element.html (kapsam.dynamic) öğesini deneyin; element.html'den (attr.dynamic);

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.