26

I am using an angular 5 with ag-grid data table i cant able to trigger a click event from cell using cellRenderer here how am using my ag-grid --> colDefs

this.columnDefs = [
            {headerName: '#', rowDrag: true, width: 75},
            {headerName: 'One', field: 'fieldName',
                cellRenderer : function(params){
                    return '<div><button (click)="drop()">Click</button></div>'
                }
            }
];

drop() {
    alert("BUTTON CLICKEFD")
}

if am using onClick="alert("123")" --> it works, but i cant able to use onClick="drop()" it throws drop of undefined,

i tried this too inside of cellRenderer --> params = params.$scope.drop = this.drop;

if am using gridOptions with angularCompileRows : true it throws an error Cannot read property '$apply' of undefined. Do i need to install ag-grid enterprise ??

YuvaMac
  • 419
  • 1
  • 6
  • 13
  • I am not familiar with ag-Grid but you can try replacing the function with an arrow function: `cellRenderer : (params) => {...}`. It preserves the value of `this`. – ConnorsFan Jun 10 '18 at 00:47
  • Its not working buddy... – YuvaMac Jun 10 '18 at 16:37
  • Please show us your code after changing the `cellRenderer` property to an arrow function. For example: `{headerName: 'One', field: 'fieldName', cellRenderer: (params) => {return '
    '}`.
    – Andy King Jun 11 '18 at 15:06
  • `cellRenderer : (params)=> {return ''+params.value+'' }` `this.gridOptions = { enableColResize: true, rowDragManaged: true, onGridReady : function(params) { params.api.sizeColumnsToFit(); }, rowSelection : 'single' }` I Have tried this it cannot triggered the click event do i need to add any options in gridOptions ?? – YuvaMac Jun 14 '18 at 09:35
  • Refer to https://stackoverflow.com/questions/61493626/ag-grid-cell-renderer-icons-are-not-clickable-react/66546366#66546366 – Nivil Boban Mar 09 '21 at 11:54

5 Answers5

27

You can use cellRenderer with a button component. If you want to get the click event on the button from the user on the table, just declare the callback function you want to cellRendererParams.

// app.component.ts
columnDefs = [
{
  headerName: 'Button Col 1',
  cellRenderer: 'buttonRenderer',
  cellRendererParams: {
    onClick: this.onBtnClick.bind(this),
    label: 'Click'
  }
},
...
]

The above code is just a small part, check out full example on Stackblitz

T4professor
  • 286
  • 2
  • 4
23

Angular.
Here we create the button cell renderer as an Angular component that implements the ICellRendererAngularComp interface. Access to the params object can be found on the agInit hook.

// app/button-cell-renderer.component.ts
@Component({
  selector: 'btn-cell-renderer',
  template: `
    <button (click)="btnClickedHandler($event)">Click me!</button>
  `,
})
export class BtnCellRenderer implements ICellRendererAngularComp, OnDestroy {
  private params: any;
  agInit(params: any): void {
    this.params = params;
  }
  btnClickedHandler() {
    this.params.clicked(this.params.value);
  }
  ngOnDestroy() {
    // no need to remove the button click handler as angular does this under the hood
  }
}

The renderer is registered to ag-Grid via gridOptions.frameworkComponents. Note that we’re passing the button click handler dynamically to our renderer via cellRendererParams - allowing for a more flexible and reusable renderer.

// app/app.component.ts
this.columnDefs = [
    {
        field: 'athlete',
        cellRenderer: 'btnCellRenderer',
        cellRendererParams: {
          clicked: function(field: any) {
            alert(`${field} was clicked`);
          }
        },
        minWidth: 150,
    }
    // [...]
];
this.frameworkComponents = {
    btnCellRenderer: BtnCellRenderer
};

It is also necessary to pass our renderer to our @NgModule decorator to allow for dependency injection.

// app/app.modules.ts
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    AgGridModule.withComponents([BtnCellRenderer]),
  ],
  declarations: [AppComponent, BtnCellRenderer],
  bootstrap: [AppComponent],
})

See demo.
Learn more about Angular Cell Renderer.

Vanilla JavaScript.
A DOM element is created in the init method, which is then returned in the getGui method. The optional destroy hook has also included to do some cleanup (removing the click listener from our component).

// btn-cell-renderer.js
function BtnCellRenderer() {}
BtnCellRenderer.prototype.init = function(params) {
  this.params = params;
  this.eGui = document.createElement('button');
  this.eGui.innerHTML = 'Click me!';
  this.btnClickedHandler = this.btnClickedHandler.bind(this);
  this.eGui.addEventListener('click', this.btnClickedHandler);
}
BtnCellRenderer.prototype.getGui = function() {
  return this.eGui;
}
BtnCellRenderer.prototype.destroy = function() {
  this.eGui.removeEventListener('click', this.btnClickedHandler);
}
BtnCellRenderer.prototype.btnClickedHandler = function(event) {
  this.params.clicked(this.params.value);
}

The renderer is registered to ag-Grid in gridOptions.components and is used on the athlete column. Note that we’re passing the button click handler dynamically to our renderer via cellRendererParams - this makes for a more flexible and reusable renderer.

// main.js 
var gridOptions = {
  columnDefs: [
    { 
      field: 'athlete', 
      cellRenderer: 'btnCellRenderer', 
      cellRendererParams: {
        clicked: function(field) {
          alert(`${field} was clicked`);
        }
      },
      minWidth: 150
    },
  // [...]
  components: {
    btnCellRenderer: BtnCellRenderer
  }
};

See demo.
Learn more about JavaScript Cell Renderers.

React.
Here our button cell renderer is constructed as a React component. The only thing to take note of here is that cell params will be available on the component via props.

// BtnCellRenderer.jsx
class BtnCellRenderer extends Component {
  constructor(props) {
    super(props);
    this.btnClickedHandler = this.btnClickedHandler.bind(this);
  }
  btnClickedHandler() {
   this.props.clicked(this.props.value);
  }
  render() {
    return (
      <button onClick={this.btnClickedHandler}>Click Me!</button>
    )
  }
}

The renderer is registered to ag-Grid via gridOptions.frameworkComponents. The button click handler is passed to our renderer at run time via cellRendererParams - allowing for a more flexible and reusable renderer.

// index.jsx
columnDefs: [
    {
          field: 'athlete',
          cellRenderer: 'btnCellRenderer',
          cellRendererParams: {
            clicked: function(field) {
              alert(`${field} was clicked`);
            },
          },
        // [...]
    }
];
frameworkComponents: {
    btnCellRenderer: BtnCellRenderer,
}

See demo.
Learn more about React Cell Renderers.

Vue.js.
Configuring the renderer in Vue.js is simple:

// btn-cell-renderer.js
export default Vue.extend({
  template: `
        <span>
            <button @click="btnClickedHandler()">Click me!</button>
        </span>
    `,
  methods: {
    btnClickedHandler() {
      this.params.clicked(this.params.value);
    }
  },
});

As with the other frameworks, the renderer is registered to ag-Grid via gridOptions.frameworkComponents and the button click handler is passed to our renderer at run time via cellRendererParams - allowing for a more flexible and reusable renderer.

// main.js
    this.columnDefs = [
      {
        field: 'athlete',
        cellRenderer: 'btnCellRenderer',
        cellRendererParams: {
          clicked: function(field) {
            alert(`${field} was clicked`);
          }
        },
      // [...]
    ],
    this.frameworkComponents = {
      btnCellRenderer: BtnCellRenderer
    }

See demo.
Learn more about Vue.js Cell Renderers.

Read the full blog post on our website or check out our documentation for a great variety of scenarios you can implement with ag-Grid.

Ahmed Gadir | Developer @ ag-Grid

AO_
  • 2,573
  • 3
  • 30
  • 31
ahmedg94
  • 446
  • 4
  • 7
  • This comment might help someone. After tying solutions in this question the click was not working. it was because of button's z-index. i added the z index and it worked. – Raas Masood Jun 29 '20 at 20:00
  • is there any way I can get some other data to this button component? some other field data from the table, like if I want the age field in the button component – Shubham Kumar Sep 28 '20 at 14:05
  • `this.btnClickedHandler = this.btnClickedHandler.bind(this);` was a great find, as well as `this.eGui.addEventListener('click', this.btnClickedHandler);` which showed me I was trying to bind a specific sub-element, and should have been binding the `eGui` level element instead. – nmz787 Mar 05 '22 at 08:44
2

To expand on the answer from @T4professor, I will post some code to also have a dynamic label on that Click button.

// Author: T4professor

import { Component, OnInit, AfterContentInit } from '@angular/core';
import { ICellRendererAngularComp } from 'ag-grid-angular';

@Component({
  selector: 'app-button-renderer',
  template: `
    <button class="{{btnClass}}" type="button" (click)="onClick($event)">{{label}}</button>
    `
})

export class ButtonRendererComponent implements ICellRendererAngularComp {
    //https://stackblitz.com/edit/angular-ag-grid-button-renderer?file=src%2Fapp%2Fapp.component.ts
  params: any;
  label: string;
  getLabelFunction: any;
  btnClass: string;

  agInit(params: any): void {
    this.params = params;
    this.label = this.params.label || null;
    this.btnClass = this.params.btnClass || 'btn btn-primary';
    this.getLabelFunction = this.params.getLabelFunction;

    if(this.getLabelFunction && this.getLabelFunction instanceof Function)
    {
      console.log(this.params);
      this.label = this.getLabelFunction(params.data);
    }

  }

  refresh(params?: any): boolean {
    return true;
  }

  onClick($event) {
    if (this.params.onClick instanceof Function) {
      // put anything into params u want pass into parents component
      const params = {
        event: $event,
        rowData: this.params.node.data
        // ...something
      }
      this.params.onClick(params);

    }
  }
}

Then, in the component with the grid you do the following:

columnDefs = [

    {
      headerName: 'Publish',
      cellRenderer: 'buttonRenderer',
      cellRendererParams: {
        onClick: this.onRowPublishBtnClick.bind(this),        
        label: 'Publish',
        getLabelFunction: this.getLabel.bind(this),
        btnClass: 'btn btn-primary btn-sm'
      }
    }  
]

onRowPublishBtnClick(e) {    
    this.rowDataClicked = e.rowData;
  }

  getLabel(rowData)
  {    
    console.log(rowData);
    if(rowData && rowData.hasIndicator)
      return 'Republish';
      else return 'Publish';
  }
Dragos Durlut
  • 8,018
  • 10
  • 47
  • 62
0

You have this issue because you invoke drop() incorrectly you should change it to this.drop()

In general you should use cellRenderer property with simple logic. More convenient way for complex logic renderer you should use cellRendererFramework: YourCustomRendererAngularComponent.

columnDefs = [
{
  headerName: 'Col Name',
  cellRendererFramwork: MyAngularRendererComponent, // RendererComponent suffix it is naming convention
  cellRendererParams: {
    onClick: (params) => this.click(params);  
  }
},
...
]

MyAngularRendererComponent should implements AgRendererComponent.

Also in angular module where you use MyAngualrRendererComponent don`t forget put this code:

@NgModule({
 imports: [
   AgGridModule.withCompoennts([
      MyAngualrRendererComponent 
   ])
 ]
})
CREZi
  • 111
  • 1
  • 6
0

I was looking for a solution to this but for multiple buttons in the same column. I couldn't find an answer anywhere so I wrote up this Plain Javascript solution. I hope it helps other people looking for the solution I was looking for. Also open to suggestions on how to make the javascript less hacky.

// multi-btn-cell-renderer.js

function multiBtnCellRenderer() {}

multiBtnCellRenderer.prototype.init = function(params) {
  var self = this;
  self.params = params;
  self.num_buttons = parseInt(this.params.num_buttons);
  self.btnClickedHandlers = {};
  let outerDiv = document.createElement('div')
  for(let i = 0; i < self.num_buttons; i++) {
    let button = document.createElement('button');
    button.innerHTML = self.params.button_html[i];
    outerDiv.appendChild(button);
    self.btnClickedHandlers[i] = function(event) {
      self.params.clicked[i](self.params.get_data_id());
    }.bind(i, self);
    button.addEventListener('click', self.btnClickedHandlers[i]);
  }
  self.eGui = outerDiv;
};

multiBtnCellRenderer.prototype.getGui = function() {
  return this.eGui;
};

multiBtnCellRenderer.prototype.destroy = function() {
  for(let i = 0; i < this.num_buttons; i++) {
    this.eGui.removeEventListener('click', this.btnClickedHandlers[i]);
  }
};

// main.js

var columnDefs = [
  {
    headerName: "Action",
    maxWidth: 60,
    filter: false,
    floatingFilter: false,
    suppressMenu: true,
    sortable: false,
    cellRenderer: multiBtnCellRenderer,
    cellRendererParams: {
      num_buttons: 2,
      button_html: ["<i class='fa fa-pencil'></i>","<i class='fa fa-trash'></i>"],
      get_data_id: function() {
        return this.data.id;
      },
      clicked: {
        0: function(data_id) {
          $.get(`/employee/${data_id}/edit`)
        },
        1: function(data_id) {
          $.delete(`/employee/${data_id}`)
        }
      }
    }
  }
]
Rob Carpenter
  • 602
  • 7
  • 16