简体   繁体   中英

innerHTML - pass in value on (click) event

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>

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

Update: multiple <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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM