Biên dịch chuỗi HTML động từ cơ sở dữ liệu


132

Tình huống

Được lồng trong ứng dụng Angular của chúng tôi là một lệnh được gọi là Trang, được hỗ trợ bởi bộ điều khiển, chứa div với thuộc tính ng-bind-html-không an toàn. Điều này được gán cho một var phạm vi $ được gọi là 'pageContent'. Var này được gán HTML được tạo động từ cơ sở dữ liệu. Khi người dùng lật sang trang tiếp theo, một lệnh gọi tới DB được tạo và varContent page được đặt thành HTML mới này, được hiển thị trên màn hình thông qua ng-bind-html-không an toàn. Đây là mã:

Chỉ thị trang

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

Mẫu của chỉ thị trang ("page.html" từ thuộc tính templateUrl ở trên)

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

Trình điều khiển trang

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

});

Điều đó làm việc. Chúng tôi thấy HTML của trang từ DB được hiển thị độc đáo trong trình duyệt. Khi người dùng lật sang trang tiếp theo, chúng tôi sẽ thấy nội dung của trang tiếp theo, v.v. Càng xa càng tốt.

Vấn đề

Vấn đề ở đây là chúng tôi muốn có nội dung tương tác bên trong nội dung của trang. Chẳng hạn, HTML có thể chứa hình ảnh thu nhỏ trong đó, khi người dùng nhấp vào nó, Angular sẽ làm một cái gì đó tuyệt vời, chẳng hạn như hiển thị một cửa sổ phương thức bật lên. Tôi đã đặt các cuộc gọi phương thức Angular (ng-click) trong các chuỗi HTML trong cơ sở dữ liệu của chúng tôi, nhưng tất nhiên Angular sẽ không nhận ra các cuộc gọi hoặc chỉ thị phương thức trừ khi bằng cách nào đó phân tích chuỗi HTML, nhận ra chúng và biên dịch chúng.

Trong DB của chúng tôi

Nội dung cho Trang 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>

Nội dung cho Trang 2:

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

Quay lại trình điều khiển Trang, sau đó chúng ta thêm hàm $ scope tương ứng:

Trình điều khiển trang

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

Tôi không thể tìm ra cách gọi phương thức 'doS SomethingAwgie' từ trong chuỗi HTML từ DB. Tôi nhận ra Angular phải phân tích chuỗi HTML bằng cách nào đó, nhưng bằng cách nào? Tôi đã đọc những lời lầm bầm mơ hồ về dịch vụ $ compile, và sao chép và dán một số ví dụ, nhưng không có gì hoạt động. Ngoài ra, hầu hết các ví dụ cho thấy nội dung động chỉ được thiết lập trong giai đoạn liên kết của chỉ thị. Chúng tôi muốn Trang tồn tại trong suốt vòng đời của ứng dụng. Nó liên tục nhận, biên dịch và hiển thị nội dung mới khi người dùng lật qua các trang.

Theo một nghĩa trừu tượng, tôi đoán bạn có thể nói rằng chúng tôi đang cố gắng tự động lồng các khối Angular trong một ứng dụng Angular và cần có thể trao đổi chúng vào và ra.

Tôi đã đọc nhiều mẩu tài liệu Angular nhiều lần, cũng như tất cả các loại bài đăng trên blog và JS được xử lý bằng mã của mọi người. Tôi không biết liệu tôi hoàn toàn hiểu nhầm Angular hay chỉ thiếu một cái gì đó đơn giản, hoặc có thể tôi chậm. Trong mọi trường hợp, tôi có thể sử dụng một số lời khuyên.


2
$ compile và các blog tài liệu xung quanh nó làm tôi cảm thấy rằng tôi cũng chậm - mặc dù tôi cảm thấy js của mình khá mạnh - tôi nghĩ nếu tôi hiểu được điều này, tôi sẽ tạo một blog theo phong cách ngu ngốc - đó là chuyên môn của tôi!
hạ cánh

Câu trả lời:


248

ng-bind-html-unsafechỉ hiển thị nội dung dưới dạng HTML. Nó không liên kết phạm vi góc với DOM kết quả. Bạn phải sử dụng $compiledịch vụ cho mục đích đó. Tôi đã tạo plunker này để trình bày cách sử dụng $compileđể tạo một HTML động hiển thị chỉ thị được nhập bởi người dùng và ràng buộc với phạm vi của bộ điều khiển. Nguồn được đăng dưới đây.

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
Cảm ơn rất nhiều, Bửu! Tạo chỉ thị thuộc tính và thêm chức năng theo dõi phạm vi là hai điều tôi còn thiếu. Bây giờ điều này đang hoạt động, đoán tôi sẽ đọc lại các chỉ thị và $ biên dịch, để hiểu rõ hơn những gì đang diễn ra dưới mui xe.
gi hươu_s_sense

11
Tôi cũng vậy! Nhóm Angular thực sự có thể làm với việc cải thiện các tài liệu về điều này.
Craig Morgan

$compile(ele.contents())(scope);- dòng này đã giải quyết vấn đề của tôi về việc không biên dịch các thành phần góc được thêm động. Cảm ơn.
Mital Pritmani

Nếu
anam

1
Tôi thích ví dụ này nhưng nó không làm cho tôi làm việc. Tôi có một tuyên bố chuyển đổi xảy ra do sự lựa chọn của người dùng vì vậy nó năng động. Tùy thuộc vào điều đó tôi muốn chèn html chứa chỉ thị. Lệnh này hoạt động nếu tôi đặt nó trong giai đoạn bootstrap tự nhiên. Tuy nhiên, tôi có cái này đơn giản là không bắn --- case 'thông tin': $ scope.htmlString = $ sce.trustAsHtml ('<div Dynamic = "htmlString"> dddzzz </ div>'); phá vỡ; --- khi tôi muốn làm một cái gì đó như --- $ compile ($ sce.trustAsHtml ('<div Dynamic = "htmlString"> dddzzz </ div>')); Bất kỳ ý tưởng nào về cách giải quyết, v.v ...
đã hạ cánh vào

19

Trong góc 1.2.10, dòng scope.$watch(attrs.dynamic, function(html) {đã trả về lỗi ký tự không hợp lệ vì nó đang cố xem giá trị của attrs.dynamicnó là văn bản html.

Tôi đã sửa nó bằng cách tìm nạp thuộc tính từ thuộc tính scope

 scope: { dynamic: '=dynamic'}, 

Ví dụ của tôi

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

Xin chào, Nếu tôi sử dụng phần tử.html, nó sẽ trả về cho tôi TypeError: Không thể gọi phương thức 'insertB Before' của null. Vì vậy, sau một số thông tin về điều đó, tôi thấy rằng tôi phải sử dụng phần tử.append Nhưng nếu tôi sử dụng lệnh đó trên nhiều nơi - nó sẽ tạo ra HTML nhân. Vì vậy, 2 chỉ thị tạo ra 4 mã HTML giống nhau. Cảm ơn câu trả lời của bạn.
DzeryCZ

Tôi sẽ không sử dụng nối vào vị trí của bạn, tôi sẽ xem xét tối nay và tôi sẽ liên lạc lại với bạn. Thành thật mà nói, tôi đã sử dụng chỉ thị này ở một vài nơi trong một trang mà không có vấn đề gì. Tôi sẽ cố gắng tái tạo vấn đề và tôi sẽ liên lạc lại với bạn.
Alexandros Spyropoulos

1
@AlexandrosSpyropoulos Tôi chỉ kiểm tra và thấy rằng mã của tôi chạy ổn ngay cả với 1.2.12. Tôi nghĩ rằng bạn có thể đã bỏ lỡ khai báo <div động = "html"> trong HTML? (Với tuyên bố đó, $ watch xem thuộc tính 'html' trong phạm vi, không phải HTML thực tế như bạn đã đề cập, do đó sẽ không có lỗi char không hợp lệ.) Nếu không, hãy gửi cho tôi plunkr cho thấy nó không hoạt động, tôi Tôi sẽ thấy những gì sai.
Bửu Nguyễn

Bạn có thể đúng. Tôi đã mong đợi hồi đó, html thực sự là một biến chứa html: P. Mặc dù đó là một ý tưởng tốt để đặt phạm vi cho Chỉ thị của bạn. umur.io/ Kẻ
Alexandros Spyropoulos

$compile(ele.contents())(scope);- dòng này đã giải quyết vấn đề của tôi về việc không biên dịch các thành phần góc được thêm động. Cảm ơn.
Mital Pritmani

5

Tìm thấy trong một nhóm thảo luận google. Làm việc cho tôi.

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

3

Bạn có thể dùng

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

chỉ thị để ràng buộc html động. Tuy nhiên, bạn phải lấy dữ liệu qua dịch vụ $ sce.

Vui lòng xem bản demo trực tiếp tại 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>

Cảm ơn! Điều này đã giúp tôi. Tuy nhiên, bạn cần bao gồm ngSanitize và angular-sanitize.js:var myApp = angular.module('myApp', ['ngSanitize']);
jaggedsoft

điều đó cũng có hiệu quả với tôi trong khi liên kết biểu tượng bootstrap với thành phần nhịp md-list vật liệu
changtung

1

Hãy thử mã dưới đây để liên kết html thông qua 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);
        });
      }
    };
  });

Hãy thử phần tử này.html (scope.dynamic); hơn phần tử.html (attr.dynamic);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.