🐟
[Angular] カスタムパイプで生成したinnerHTMLのbutton要素にクリックイベントを付与する
はじめに
カスタムパイプで動的に生成したHTMLのbutton要素にクリックイベントを付与するのに、結構詰まったので備忘録として残しておきます。
HTMLタグを動的にコンポーネントに埋め込む
カスタムパイプを用いてHTML(文字列)をinnerHTML属性にバインドすることでHTMLを動的に生成することができます。
src\app\shared\pipe\buttonHtml.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
name: 'buttonHtml'
})
export class ButtonHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(value: string): any {
const html = `<button>${value}</button>`;
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
src\app\inner-htmlcustom-pipe\inner-htmlcustom-pipe.component.ts
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-inner-htmlcustom-pipe',
templateUrl: './inner-htmlcustom-pipe.component.html',
styleUrls: ['./inner-htmlcustom-pipe.component.scss']
})
export class InnerHTMLCustomPipeComponent implements OnInit, AfterViewInit {
@ViewChild('customButton') customButton?: ElementRef;
customButtonElement: string = 'Click me!';
constructor() { }
ngOnInit(): void {
}
}
src\app\inner-htmlcustom-pipe\inner-htmlcustom-pipe.component.html
<div [innerHTML]="customButtonElement | buttonHtml"></div>
動的に生成したbuttonタグにイベントを付与する
次は、カスタムパイプとinnerHTML属性で動的に生成したbutton属性にイベントを付与させます。
ただ、カスタムパイプ内で(click)イベントをバインドすることができません。
// 使えない
const html = `<button (click)="onClick()">${value}</button>`;
となると、@viewChild()
で直接カスタムパイプのinnerHTMLを参照して、click
イベントを登録したいところですが、@viewChild()
で直接innerHTMLの要素を参照することはできません。
src\app\shared\pipe\buttonHtml.pipe.ts
// テンプレート変数もしくはidを設定してみる
const html = `<button #customButton>${value}</button>`;
src\app\inner-htmlcustom-pipe\inner-htmlcustom-pipe.component.ts
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-inner-htmlcustom-pipe',
templateUrl: './inner-htmlcustom-pipe.component.html',
styleUrls: ['./inner-htmlcustom-pipe.component.scss']
})
export class InnerHTMLCustomPipeComponent implements OnInit, AfterViewInit {
@ViewChild('customButton') customButton?: ElementRef;
customButtonElement: string = 'Click me!';
constructor() { }
ngOnInit(): void {
}
ngAfterViewInit() {
console.log(this.customButton)
// undefined
}
}
- 参照できず
undefined
になる
[解決法] innerHTMLを展開する要素から参照する
解決法としては、innerHTMLを展開する要素から参照します。
カスタムパイプ内では、どのbutton要素か特定するためにidを付与しておきます。
src\app\shared\pipe\buttonHtml.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
name: 'buttonHtml'
})
export class ButtonHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(value: string): any {
const html = `<button id="customButton">${value}</button>`;
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
src\app\inner-htmlcustom-pipe\inner-htmlcustom-pipe.component.html
<div #customButton [innerHTML]="customButtonElement | buttonHtml"></div>
src\app\inner-htmlcustom-pipe\inner-htmlcustom-pipe.component.ts
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-inner-htmlcustom-pipe',
templateUrl: './inner-htmlcustom-pipe.component.html',
styleUrls: ['./inner-htmlcustom-pipe.component.scss']
})
export class InnerHTMLCustomPipeComponent implements OnInit, AfterViewInit {
@ViewChild('customButton') customButton?: ElementRef;
customButtonElement: string = 'Click me!';
constructor() { }
ngOnInit(): void {
}
ngAfterViewInit() {
const customButtonElement = this.customButton?.nativeElement.querySelector('#customButton');
if(customButtonElement) {
customButtonElement.addEventListener('click', () => {
console.log('クリックしたよ')
})
}
}
}
-
@viewChild()
でテンプレート変数customButtonを取得 -
querySelector()
で任意のid
の要素を取得 - 取得したbutton要素に
addEventListener
でclick
イベントを登録
こんな特殊な書き方をするケースはあるのでしょうか。。他に良い方法があれば随時更新します。
Discussion