この記事の例では、AngularJS で HTML を動的にバインドする方法について説明します。参考までに皆さんと共有してください。詳細は次のとおりです:
Web フロントエンド開発では、特にコンテンツにおいて、バックエンドからの HTML 文字列の動的バインドやページの DOM 表示への動的スプライシングの必要性によく遭遇します。管理システム(CMS:Content Management Systemの略)、そのようなニーズはどこにでもあります。
angular の読者はまず ngBindHtml を思い浮かべるでしょう。そうです、angular は HTML を動的にバインドするためのこの命令を提供します。これは、innerHTML を使用して計算された式の結果を DOM にバインドします。しかし、問題はそれほど単純ではありません。 Web セキュリティにおいて、XSS (クロスサイト スクリプティング、スクリプト インジェクション攻撃) は、Web アプリケーションにおける典型的なコンピュータ セキュリティ脆弱性です。 XSS 攻撃とは、実行可能なクライアント側コードを Web ページに挿入し、ブラウザーによって実行されて攻撃の目的を達成することを指し、攻撃が成功すると、ユーザーの機密情報が取得される可能性があります。ユーザー エクスペリエンスを変更し、ユーザーやその他の違法な行為を誘発し、場合によっては XSS 攻撃を、サーバーやデータベースへの SQL インジェクション攻撃、クリック ハイジャック、相対リンク ハイジャックなどの他の攻撃方法と組み合わせてフィッシングを実行することで、被害がもたらされます。巨大で、Web セキュリティの最大の敵でもあります。 Web セキュリティの問題の詳細については、ウィキ https://en.wikipedia.org/wiki/Cross-site_scripting%E3%80%82 を参照してください。 Angular では、デフォルトでは追加された HTML コンテンツを信頼しません。 HTML コンテンツを追加するには、まず $sce.trustAsHtml を使用して、これが信頼できる HTML コンテンツであることを Angular に伝える必要があります。そうしないと、$sce:unsafe 例外エラーが発生します。
エラー: [$sce:unsafe] 安全なコンテキストで安全でない値を使用しようとしています。
これは、単純な Angular リンクをバインドするデモです:
HTML:
<div ng-controller="DemoCtrl as demo"> <div ng-bind-html="demo.html"></div> </div>
JavaScript:
angular.module("com.ngbook.demo", []) .controller("DemoCtrl", ["$sce", function($sce) { var vm = this; var html = '<p>hello <a href="https://angular.io/">angular</a></p>'; vm.html = $sce.trustAsHtml(html); return vm; }]);
単純な静的 HTML の場合、この問題は解決されます。ただし、複雑な HTML の場合、ここでの複雑さは、Angular の式と命令を含む HTML テンプレートを指します。これらの場合、大きな DOM 表示をバインドするだけでなく、Angular の強力な双方向バインディング メカニズムを取得することも期待されます。 ngBindHhtml は、双方向バインディングの $scope に関連付けられません。HTML に ngClick、ngHref、ngSHow、ngHide などの angular 命令がある場合、これらのボタンをクリックしても、バインディングの式は何も反応しません。式は更新されません。たとえば、最後のリンクを ng-href="demo.link" に変更しようとすると、リンクは解析されず、元の HTML 文字列が DOM に表示されたままになります。
Angular のすべての命令を有効にするには、コンパイルにプレリンクとポストリンクが含まれており、動作する前に特定の動作に接続される必要があります。ほとんどの場合、コンパイルは angular の起動時に自動的にコンパイルされます。ただし、動的に追加されたテンプレートの場合は、手動でコンパイルする必要があります。 Angular は、この関数を実装するための $compile サービスを提供します。以下は、より一般的なコンパイルの例です。
HTML:
<body ng-controller="DemoCtrl as demo"> <dy-compile html="{{demo.html}}"> </dy-compile> <button ng-click="demo.change();">change</button> </body>
JavaScript:
angular.module("com.ngbook.demo", []) .directive("dyCompile", ["$compile", function($compile) { return { replace: true, restrict: 'EA', link: function(scope, elm, iAttrs) { var DUMMY_SCOPE = { $destroy: angular.noop }, root = elm, childScope, destroyChildScope = function() { (childScope || DUMMY_SCOPE).$destroy(); }; iAttrs.$observe("html", function(html) { if (html) { destroyChildScope(); childScope = scope.$new(false); var content = $compile(html)(childScope); root.replaceWith(content); root = content; } scope.$on("$destroy", destroyChildScope); }); } }; }]) .controller("DemoCtrl", [function() { var vm = this; vm.html = '<h2>hello : <a ng-href="{{demo.link}}">angular</a></h2>'; vm.link = 'https://angular.io/'; var i = 0; vm.change = function() { vm.html = '<h3>change after : <a ng-href="{{demo.link}}">' + (++i) + '</a></h3>'; }; }]);
dy-compile というディレクティブがここで作成され、最初にバインドされた属性の HTML 値をリッスンします。変更すると、HTML コンテンツが存在する場合、最初にサブスコープの作成が試行され、次に $compile サービスを使用して受信 HTML に動的に接続し、現在の DOM ノードを置き換えます。ここでサブスコープを作成する理由は、それぞれの処理を容易にするためです。 DOM は初めて破棄され、スコープは簡単に破棄でき、HTML コンパイルによってもたらされたウォッチャー機能は削除され、最後の親スコープが破棄されるとスコープも破棄しようとします。
上記のコンパイルと接続により、ngHref 命令が有効になります。ここでは、動的コンパイル Angular モジュールの例を示します。具体的な実装方法については、具体的なディレクティブを宣言する方法を参照してください。