Search code examples
angularangular-materialangular-directive

Angular How to pass data from Directive to Parent component


My requirement is something like, I have a reusable table which I wanna use on multiple places. I am passing table data and columns wherever I am using this table.

Now in some parent components I wanna get data when user click on table row or double click on table row.

For that I have create a directive using @HostListener.

When I am using directive on table row I can get clicked row data in directive but when I am emitting this data I am not getting it in parent.

table.component.html

<section class="example-section">

  <div class="cdx-table-wrapper cdx-table-wrapper-fixed">
    <table mat-table [dataSource]="tableData" class="mat-elevation-z8">
      <!--- Note that these columns can be defined in any order.
            The actual rendered columns are set as a property on the row definition" -->

      <ng-container *ngFor="let disCol of tableColumns; let colIndex = index" matColumnDef="{{disCol}}">
        <th mat-header-cell *matHeaderCellDef>{{disCol}}</th>
        <td mat-cell *matCellDef="let element">{{element[disCol]}}</td>
      </ng-container>

      <tr class="persues-table-row" mat-header-row *matHeaderRowDef="tableColumns"></tr>
      <tr class="persues-table-row" mat-row *matRowDef="let row; columns: tableColumns;" appTableEvent [rowData]="row"></tr>
    </table>
  </div>
</section>

table-events.directive.ts

import { Directive, HostListener, Input, Output, EventEmitter } from '@angular/core';

@Directive({
  selector: '[appTableEvent]'
})
export class TableEventDirective {
  @Input() rowData;

  @Output()
  clickHandler: EventEmitter<any> = new EventEmitter();

  constructor() { }

  @HostListener('click', ['$event'])
  onClickHandler(event: MouseEvent) {
    this.clickHandler.emit(this);
  }
}

parent component

<app-table
  [tableData]="dataSourceBase"
  [tableColumns]="displayedColumnsBase"
  (clickHandler)="onRowClick($event)">
</app-table>

parent component ts

onRowClick(event) {
  console.log('onRowClick', event)
}

I am not getting data in onRowClick() method.

Now there are some cases when I just wanna use table to just show data, In that case I don't wanna apply directive.

Thanks in advance


Solution

  • You need to create an HostListener on your table component as well:

    @Component({
      selector: 'app-table'
    })
    export class TableComponent {
      @Output()
      clickHandler: EventEmitter<MouseEvent> = new EventEmitter();
    }
    

    and have this emit from your appTableEvent directive:

    <tr class="persues-table-row" mat-row
        *matRowDef="let row; columns: tableColumns;" [rowData]="row"
        appTableEvent (clickHandler)="clickHandler.emit($event)">
    </tr>
    

    If you plan on having multiple of these cases, you can also go for the following solution. You would still need the clickHandler on your TableComponent like I showed before, but you can update your directive like this:

    @Directive({
      selector: '[appTableEvent]'
    })
    export class TableEventDirective {
      @Input() rowData;
    
      constructor(@Host() private table: TableComponent) { }
    
      @HostListener('click', ['$event'])
      onClickHandler(event: MouseEvent) {
        this.table.clickHandler.emit(this);
      }
    }
    

    This way you don't have to add the click handler to the <tr> element