Компіляція динамічних рядків HTML із бази даних


132

Ситуація

Вкладений у наш додаток Angular - це директива під назвою Page, підкріплена контролером, яка містить div з атрибутом ng-bind-html-unsafe. Це присвоєно варі $ діапазону під назвою 'pageContent'. Цей вар отримує динамічно генерований HTML з бази даних. Коли користувач переходить на наступну сторінку, робиться виклик до БД, і varCortent page встановлюється на цей новий HTML-код, який відображається на екрані через ng-bind-html-unsafe. Ось код:

Сторінка директива

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

Шаблон директиви сторінки ("page.html" із властивості templateUrl, наведеної вище)

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

Контролер сторінки

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

});

Це працює. Ми бачимо HTML сторінки з БД, добре відображеної в браузері. Коли користувач переходить на наступну сторінку, ми бачимо вміст наступної сторінки тощо. Все йде нормально.

Проблема

Проблема тут полягає в тому, що ми хочемо мати інтерактивний вміст всередині вмісту сторінки. Наприклад, HTML може містити ескіз зображення, коли, коли користувач натискає на нього, Angular повинен зробити щось приголомшливе, наприклад відображення спливаючого модального вікна. Я розміщував виклики методу Angular (ng-click) у рядках HTML в нашій базі даних, але, звичайно, Angular не збирається розпізнавати виклики методів або директиви, якщо він якось не розбирає HTML-рядок, розпізнає їх і компілює їх.

У нашій БД

Вміст для сторінки 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>

Вміст для сторінки 2:

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

Повертаючись до контролера сторінки, ми додаємо відповідну функцію $ range:

Контролер сторінки

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

Я не можу зрозуміти, як викликати цей метод "doSomethingAwesome" з рядка HTML в БД. Я розумію, що Angular повинен якось проаналізувати рядок HTML, але як? Я читав смутні помилки про службу $ compile, копіював і вставляв деякі приклади, але нічого не працює. Крім того, більшість прикладів показує, що динамічний контент встановлюється лише на етапі посилання директиви. Ми хочемо, щоб Пейдж залишався живим протягом усього життя програми. Він постійно отримує, збирає та показує новий вміст, коли користувач гортає сторінки.

В абстрактному розумінні, я думаю, ви могли б сказати, що ми намагаємося динамічно вкладати шматки кутових у програму Angular, і нам потрібно мати змогу поміняти їх і вийти.

Я читав різні біти кутової документації кілька разів, а також всілякі дописи в блогах і JS Fiddled з кодом людей. Я не знаю, чи я зовсім не розумію Кутового, чи просто пропускаю щось просте, чи, можливо, я повільний. У будь-якому випадку, я міг би скористатися порадою.


2
$ compile та докторські блоги, що його оточують, змушують мене відчувати, що я також повільний - хоча я відчуваю, що мій js досить сильний - я думаю, якщо мені вдасться впоратися з цим, я зроблю блог у стилі ідіотів - ось моя спеціальність!
приземлився

Відповіді:


248

ng-bind-html-unsafeнадає вміст лише у вигляді HTML. Він не прив'язує кутовий діапазон до результату DOM. Для цього вам потрібно скористатися $compileпослугою. Я створив цей плункер, щоб продемонструвати, як використовувати $compileдля створення директиви, що відображає динамічний HTML, введений користувачами та прив’язуючи до області контролера. Джерело розміщено нижче.

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
Велике спасибі, Буу! Створення директиви атрибутів та додавання функції перегляду області були двома речами, які мені не вистачало. Тепер, коли це працює, напевно, я ще раз прочитаю директиви та $ compile, щоб краще зрозуміти, що відбувається під капотом.
giraffe_sense

11
Команда Angular справді могла б покращити документи щодо цього.
Крейг Морган

$compile(ele.contents())(scope);- цей рядок вирішив моє питання про некомпіляцію кутових компонентів, які додаються динамічно. Дякую.
Mital Pritmani

@BuuNguyen всередині teplateURL припустимо, якщо ви включаєте деяку динамічну сторінку htmnl за допомогою ng-bind-html, то при використанні компіляція не працює помилка з деякого небезпечного вмісту, інша сторона за допомогою trustAsHTml видаляє лише небезпечну помилку, не компілює помилки, будь-які пропозиції?
анам

1
Мені подобається цей приклад, але він не працює. У мене є перемикач, який відбувається через вибір користувача, тому його динамічність. Залежно від цього я хочу вставити html-директиву. Директива працює, якщо я розміщую її у фазі природного завантаження. Однак у мене це є, що просто не запускається --- інформація 'case': $ range.htmlString = $ sce.trustAsHtml ('<div динамика = "htmlString"> dddzzz </div>'); перерва; --- коли я хочу зробити щось на кшталт --- $ compile ($ sce.trustAsHtml ('<div dynamic = "htmlString"> dddzzz </div>')); Будь-які ідеї щодо обхідних шляхів тощо ...
приземлився

19

У куті 1.2.10 рядок scope.$watch(attrs.dynamic, function(html) {повертав недійсну помилку символів, оскільки він намагався спостерігати за значенням attrs.dynamicтексту HTML.

Я це виправив, отримуючи атрибут із властивості області

 scope: { dynamic: '=dynamic'}, 

Мій приклад

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

Привіт! Якщо я використовую element.html, він поверне мені TypeError: Неможливо викликати метод "insertBefore" з null. Тож після деякого гуглювання з цього приводу я виявляю, що я повинен використовувати element.append Але якщо я використовую цю директиву в декількох місцях - вона генерує мультиплікаційний HTML. Отже, 2 директиви генерують 4 однакових HTML-коду. Дякую за вашу відповідь.
DzeryCZ

Я б не використовував додаток на вашому місці, я перегляну це сьогодні ввечері, і я повернуся до вас. Якщо чесно, я використав цю директиву в досить багатьох місцях на сторінці без жодних проблем. Я спробую відтворити проблему і повернусь до вас.
Олександрос Спиропулос

1
@AlexandrosSpyropoulos Я просто перевіряю і бачу, що мій код працює нормально навіть з 1.2.12. Думаю, ви, мабуть, пропустили декларацію <div динамики = "html"> у HTML? (З цією декларацією $ watch дивиться властивість 'html' за рамками, а не власне HTML, як ви згадали, тому не повинно бути помилкових помилок char.) Якщо ні, надішліть мені plunkr, який показує, що він не працює, я Я побачу, що не так.
Buu Nguyen

Ви, мабуть, праві. Я тоді очікував, що html - це фактично змінна, що містить html: P. Хоча добре встановити сферу застосування Ваших Директив. umur.io/…
Олександрос Спіропулос

$compile(ele.contents())(scope);- цей рядок вирішив моє питання про некомпіляцію кутових компонентів, які додаються динамічно. Дякую.
Mital Pritmani

5

Знайдено у групі обговорень google. Працює для мене.

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

3

Можна використовувати

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

директива динамічно прив’язувати html. Однак вам потрібно отримати дані через сервіс $ sce.

Перегляньте демонстрацію на веб-сайті 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>

Дякую! Це мені допомогло. Однак вам потрібно включити ngSanitize та angular-sanitize.js:var myApp = angular.module('myApp', ['ngSanitize']);
jaggedsoft

що працювало і для мене під час прив’язки піктограми завантажувального
пристрою

1

Спробуйте цей код нижче для прив’язки html через 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);
        });
      }
    };
  });

Спробуйте цей element.html (range.dynamic); ніж element.html (attr.dynamic);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.