Ag-Grid cellRender with Button Click - javascript

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 ??

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

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

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';
}

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
])
]
})

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}`)
}
}
}
}
]

Related

How to disable fa icon in span when data is loading?

I have anuglar 11 application. And I am using an icon to load a graph. But the time that the graph is loaded when the icon is triggered takes a long time. So to prevent that a user triggers many times the icon. I want to disable the icon till the graph is loaded.
So this is what I have for the icon:
<span (click)="createChartFromMap(selectedSensor.sensor.charts[0],selectedSensor.properties['key'],selectedSensor.properties['name'] )"
class="ml-auto " >
<fa-icon [icon]="selectedSensor.sensor.icon" [styles]="{'color': '#BF0404'}" size="lg" class="menu-list-item">
</fa-icon>
</span>
and this is the method:
createChartFromMap(element: string, node: string, name: string) {
const chartParams: ChartParams = new ChartParamsObj(
node,
DateTime.utc().startOf('day').toISO(),
DateTime.utc().endOf('day').toISO(),
'P1D'
);
const el = {
config: {
label: `${name}`,
xrange: [
DateTime.local().startOf('day').toFormat('yyyy-LL-dd HH:mm:ss'),
DateTime.local().endOf('day').toFormat('yyyy-LL-dd HH:mm:ss')
],
yrange:[0, 10]
},
type: element,
paramObj: chartParams
};
this.mapRegistryService.components.load(el.type, el.config, el.paramObj);
}
and the service that loads the data looks like this:
$blockButtonGraph: Observable<boolean>;
components = {
'area-chart':
{
component: AreaChartComponent,
config: {
grid: {
style: 'area-chart',
},
call: (params): Observable<WifiDensityDto[]> => {
return this.wifiDensityService.getWifiDensities(
DateTime.utc(params.start).startOf('day').toISO(),
DateTime.utc(params.end).endOf('day').toISO(),
params.node)
},
}
},
'line-chart':
{
component: LineChartComponent,
config: {
grid: {
style: 'line-chart'
},
call: (params) => {
return this.cameraValuesService.cameraDataInInterval(
params.start,
params.end,
params.node)
}
}
},
load: (comp, config, paramObj?) => {
const cmp =JSON.parse(JSON.stringify(this.components[comp]));
cmp.config.grid.label = config.label;
cmp.config.grid.id = this.components.createUnId();
},
createUnId: () => {
const id = new Date().getTime();
return id;
},
register: (comp: any, injector: Injector) => {
const factory = new WidgetFactory(
this.components[comp.config.grid.name].component,
{
element: comp.config.grid.name,
config: comp.config
}
);
}
};
So I made a $blockButtonGraph observable.
But how to use now that observable?
Thank you
use [disabled] in your template. I'm not sure, but you might have to change your <span> to a <button> but it would look something like this:
<button
(click)=createChartFromMap(...)
[disabled]=$blockButtonGraph | async>
</button>

"this" doesn't work in click event handler

I have attached a click event handler to my ChartJS-derived component, like this:
export default {
extends: HorizontalBar,
data() {
return {
my_data: [],
options: {
onClick: function(event, args) {
//need to access my_data here
}
},
};
},
}
I need to access one of my data members inside the handler. Unfortunately, this.my_data doesn't work here. ChartJS documentation tells me that this event is called in the context of the Chart component, not my Vue component. How can I get access to my_data?
update
So I'm now using #Dan's way of defining the handler:
export default {
extends: HorizontalBar,
data() {
return {
my_data: [],
options: {
onClick: this.ClickHandler,
},
};
},
methods: {
ClickHandler: function(event, args) {
var datapoint = this.getElementAtEvent(event);
var value = this.my_data[datapoint._datasetIndex];
},
}
}
The handler is called correctly, but this is now refering to my Vue component and therefore I do not have any reference to the Chart context to call its getElementAtEvent.
So if I declare it in front of onClick above, I get the Chart context in this, but no longer have access to my_data. If I use your way, I get this.my_data, but lose Chart context.
You need to put the handler into your methods object and then reference it from the chart options handler:
data() {
return {
my_data: [],
options: {
onClick: this.ClickHandler,
}
};
},
methods: {
ClickHandler: function(event, points) {
// Here is how to access the chart
const c = this._data._chart;
const datapoint = c.getElementAtEvent(event)[0];
const indexBar = datapoint._index;
const indexSegment = datapoint._datasetIndex;
// Do whatever with this.my_data, indexBar, and indexSegment
}
}
The chart is accessible to the component through this._data._chart.
Create a closure variable
data() {
const vm = this;
return {
my_data: [],
options: {
onClick: function(event, args) {
this.chartjs.something;
vm.my_data[]
}
}
}

State changes but component doesn't update - ANGULAR

I'm using ionic with angular, when i'm creating alert in ok button's callback function i'm changing my state. State changes, but this change doesn't effects in iu, i think component is not updating. How can I fix this?
async presentAlert() {
const alert = await this.alertController.create({
header: '',
message: '',
buttons: [
'cancel',
{
text: 'ok',
handler: () => {
this.currentScreen = "";
this.dates[this.currentDateIndex].isOrdered = false;//disable order
}
}
]
});
await alert.present();
}
You can try to use ChangeDetectorRef to explicitly state that change has been made and view needs to be updated.
Reference: https://angular.io/api/core/ChangeDetectorRef
Example:
Declare ChangeDetectorRef in constructor
constructor(public cd: ChangeDetectorRef) {}
Then use it in your callback:
buttons: [
'cancel',
{
text: 'ok',
handler: () => {
this.currentScreen = "";
this.dates[this.currentDateIndex].isOrdered = false;//disable order
this.cd.detectChanges();
}
}
]

Elegant way to apply jquery plugin in Angular2

I fetch data from webservice in ngOnInit block and then i need to apply jquery data table plugin (this action i performed both in ngAfterViewInit and ngAfterContentInit). The problem is that jquery datatable plugin don't see data in the component table which was fetched in ngOnInit block. The only way I could implement this is setTimeout or setInterval (which stops execution if plugin instance created).
Is there any other method to perform task I try to ?
export class DataTable implements OnInit, AfterViewInit, AfterContentInit, AfterContentChecked {
public data = null;
public table = null;
constructor(public translate: TranslateService, public ngZone: NgZone) {
}
ngOnInit(): void {
this.translate.getData().subscribe((records) => {
this.data = records;
});
}
ngAfterViewInit(): void {
this.dataTableDynamicUpdater();
}
ngAfterContentInit() : void {
}
ngAfterContentChecked() {
}
dataTableDynamicUpdater() {
let self = this;
let interval = setInterval(() => {
if(self.data) {
self.instantiateDataTable();
clearInterval(interval);
}
}, 200);
}
instantiateDataTable() {
let _FILTER_PLACEHOLDER_ = this.translate.instant('APP_DATATABLE_PLACEHOLDER_TYPE_TO_FILTER');
let _FILTER_LABEL_ = this.translate.instant('APP_DATATABLE_LABEL_FILTER');
let _SHOW_LABEL_ = this.translate.instant('APP_DATATABLE_LABEL_SHOW');
$.extend($.fn.dataTable.defaults, {
autoWidth: false,
dom: '<"datatable-header"fBl><"datatable-scroll-wrap"t><"datatable-footer"ip>',
language: {
search: '<span>_FILTER_LABEL_</span> _INPUT_',
lengthMenu: '<span>_SHOW_LABEL_</span> _MENU_',
paginate: { 'first': 'First', 'last': 'Last', 'next': '→', 'previous': '←' }
}
});
// Column selectors
let table = $('.table').DataTable({
buttons: {
buttons: [
{
extend: 'excelHtml5',
className: 'btn btn-default',
exportOptions: {
columns: ':visible'
}
}
]
}
});
// Add placeholder to the datatable filter option
$('.dataTables_filter input[type=search]').attr('placeholder', _FILTER_PLACEHOLDER_);
// Enable Select2 select for the length option
$('.dataTables_length select').select2({
minimumResultsForSearch: Infinity,
width: 'auto'
});
return table;
}
}
Try doing it after the data was fetched
ngOnInit(): void {
let self = this;
this.translate.getData().subscribe((records) => {
this.data = records;
self.instantiateDataTable();
});
}

How to make react data grid table cell editable and focused on click of a button without performing a double click action

I am using react data grid to list down my data .
I want to add a new row on click of a button and therefore the first column ( not necessarily first it can be any ) should be editable and focused upon creation.
I know react data grid has editable cell feature on double click. But I want this on row creation without any click. And it would be great if there is any way to disable editing once user hit enter.
Code to generate list:
import React, { Component } from 'react';
import FileMenuFormatter from './filemenuformatter';
import NameFormatter from './nameformatter';
import ListView from '../../components/listview/listview';
import { getRow } from '../../helpers/fileviewer/fileutils';
const columns = [
{
key: 'name',
name: 'Name',
formatter: NameFormatter,
resizable: true,
sortable: true
}, {
key: 'created',
name: 'Created On',
resizable: true,
sortable: true
}, {
key: 'lastmodified',
name: 'Last Modified',
resizable: true,
sortable: true
}, {
key: 'filesize',
name: 'File Size',
resizable: true,
sortable: true
},
{
key: 'menu',
name: '',
width: 35,
formatter: FileMenuFormatter,
draggable: false
}
];
/**
*
*
* #class myComp
* #extends {Component}
*/
class myComp extends Component {
getList() {
let rows = [];
for (let index in this.props.dList) {
if (typeof index !== 'undefined') {
let dir = this.props.dList[index];
rows.push(getRow("dir", dir))
}
}
for (let index in this.props.fList) {
if (typeof index !== 'undefined') {
let file = this.props.fList[index];
rows.push(getRow("file", file))
}
}
var newRow = this.props.newRow;
if(Object.keys(newRow).length !== 0) {
rows.push(getRow("dir", newRow[0]));
}
return rows
}
getRow(rowId) {
return this.getList()[rowId];
}
render() {
let rowListData = this.getRowList();
return (
<div>
<ListView
columns={columns}
rows={rowListData}
minHeight={this.props.minHeight} />
</div>
);
}
}
export default myComp;
Anybody has any idea how to achieve this ?
I have resolved this problem. The workaround is here.
so onClick of a button I called my showActive() function which is responsible to make the column ( 1st in my case ) editable exactly as react data grid does.
1st make the column editable, ReactDataGrid takes "editable" as input function. allowEdit is to check if this column is editable. For me I wanted to make the cell editable only upon new row creation.
Create new obj to insert as the new row in the table. Like this -
let newRow = [
{
created: milliseconds,
absPath: this.props.dirSelectedPath,
modified: milliseconds,
size: 0,
filename: folderName,
type: "FILE_DIR",
allowEdit: true
}
];
Then below is the column configuration of the editable cell.
const columns = [
{
key: 'name',
name: 'Name',
resizable: true,
sortable: true,
filterable: true,
editable: function (rowData) {
return rowData.allowEdit ? true : false;
}
}
];
Now you have to write a function to show the cell highlighted and active. To do so I called the same function as react data grid calls.
get the handle of grid.
<ReactDataGrid showActive={this.showActive.bind(this)} rowsCount={this.getSize()} ref={node => this.grid = node} {...this.props } />
showActive() {
let length = this.getSize(); // get the length of the grid i.e number of rows
let obj = { idx: 0, rowIdx: length };
let promise = new Promise(function (resolve, reject) {
if (this.grid) {
resolve("this worked!");
}
}.bind(this));
promise.then(function () {
this.grid.onSelect(obj);
this.grid.setActive('Enter');
}.bind(this), function () {
});
}
hope the helps.

Categories