Reputation: 1986
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
Upvotes: 2
Views: 3242
Reputation: 3387
Working Demo in this StackBlitz Link
Here, you can use service
and rxjs - Behaviorsubject
. For this you have to create one service and then in this service define your Behaviorsubject
and all common communicate related stuff. You have to inject this one service in component and in directive to. so that, dont have to @Output()
multiple level and one benifit of this you can communicate with any parent level of component regardless of chaining of events.
table.component.html
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Company Name</th>
<th scope="col">Brand</th>
<th scope="col">Engine Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of tableService.tableData; let i = index" appTableRow [rowData]="row">
<th scope="row">{{row.id}}</th>
<td>{{row.companyName}}</td>
<td>{{row.brand}}</td>
<td>{{row.engineType}}</td>
</tr>
</tbody>
</table>
<div *ngIf="selectedRow | async as rowSelect" class="alert alert-primary">
<pre>
{{rowSelect | json}}
</pre>
</div>
table.component.ts
export class TableComponent implements OnInit {
selectedRow;
constructor(private tableService: TableDataService) { }
ngOnInit() {
this.selectedRow = this.tableService.getTableRow();
}
}
directive.ts
@Directive({
selector: '[appTableRow]'
})
export class TableRowDirective {
@Input() rowData;
constructor(private tableRowSerivce: TableDataService) { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.tableRowSerivce.tableRowData.next(this.rowData);
}
}
table-service.ts
@Injectable()
export class TableDataService {
tableData = [{
id: 1,
companyName: 'Audi',
brand: 'A4',
engineType: 'Diesel'
},{
id: 2,
companyName: 'Rolls-Roys',
brand: 'R4',
engineType: 'Diesel'
},{
id: 3,
companyName: 'Hyundai',
brand: 'Kona',
engineType: 'Electric'
},{
id: 4,
companyName: 'Ford',
brand: 'Endevour',
engineType: 'Diesel'
}];
tableRowData : BehaviorSubject<any> = new BehaviorSubject<any>({});
tableRowData$ = this.tableRowData.asObservable();
constructor() { }
getTableRow(): Observable<any>{
return this.tableRowData$;
}
}
Upvotes: 1
Reputation: 71961
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
Upvotes: 1