如何为 Angular Material table 中的每一行创建 click () 事件的可观察对象并响应所有这些事件?

How to create observables of click () events for every row in an Angular Material table and respond to all of them?

I was able to do it with an HTML button also with an Angular Material button But in an Angular Material table I only manage to do it in the first row First use fromEvent creating observables that emit event clicks when not getting the desired result then try it with Renderer2 It is not possible for me to find the way to be able to subscribe to each clik () event that is emitted in the table either using merge with @ViewChildren () I could not do it either inside a table of angular material DOM events in Anbular 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{
  buttonsClik!:() => 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','descargaPdfDoc'];
 dataSource = ELEMENT_DATA;
//another way I try with Renderer2 I try to catch not only the click () events of //the first row
 constructor(private renderer: Renderer2) { }
 ngOnDestroy(): void {
 this.buttonsClik();
 this.documentClick();
 }

 ngOnInit(): void {
   this.event$.subscribe();
 }

 ngAfterViewInit() {
   this.pdfExterno$.subscribe()
   this.render();
   console.log(this.pdf);
   console.log(this.docx);
   console.log(this.mostrar);
    /* I can not with

  
   <div>
        <button  id="pdf"   #pdf (click)="(pdf.id) ;descargarPDF(row)" >PDF</button>
      </div>
      <div>
        <button   id="docx" #docx (click)="(docx.id);descargarDocx(row)" >DOCX </button>
      </div>
    </div>
*/
   merge(
fromEvent(this.pdf.nativeElement, 'click'),
fromEvent(this.docx.nativeElement, 'click'),

).subscribe((event: Event | null)=> {
  console.log('desde Merge',event?.target);
});

  /* I can not with
   docx$!: Observable<any>;
   this.docx$=fromEvent(this.docx.nativeElement, 'click')
.pipe(

  map(()=>new Date().toString()),
  tap(console.log)
  )
  this.docx$.subscribe(e=>console.log('docx',e))
  */
 }
 render(){
  this.documentClick = this.renderer.
  listen('document', 'click', (event:MouseEvent) =>{

    console.log('Desde render docx',event)
   } )

 this.buttonsClik= this.renderer.
  listen(this.docx.nativeElement, 'click', (event:MouseEvent) =>{

    console.log('Desde render docx',event)
   } )


  this.renderer.listen(this.pdf.nativeElement, 'click', (event) => {
    console.log('Desde render pdf',event)
  })
  this.renderer.listen(this.mostrar.nativeElement, 'click', (event) => {
    console.log('Desde Render mostrar',event)
  })
}
  onDocumentClick(e: any): boolean | void {
    throw new Error('Method not implemented.');
  }
descargarPDF(row:any){
  console.log('pdf',row)
}

descargarDocx(row:any){
  console.log('docx',row)

  }

  onRowClicked(row:any): void{
    console.log('mat-row',row)
  }

  descargarPDFFueraDeMatTable(){
    console.log('funciona')
  }

}
// html
<h1>Fuera de la Tabla sin problemas</h1>
<section>
  <div>
    <button #testBtn>Click me</button>
  </div>
  <div>

</div>
</section>
<button mat-raised-button
 (click)="descargarPDFFueraDeMatTable()"
 #pdfExterno>PDF</button>
<h1>Dentro de la tabla solo puede vincular a un Observable para la 1 fila</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="descargaPdfDoc">
  <th mat-header-cell *matHeaderCellDef mat-sort-header>Descarga</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) ;descargarPDF(row)" >PDF</button>
      </div>
      <div>
        <button   id="docx" #docx (click)="(docx.id);descargarDocx(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>

这是不必要的,但如果你有,例如td

中的一个按钮
<td mat-cell *matCellDef="let element">
     <button #bt>{{element.weight}}</button>
</td>

您可以在给数据源赋值后使用

@ViewChildren('bt') bts:QueryList<ElementRef>

//After give value to dataSource
this.dataSource = new MatTableDataSource(ELEMENT_DATA);

//enclosed in a setTimeout
setTimeout(()=>{
  this.bts.forEach(x=>{
    fromEvent(x.nativeElement,'click').subscribe(res=>{
      console.log(res)
    })
  })
})

请注意,您需要在 setTimeout 中“封闭”,以便 Angular 有时间绘制 table

我不完全确定你的意图,但看起来你正在做的事情比你的问题标题所暗示的要复杂得多。不需要使用 @ViewChildRenderer2 来:

"create observables of click events for every row in an Angular Material table and respond to all of them"

要在点击时创建事件流,您可以简单地通过 Subject:

推送事件
private rowClick$ = new Subject<Event>();

public onRowClick(event: Event) {
  this.rowClick$.next(event);
}

如果您愿意,可以推送 PeriodicElement 数据而不是推送 Event

private rowClick$ = new Subject<PeriodicElement>();

public onRowClick(data: PeriodicElement) {
  this.rowClick$.next(data);
}

您似乎在尝试创建“渲染作业”流。如果是这样的话,你可以这样做:

  private renderJob$ = new Subject<RenderJob>();

  descargarPDF(row: PeriodicElement) {
    this.renderJob$.next({ type: 'pdf', data: row });
  }

  descargarDocx(row: PeriodicElement) {
    this.renderJob$.next({ type: 'docx', data: row });
  }

  ngOnInit() {
    this.renderJob$.subscribe(
      ({ type, data }) => console.log(`[render job received] type: ${type}`, data)
    );
  }

您可以使用以下几个 Stackblitz 链接: