web-dev-qa-db-ja.com

データベースからの動的HTML文字列のコンパイル

状況

Angularアプリ内に入れ子になっているのは、Pageという名前のディレクティブです。コントローラーには、ng-bind-html-unsafe属性のdivが含まれています。これは 'pageContent'と呼ばれる$ scope varに割り当てられます。この変数はデータベースから動的に生成されたHTMLを割り当てられます。ユーザーが次のページに移動すると、DBが呼び出され、pageContent変数がこの新しい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ディレクティブのテンプレート(上記のtemplateUrlプロパティの "page.html")

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

});

それはうまくいきます。 DBからのページのHTMLがブラウザでうまくレンダリングされているのがわかります。ユーザーが次のページに移動すると、次のページのコンテンツなどが表示されます。ここまでは順調ですね。

問題

ここでの問題は、ページのコンテンツの中にインタラクティブなコンテンツを入れたいということです。たとえば、HTMLには、ユーザーがクリックしたときにAngularがポップアップモーダルウィンドウを表示するなど、すごいことをするはずのサムネイル画像が含まれている場合があります。データベース内のHTML文字列にAngularメソッド呼び出し(ng-click)を配置しましたが、Angularはメソッド呼び出しやディレクティブを認識できません。 HTML文字列を解析し、それらを認識してコンパイルします。

私達のDBでは

ページ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>

Pageコントローラーに戻り、対応する$ scope関数を追加します。

ページコントローラ

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

DBからのHTML文字列内からその 'doSomethingAwesome'メソッドを呼び出す方法を私は理解できません。私はAngularがどういうわけかHTML文字列を解析しなければならないことを理解しています、しかしそれはどうですか?私は$ compileサービスに関する曖昧な議論を読み、いくつかの例をコピーして貼り付けましたが、何もうまくいきません。また、ほとんどの例では、動的コンテンツがディレクティブのリンク段階でのみ設定されることを示しています。私たちは、Pageがアプリの存続期間中存続し続けることを望みます。ユーザーがページをめくるたびに、常に新しいコンテンツを受信し、コンパイルして表示します。

抽象的な意味では、Angularアプリ内にAngularのチャンクを動的にネストしようとしているため、それらをやり取りできるようにする必要があると思います。

私はAngularドキュメンテーションのさまざまな部分、およびあらゆる種類のブログ記事を何度も読んだことがありますが、JSは人のコードでいっぱいです。 Angularを完全に誤解しているのか、それとも単純なものを見逃しているのか、それとも遅いのかはわかりません。いずれにせよ、私はいくつかのアドバイスを使用することができます。

129
giraffe_sense

ng-bind-html-unsafeはコンテンツをHTMLとしてレンダリングするだけです。 Angularスコープを結果のDOMにバインドしません。そのためには$compileサービスを使用する必要があります。 $compileを使用してユーザーが入力した動的HTMLをレンダリングし、コントローラーのスコープにバインドするディレクティブを作成する方法を示すために、 this plunker を作成しました。ソースは以下に掲載されています。

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>';
}
246
Buu Nguyen

角度1.2.10では、HTMLテキストであるattrs.dynamicの値を監視しようとしていたため、scope.$watch(attrs.dynamic, function(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);
        });
      }
    };
  });

Googleのディスカッショングループで見つかりました。私のために働きます。

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

あなたが使用することができます

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

Attrを介してhtmlをバインドするための以下のコードを試してください

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

1
Ramesh M