angulartypescriptangular-materialangular-componentsangular-event-emitter

Angular: A Shared Table Component adding inline buttons passed from Parent Component


I am stuck but pretty sure what I am doing is possible.

Scenario: I have made a common-table.component.ts that contains a mat-table (Angular Material Table) to display data.
Now I need to add buttons(with events) to it from the parent in a very generic/dynamic way. The child-component will need to update attributes or parameters on button to make it specific to that row.

Scenario Example:

  1. A "Users" page with a table of users (where the table is a child component.)
  2. Now we want to add inline buttons to do custom actions like (edit/go-to-details/remove).
  3. As the rows render each button is customized for that row
           Template-Html: <button type="button" (click)="removeUser( row.id )">Edit
  4. Here is a quick example of the above
    https://stackblitz.com/edit/angular-datatable-responsive-mhbdfn?file=src%2Fapp%2Fviews%2Fshared-table%2Fshared-table.component.html

Ideas so far involve passing in
CustomElement has a string html-template with events to match
or using the Renderer2-Element to change the DOM?

Renderer2 example.
https://stackblitz.com/edit/angular-gnyd4e?file=src%2Fapp%2Fapp.component.ts
or
the other way is to give ID's and in the parent use addEventListener()

But I am open for suggestions or thoughts of the best way to do this? enter image description here


Solution

  • Ok I finally figured out a way to make it work but would still be interested in how others do it.

    Summary: In the Parent Component there is an object that contains column information, I extended that with a 'button' section and included the name of the object I want executed and the parent object itself. Setting the method by itself will work but if you use any of the other private methods or passed in modules then it will throw an error. In the HTML I added a new output method but set it to '$event' instead of a method (this was the part I was missing) In the Child Component I made a new output object for the event that will be fired. Then in my HTML I loop through and build my cells while checking the button object. If there is a button, I make it and set the click event to emit the output and dynamically set the method I want to do

    Parent Component

    HTML

    <shared-table [dataSource]="dataSource" [columnsdef]="columns" (parentMethod)="$event"></shared-table>    
    

    Typescript:

    public dataSource = new MatTableDataSource <SampleTable>(); 
     public columns: IColumn[] = [
            { id: 'title', label: 'Title'},
            { id: 'id', label: 'Id'},
            {
                id: 'testing',
                button: {
                    id: "button1",
                    buttonText: "showAlertMessage",
                    clickMethodName: "alertMe",
                    parentClassObject: this
                }
            }
           ]
    

    Child Component
        *please note I am skipping code not relevant to answer.

    HTML (inside Angular Material Table)

    <ng-container [matColumnDef]="column.id" *ngFor="let column of allColumns">
        <th mat-header-cell *matHeaderCellDef [fxFlex]="column.width + 'px'" mat-sort-header [ngClass]="!column.visible ? 'sr-only' : null">
                {{ column.label }}
            </th>
            <td mat-cell *matCellDef="let row" [fxFlex]="column.width + 'px'" [ngClass]="!column.visible ? 'sr-only' : null">
                <ng-container *ngIf="!column.button">
                    {{ row[column.id] }}
                </ng-container>
                <ng-container *ngIf="column.button">
                    <button (click)="parentMethod.emit(column.button.parentClassObject[column.button.clickMethodName](row))">
                        <span>{{column.button.buttonText}}</span>
                    </button>
                </ng-container>
            </td>
        </ng-container>
    

    TypeScript

    @Input() dataSource: MatTableDataSource<any>;
    @Input() columnsdef: IColumn[];
    @Output() parentMethod = new EventEmitter<any>();