Home > Enterprise >  How can I listen for specific button presses from a parent component in Angular?
How can I listen for specific button presses from a parent component in Angular?

Time:11-17

I have a table that is meant to act as a configurable base class. In that class I want to be able to pass a menu with actions for each row:

 <table mat-table [dataSource]="tableDataSource" matSort (matSortChange)="sortTable($event)" >
        ...
        <!-- action column -->
        <ng-container *ngIf="rowActionIcon?.length" [matColumnDef]="rowActionIcon">
            <th mat-header-cell *matHeaderCellDef></th>
            <td mat-cell  *matCellDef="let element" [id]="rowActionIcon" (click)="emitRowAction(element)">
                <button mat-button [matMenuTriggerFor]="menu">
                    <mat-icon>{{rowActionIcon}}</mat-icon>
                </button>
                <mat-menu #menu="matMenu">
                    <button mat-menu-item *ngFor="let action of menuActions"><mat-icon>{{action.icon}}</mat-icon>{{action.label}}</button>
                </mat-menu>
            </td>
        </ng-container>
</mat-table>

The action columns shows a button with a dropdown of menu items:

enter image description here

How can I in my child component that uses this component get these clicks, along with the data for the row? Ideally I'd like to display that data in a dialog somehow.

The child component defines the menuActions that should exist on the parent table:

export class CustomersComponent {
  customers: Customer[];
  selectedCustomer: Customer | null;
  menuActions: MenuItemDefinition[];
  customerColumns: { name: string; dataKey: string; isSortable: boolean }[];
  constructor(private cust_api: CustomerApiService, public dialog: MatDialog) {
    this.customers = [];
    this.selectedCustomer = null;
    this.menuActions = [
      { icon: 'border_color', label: 'Edit' },
      { icon: 'delete', label: 'Delete' },
    ];
    this.customerColumns = [
      {
        name: 'Region',
        dataKey: 'region',
        isSortable: true,
      },
      { name: 'Name', dataKey: 'name', isSortable: true },
      { name: 'Id', dataKey: 'id', isSortable: true },
    ];
  }

and in the template:

  <app-data-table 
        [isFilterable]="true"
        [isPageable]="true"
        [tableColumns]="customerColumns"
        [tableData]="customers"
        [rowActionIcon]="'more_vert'"
        [menuActions]="this.menuActions"
        >
    </app-data-table>

But I'm completely clueless as to how I should dynamically map data and actions to each of the buttons I create in the parent template.

CodePudding user response:

If I am not wrong then you have implemented Parent / child component where parent gives data to child component for display.

So when action perform using child component need to pass output parameter for value as below:

Parent Component

HTML

<app-data-table 
        [isFilterable]="true"
        [isPageable]="true"
        [tableColumns]="customerColumns"
        [tableData]="customers"
        [rowActionIcon]="'more_vert'"
        [menuActions]="this.menuActions"
        (onAction)="onParentAction($event)"
        >
    </app-data-table>

ts

onParentAction(res:any){
  //@ts-ignore
  if(res.action=="EDIT"){
   your action here ......
  }else if(res.action == "DELETE"){
   your action here ....
  }
}

Child Component

  @Output() onAction = new EventEmitter<any>();

  onClickEdit(id: number) {
    this.onAction.emit({"action":"EDIT","id":id});
  }
  onClickDelete(id: number) {
    this.onAction.emit({"action":"DELETE","id":id});
  }

Explanation : When user perform any action in child component like edit then they need to call function called onClickEdit with value of id so using emit method of output parameter value go back to parent component & parent get object with two pair object { action: "EDIT or DELETE" & id value }

CodePudding user response:

Complementary the @Parth M. Dave's answer, to control the actions, you should use the mat-menu-item -not in td (click)-. Just use:

<button mat-menu-item *ngFor="let action of menuActions" 
        (click)="doSomething(element,action.label)">
        <mat-icon>{{action.icon}}</mat-icon>{{action.label}}
</button>

doSomething(element:any,action:string)
{
    switch(action)
    {
        case "Edit":
           this.onAction.emit({"action":"EDIT","element":element});
         break;
        case "Delete":
           this.onAction.emit({"action":"EDIT","element":element});
         break;
    }
}

But you needn't use a variable "rowActionIcon". In a mat-table the columns are showed if is in the array "displayedColumns", so you should remove the (click)="emitRowAction(element)" In the td

the stackblitz it's only the part of "mat-menu"

  • Related