Reputation: 13206
How do I pass in a parameter to a click event that has been set in innerHTML?
Component
html = "Lorem ipsum dolor sit amet, <mark (click)='hello('my name is what')'>consectetur adipiscing elit</mark>"
constructor(private changeDetectorRef: ChangeDetectorRef) {}
ngAfterContentChecked() {
this.changeDetectorRef.detectChanges();
if (this.showcaseContentText.nativeElement.querySelector('mark')) {
this.showcaseContentText.nativeElement
.querySelector('mark')
.addEventListener('click', this.hello.bind(this));
}
}
hello(test: string) {
console.log(test);
}
Template
<div class="text-md-left text-muted mb-lg-6" [innerHTML]="html" style="font-size: 15px"></div>
Upvotes: 1
Views: 533
Reputation: 31125
I'm able to capture the click
events from the mark
tag using RxJS fromEvent
instead of addEventListener
and AfterViewInit
instead of AfterContentChecked
hook. I've also sanitized the HTML using Angular DomSanitizer
.
Try the following
app.component.ts
import { fromEvent, Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
...
export class AppComponent implements AfterViewInit, OnDestroy {
@ViewChild("showcaseContentText") showcaseContentText: ElementRef<any>;
html =
"Lorem ipsum dolor sit amet, <mark (click)='hello('my name is what')'>consectetur adipiscing elit</mark>";
closed$ = new Subject<any>();
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.cdr.detectChanges();
fromEvent(
this.showcaseContentText.nativeElement.querySelector("mark"),
"click"
)
.pipe(takeUntil(this.closed$))
.subscribe(e => console.log(e));
}
ngOnDestroy() {
this.closed$.next();
}
}
app.component.html
<div #showcaseContentText class="text-md-left text-muted mb-lg-6" [innerHTML]="html | safe" style="font-size: 15px">
</div>
safe.pipe.ts
import { Pipe, PipeTransform } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
@Pipe({
name: "safe",
pure: true
})
export class SafePipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
public transform(value: any): SafeHtml {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
Working example: Stackblitz
<mark>
tags in single innerHTML
In that case you could use querySelectorAll()
instead of the querySelector()
function. Also since there would be multiple elements, you could use Array#map
along with fromEvent
and use RxJS map
operator to get the respective id
s.
Note that we're create multiple subscription streams. So more the number of mark
tags, more the number of streams. You need to close it when the component is closed (in the eg. takeUntil
is used). There are better ways to handle multiple subscriptions (eg. using combineLatest
), but they have their own pros and cons. I'll leave it to you to sort them out.
Controller
export class AppComponent implements AfterViewInit, OnDestroy {
@ViewChild("showcaseContentText") showcaseContentText: ElementRef<any>;
html =
"Lorem ipsum dolor sit amet, <mark id='mark1' (click)='hello('my name is what')'>consectetur adipiscing elit</mark>. Lorem ipsum <mark id='mark2' (click)='hello('my name is two')'>dolor sit amet</mark>, consectetur adipiscing elit";
closed$ = new Subject<any>();
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.cdr.detectChanges();
const obs$ = Array.from(
this.showcaseContentText.nativeElement.querySelectorAll("mark")
).map((el: HTMLElement) => fromEvent(el, "click").pipe(map(_ => el["id"])));
obs$.forEach(obs =>
obs.pipe(takeUntil(this.closed$)).subscribe({
next: id => {
console.log(id);
}
})
);
}
ngOnDestroy() {
this.closed$.next();
}
}
Working example: Stackblitz
Upvotes: 3