AngularテンプレートでinnerHTMLとDomSanitizerを使う方法

読了 2分

tinymceのようなプラグインで作成したコンテンツはHTMLコードとしてデータベースに保存されます。このHTMLをAngularテンプレートで安全にレンダリングする方法を整理します。

データベースに保存されたHTMLコードが次のようなものだと仮定します。

<h1><span style="color: #f1c40f;">こんにちは。イ・サンヒです。</span></h1>

最もよくある間違いは、次のようにHTMLコードをタグの中にそのまま挿入する方法です。

<div class="post-content">{{ post.content }}</div>

このように記述すると、下の画像のようにHTMLコードがそのままテキストとして出力されます。

HTMLコードがそのまま出力される問題
HTMLコードがそのまま出力される問題

この問題は次のようにinnerHTMLプロパティバインディング(property binding)を使うことで解決できます。

<div class="post-content" [innerHTML]="post.content"></div>
インラインスタイルが消える問題が発生
インラインスタイルが消える問題が発生

HTMLコードは正しくレンダリングされますが、別の問題が発生します。開発者ツールで実際に適用されたコードを確認すると、次のようにspanタグに含まれていたインラインCSSスタイルが取り除かれています。

開発者ツールでコードを確認
開発者ツールでコードを確認

この挙動は、Angularが危険なHTMLコードのDOMへの挿入を防ぐために提供している組み込みの保護機能によるものです。コンテンツが信頼できることを明示する必要があり、Angularが提供するDomSanitizerクラスで簡単に処理できます。

まずテンプレートで使用するパイプクラスを生成します。

$ ng g pipe trust-html

trust-html.pipe.tsが生成されたら、次のコードのように修正します。

trust-html.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({
  name: 'trustHtml'
})
export class TrustHtmlPipe implements PipeTransform {

  constructor(private sanitizer: DomSanitizer) {
  }

  transform(content): unknown {
    return this.sanitizer.bypassSecurityTrustHtml(content);
  }
}

app.module.tsdeclarationsに登録するのも忘れずに行います。

app.module.ts
import { TrustHtmlPipe } from './pipes/trust-html.pipe';

@NgModule({
  declarations: [
    AppComponent,
    ... 省略 ...
    TrustHtmlPipe,
  ],

生成したパイプをテンプレートで使うと、次のようになります。

<div class="post-content" [innerHTML]="post.content | trustHtml"></div>
正常に出力される場合
正常に出力される場合

以上で、innerHTMLプロパティバインディングとパイプ(pipe)クラスを活用して、データベースに保存されたHTMLをAngularテンプレートで安全にレンダリングする方法を整理しました。

X