Tarun Kumar
Tarun Kumar

Reputation: 148

Angular 4 add click event on button dynamically

Here is the code of button displayed from loop.

for( let k=0; k<this.newTmpArr.length; k++ ){
    myTable += "<tr>";            
    for( let key2 in this.newTmpArr[k] ){
        if(this.newTmpArr[k][key2] == 'button' ){
          myTable += `<td><button (click)="test()">Click</button></td>`; 
          break;    
        }
        if(this.newTmpArr[k][key2] == 'link' ){
          myTable += `<td><a href="#" (click)="test()">Click</a></td>`; 
          break;    
        } else {
          myTable += "<td>" + this.newTmpArr[k][key2] + "</td>";                    
        }

    }
    myTable += "</tr>"; 
}

test(){
  alert("Working!");
}

Here is the screenshot of the page look like after dynamic data rendered.

enter image description here

Upvotes: 4

Views: 22156

Answers (4)

Pranay Rana
Pranay Rana

Reputation: 176896

Read for detail : Dynamic Html Structure with - *ngIf, *ngFor & ngSwitch

you can try out , Template way below is working fine without much change, below took just 20 min

App.Component.html --- template file

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Date</th>
      <th>Ammount</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
   <tr *ngFor="let ele of newTmpArr" >
     <td>{{ele[0]}}</td>
     <td>{{ele[1]}}</td>
     <td>{{ele[2]}}</td>
     <td>
        <div ngSwitch="{{ele[3]}}">
            <div *ngSwitchCase="'button'">
              <input type="button" (click)="DoWork(1)" value="click"/>
            </div>
            <div *ngSwitchCase="'link'">
                <a href="#" (click)="DoWork(1)" >click</a>
            </div>
        </div> 
     </td>
   </tr>
  </tbody> 
</table>

App.Component.ts--typescript

export class AppComponent implements OnInit {
ngOnInit(): void {}
title = 'app';
DoWork()
  {
    //here you can do work , 
    //this get called when you press "click" button in table 
    alert(this.title);
  }
}

Without template dynamic way

It took me 5 hours to reach this code but fater that Following is working fine , you need to by pass security with the help of DomSanitizer as you want to put input element from you template file.

//typings.d.ts
interface Window { my: any; }

above one extends your window object

app.Component.html

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Date</th>
      <th>Ammount</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody id="tid" [innerHTML]="bodytext"></tbody> 
</table>

typescript file - app.Component.ts

import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import {Component, NgZone, OnInit, OnDestroy} from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})

export class AppComponent implements OnInit {
   title = 'app';
  _myTable : string;
  bodytext ;
  newTmpArr = [
          ["567", "2017-04-17T14:22:48", "45000", "button"],
          ["567", "2017-04-17T14:22:48", "45000", "button"],
          ["567", "2017-04-17T14:22:48", "45000", "link"],
          ["567", "2017-04-17T14:22:48", "45000", "button"]
        ];
   constructor(private http: Http,private _sanitizer: 
                 DomSanitizer,private ngZone: NgZone) {
   }

  ngOnInit(): void {
    //below code add dynamci table to your html
    for( let k=0; k<this.newTmpArr.length; k++ ){
            this._myTable += "<tr>";            
            for( let key2 in this.newTmpArr[k] ){
                if(this.newTmpArr[k][key2] == 'button' ){
                //below is table click button calls angular function, 
                //here 1 is passed as arugment you can pass 
                //any other value you want
                //or if you dont want to pass than just do
                // onclick="my.namespace.publicFunc();" 
                // and remove parameter from typscript function also 
                this._myTable += `<td><input  type="button" 
                   onclick="my.namespace.publicFunc(1);" 
                   value="Click me"  /></td>`; 
                break;    
                }
                if(this.newTmpArr[k][key2] == 'link' ){
                this._myTable += `<td><a href="#"
                        onclick="my.namespace.publicFunc(1); 
                             return false;">Click</a>
                         </td>`; 
                break;    
                } else {
                this._myTable += "<td>" + 
                   this.newTmpArr[k][key2] + "</td>";                    
                }
            }
            this._myTable += "</tr>"; 
        }
         this.bodytext = 
        this._sanitizer.bypassSecurityTrustHtml(this._myTable);
        //code to add dynamic html ends here 

         //below script helps to expose anglar function to javascript 
         window.my = window.my || {};
         window.my.namespace = window.my.namespace || {};
        //below line bind typescript function to window object publicFunc
         window.my.namespace.publicFunc = 
          this.OnTableButtonClick.bind(this);
        //code to expose typscipt function ends here 
  }

  //this function get called when Table click button ge clicked 
  public OnTableButtonClick(input:string):void{ 
    this.ngZone.run(() => this.DoWork(input)); 
  }

  DoWork(input:string)
  {
    //here you can do work , 
    //this get called when you press "click" button in table 
    alert(this.title + input);
    //this will display app1 in output
  }
}

Upvotes: 4

Tarun Kumar
Tarun Kumar

Reputation: 148

here is the code work for me. it may helpful for you guys too.

<tr *ngFor="let item of newTmpArr">
       <td *ngFor="let i of item">
          <span *ngIf="i.value; then dynamicVal; else elseblock"></span>
          <ng-template #dynamicVal>
          <span *ngIf="i.type == 'button'">
          <button (click)="clickMe(item)">Click</button>
          </span>
          <span *ngIf="i.type == 'link'">
             <a [href]="i.link">{{i.value}}</a>
          </span>
          </ng-template> 
          <ng-template #elseblock>{{i}}</ng-template>
       </td>
</tr> 

Upvotes: -1

bryan60
bryan60

Reputation: 29325

I'm going to go ahead and post the way I feel this should be done, in the angular way. Writing html in code almost always means you're doing something wrong. This is template logic that should be done IN TEMPLATE, like this:

<tr *ngFor="let row of newTmpArr">
  <td *ngFor="let cell of row">
    <ng-container [ngSwitch]="cell">
      <button *ngSwitchCase="'button'" (click)="test()">Click</button>
      <a *ngSwitchCase="'link'" href="#" (click)="test()">Click</a>
      <ng-container *ngSwitchDefault>{{ cell }}</ng-container>
    </ng-container>
  </td>
</tr>

This is the angular way of achieving your goal, all done in template, using nested ng for loops and then an ngSwitch is the appropriate directive for this use case since you have 2 options and a default. Now you do not need to fight the framework or use any hacks like binding functions to the window. Angular will bind functions appropriately using this method. You also have the distinct advantage of your display being model driven, so you only need to ever update your model and the display will react accordingly, so you never need to worry about display and model falling out of sync.

For example if you were to remove row 5, you would need to manually redraw your table after updating your model, same if you added a new row, or if you edit cell 2 of row 3. Using template, angular manages all of this in a transparent and reliable manner.

Upvotes: 1

Neji
Neji

Reputation: 5

As said above you can use the *ngFor in your table row in your component.html file. Then in the button tag handle the click event as you would normally do.

<td> <button (click)="clickEvent()"> Click </button> </td>

Then in your component.ts file define the on click event

clickEvent(){console.log("click event triggered")}

Upvotes: -3

Related Questions