Компиляция динамических строк HTML из базы данных

Ситуация

Вложенный в наш Angular приложение - это директива под названием "Страница", поддерживаемая контроллером, который содержит div с атрибутом ng-bind-html-unsafe. Это присваивается переменной $scope var, называемой "pageContent". Этот var получает динамически генерируемый HTML из базы данных. Когда пользователь переходит на следующую страницу, вызывается вызываемый в БД, а параметр pageContent var устанавливается на этот новый 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, не распознает их и компилирует их.

В нашей DB

Содержание для страницы 1:

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

Вернемся в контроллер страницы, затем добавим соответствующую функцию $scope:

Контроллер страницы

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

Я не могу понять, как вызвать этот метод doSomethingAwesome из строки HTML из БД. Я понимаю, что Angular должен каким-то образом анализировать HTML-строку, но как? Я читал смутные расплывчатые сообщения об услуге компиляции $, копировал и вставлял несколько примеров, но ничего не работает. Кроме того, большинство примеров показывают, что динамический контент устанавливается только во время фазы связывания директивы. Мы хотели бы, чтобы Страница оставалась в живых на протяжении всей жизни приложения. Он постоянно получает, компилирует и отображает новый контент, когда пользователь просматривает страницы.

В абстрактном смысле, я думаю, вы могли бы сказать, что мы пытаемся динамически вставлять фрагменты Angular в приложение Angular и должны иметь возможность их вносить и удалять.

Я несколько раз читал различные фрагменты документации Angular, а также всевозможные сообщения в блогах и JS Fiddled with people code. Я не знаю, полностью ли я недопонимаю Angular, или просто отсутствует что-то простое, или, может быть, я медленный. В любом случае, я мог бы использовать некоторые советы.

Ответ 1

ng-bind-html-unsafe только отображает содержимое как HTML. Он не привязывает область Angular к приведенному DOM. Для этого вам нужно использовать службу $compile. Я создал этот плункер, чтобы продемонстрировать, как использовать $compile для создания директивы рендеринга динамического HTML, введенного пользователями, и привязки к области контроллера. Источник размещен ниже.

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

Ответ 2

В angular 1.2.10 строка scope.$watch(attrs.dynamic, function(html) { возвращала недопустимую символьную ошибку, потому что она пыталась посмотреть значение attrs.dynamic, которое было html-текстом.

Я исправил это, извлекая атрибут из свойства scope

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

Ответ 3

Найдено в группе обсуждения google. Работает для меня.

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

Ответ 4

Вы можете использовать

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>

Ответ 5

Попробуйте этот ниже код для привязки 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(scope.dynamic); чем element.html(attr.dynamic);