Search code examples
angularangular-componentsdynamic-html

Angular 9 Add click event to dynamic buttons


I have buttons that I need to add for a toolbar and I can't seem to properly add a click event to them without it feeling like a hack.

Here is how I setup/return my buttons:

    export class ImportButtons {
      getButtons(): ButtonItem[] {
         return [new ButtonItem({
                 group: 'imports',
                 section: 'admin',
                 name: 'Approve',
                 class: 'btn btn-primary approve-all-button',
                 text: 'Approve',
                 hasMenu: true,
                 menuClass: 'approve-all-menu',
                 subItems: [new SubItem({ value: 'Approve All' })]
              }),
              new ButtonItem({
              group: 'imports',
              section: 'admin',
              name: 'OnHold',
              class: 'btn btn-primary on-hold-button',
              text: 'On Hold',
              hasMenu: true,
              menuClass: 'on-hold-menu',
              subItems: [new SubItem({ value: 'On Hold All' }),
              new SubItem({ value: 'Revert from On Hold' }),
              new SubItem({ value: 'Revert All from On Hold' })]
             })
           ];
      }
    }

This is in my html that grabs these and displays them:

    <agc class="btn-group dropdown drop-button-container" ngbdropdown="" placement="bottom-right" ng-reflect-placement="bottom-right">
        <ng-container *ngFor="let buttons of this.fillTable('import', 'admin')">
           <ng-container *ngIf="buttons.hasMenu === true">
               <button class="{{buttons.class}}" type="button" (click)="buttonClicked( buttons );" > {{buttons.text}} </button>
               <button aria-haspopup="true" class="btn btn-primary dropdown-toggle dropdown-toggle-split admin-split-button icon-fa-caret-down" data-toggle="dropdown" ngbdropdowntoggle="" type="button" aria-expanded="false"></button>
               <ng-container *ngFor="let sub of buttons.subItems">
                 <agc class="dropdown-menu" ngbdropdownmenu="">
                 <span class="dropdown-item c-pointer" (click)="buttonClicked( buttons, sub )" >{{sub.value}}</span>
                 </agc>
               </ng-container>
           </ng-container>
        </ng-container>
      </agc>

I currently tried calling a method when clicked ('buttonClicked()') where it tells me what was clicked. But when I click a button I see the rest of the buttons blink as if it was doing a PostBack. Because of this it feels like a hack.

Is there a correct way of doing this? Each button added will have it's own method to call when clicked (obviously) and I couldn't find a way to use a string in the click event.

For instance I initially had in my button class a 'click' property where I would put the method name to call - so "click = 'onHoldClick($event)'" and the html click would look like "(click)='{{ buttons.click}}'" but it didn't like that.

the html iterates through the buttons it grabs from method 'fillTable' and it looks like this:

    fillTable(groupName: string, section: string): ButtonItem[] {           
       switch (this.page.currentPage) {
         case 'imports':
           return this.importButtons.getButtons().filter(n => n.section === section);
         case 'export':
           return this.exportButtons.getButtons().filter(n => n.section === section);
         case 'sales':
           return this.salesButtons.getButtons().filter(n => n.section === section);
         case 'bom':
           return this.bomButtons.getButtons().filter(n => n.section === section);
       }
     }

Solution

  • I think that the problem is in the *ngFor when you use a function. I suggest use an auxiliar variable

    this.buttonsActual=this.fillTable('import', 'admin');
    
    //and make the *ngFor using 
    <ng-container *ngFor="let buttons of buttonsActual">
    

    You can try also use trackBy

    BTW, your idea is correct (click)="buttonClicked(button)". Well, you can pass only the button.name or whatever. Then you has two aproach, use a large switch in the function

    buttonClicked(button)
    {
       switch (button.name)
       {
          case "...":
          break;
          case "...":
          break;
          ...
       }
    }
    

    Or store in an object the diferrencs functions

    command={
      one:this.function1
      two:this.function2
    }
    function1(){..}
    function2(){..}
    //and in buttonClicked
    buttonClicked(button)
    {
       command[button.name]();
    }