My journey to achieve this functionality started with an HTML button and also with an Angular Material button. However, when it came to implementing it in an Angular Material table, I faced challenges. Initially, I could only make it work on the first row using fromEvent to create observables that emit click events. When that didn't yield the desired result, I turned to Renderer2 for a solution. Despite my efforts, I struggled to find a way to subscribe to each click event emitted within the table, whether using merge with @ViewChildren() or handling DOM events directly inside an Angular Material Table.
//ts
import { Renderer2,AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { defer, fromEvent, Observable } from 'rxjs';
import { debounceTime, map, tap, switchMap } from 'rxjs/operators';
// modal de una tabla
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
const ELEMENT_DATA: PeriodicElement[] = [
{position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
{position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
{position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
{position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
{position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
{position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
{position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
{position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
{position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
];
/**
* @title Basic use of `<table mat-table>`
*/
@Component({
selector: 'app-material',
templateUrl: './material.component.html',
styleUrls: ['./material.component.scss']
})
export class MaterialComponent implements AfterViewInit , OnInit, OnDestroy{
buttonsClick!: () => void;
documentClick!: () => void;
@ViewChild('testBtn', { static: true }) testBtn!: ElementRef<HTMLButtonElement>;
event$ = defer(() => fromEvent(this.testBtn.nativeElement, 'click')).pipe(
map(() => new Date().toString()),
tap(console.log)
)
@ViewChild('pdfExterno',{ read: ElementRef }) pdfExterno!: ElementRef<HTMLButtonElement>;
@ViewChild('docx',{ read: ElementRef }) docx!: ElementRef<HTMLButtonElement>;
pdfExterno$ = defer(() => fromEvent(this.pdfExterno.nativeElement, 'click')).pipe(
map(() => new Date().toString()),
tap(console.log)
)
@ViewChild('pdf',{ read: ElementRef }) pdf!: ElementRef<HTMLButtonElement>;
@ViewChild('mostrar',{ read: ElementRef }) mostrar!: ElementRef<HTMLButtonElement>;
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol','downloadPdfDoc'];
dataSource = ELEMENT_DATA;
// Another approach involved trying with Renderer2 to capture all click events, not just those of the first row
constructor(private renderer: Renderer2) { }
ngOnDestroy(): void {
this.buttonsClick();
this.documentClick();
}
ngOnInit(): void {
this.event$.subscribe();
}
ngAfterViewInit() {
this.pdfExterno$.subscribe()
this.render();
merge(
fromEvent(this.pdf.nativeElement, 'click'),
fromEvent(this.docx.nativeElement, 'click'),
).subscribe((event: Event | null)=> {
console.log('from Merge', event?.target);
});
}
render(){
this.documentClick = this.renderer.
listen('document', 'click', (event: MouseEvent) => {
console.log('From render docx', event)
} )
this.buttonsClick = this.renderer.
listen(this.docx.nativeElement, 'click', (event: MouseEvent) => {
console.log('From render docx', event)
} )
this.renderer.listen(this.pdf.nativeElement, 'click', (event) => {
console.log('From render pdf', event)
})
this.renderer.listen(this.mostrar.nativeElement, 'click', (event) => {
console.log('From Render show', event)
})
}
onDocumentClick(e: any): boolean | void {
throw new Error('Method not implemented.');
}
downloadPDF(row:any){
console.log('pdf',row)
}
downloadDocx(row:any){
console.log('docx',row)
}
onRowClicked(row:any): void{
console.log('mat-row',row)
}
downloadPDFFromOutsideMatTable(){
console.log('it works')
}
}
// html
<h1>Without any issues outside the table</h1>
<section>
<div>
<button #testBtn>Click me</button>
</div>
</section>
<button mat-raised-button
(click)="downloadPDFFromOutsideMatTable()"
#pdfExterno>PDF</button>
<h1>Within the table, I could only bind to an Observable for the 1st row</h1>
<div class="container text-center">
<table mat-table [dataSource]="dataSource" #mytable class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef> Weight </th>
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef> Symbol </th>
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
</ng-container>
<ng-container matColumnDef="downloadPdfDoc">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Download</th>
<td mat-cell *matCellDef="let row" class="u-text-align-center" (click)="$event.stopPropagation()">
<div class="row">
<div>
<button id="pdf" #pdf (click)="(pdf.id); downloadPDF(row)">PDF</button>
</div>
<div>
<button id="docx" #docx (click="(docx.id); downloadDocx(row)">DOCX </button>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" #mostrar (click)="onRowClicked(row);$event.stopPropagation()">
</tr>
</table>
</div>