How to show row number as the first column? - ag-grid

It's like this (but start from 1):
Please note the number is for the row element of grid instead of row data of the source. So the number shown in the first cell of each rows should indicate the position of the current row (which starts from 1 for the first row) regardless the row data and the sorting criteria.
Update: The result is like this: https://jsfiddle.net/wp6o350z/
<script src="https://unpkg.com/ag-grid/dist/ag-grid.min.noStyle.js"></script>
<link rel="stylesheet" href="https://unpkg.com/ag-grid/dist/styles/ag-grid.css">
<link rel="stylesheet" href="https://unpkg.com/ag-grid/dist/styles/ag-theme-balham.css">
<div id="myGrid" style="height: 200px;width:500px;" class="ag-theme-balham"></div>
<script type="text/javascript" charset="utf-8">
// specify the columns
var columnDefs = [
{headerName: "#", field: "row", width: 30 },
{headerName: "Make", field: "make", width: 100 },
{headerName: "Model", field: "model", width: 100},
{headerName: "Price", field: "price", width: 100}
];
// specify the data
var rowData = [
{row: 1, make: "Toyota", model: "Celica", price: 35000},
{row: 2, make: "Ford", model: "Mondeo", price: 32000},
{row: 3, make: "Porsche", model: "Boxter", price: 72000},
{row: 4, make: "Toyota", model: "Celica", price: 35000},
{row: 5, make: "Ford", model: "Mondeo", price: 32000},
{row: 6, make: "Porsche", model: "Boxter", price: 72000},
{row: 7, make: "Toyota", model: "Celica", price: 35000},
{row: 8, make: "Ford", model: "Mondeo", price: 32000},
{row: 9, make: "Porsche", model: "Boxter", price: 72000}
];
// let the grid know which columns and what data to use
var gridOptions = {
columnDefs: columnDefs,
rowData: rowData,
};
// lookup the container we want the Grid to use
var eGridDiv = document.querySelector('#myGrid');
// create the grid passing in the div to use together with the columns & data we want to use
new agGrid.Grid(eGridDiv, gridOptions);
</script>
The problems in this sample are:
The row number is defined in the data, which is not supported in real case. Maintaining a row is difficult and slow since inserting from the beginning will need to update many rows.
Since the row number is defined in the data, after sorting you won't see the row number is randomized. I still want to see the row number start from 1 from the beginning.
Basically it's easier to be part of the grid feature, and many other grids (not limited to JS grids) support this. I'm wondering if it's easy to do it in with ag-grid.

Ag-grid now has a "valueGetter" for cells that takes an expression, so you can just use
columnDefs: [
{
headerName: "Row",
valueGetter: "node.rowIndex + 1"
},
(other columns)
]
In order to have it refresh after sorting, you need to call refreshCells:
onSortChanged(e: AgGridEvent) {
e.api.refreshCells();
}
If you are filtering, you would do the same thing on the 'filterChanged' event.

One way that we can use is:
create a custom component that implements "ICellRendererAngularComp" interface
refer to https://www.ag-grid.com/javascript-grid-cell-rendering-components/#example-rendering-using-angular-components
and implements agInit method like below:
import { ICellRendererAngularComp } from 'ag-grid-angular';
import { Component } from '#angular/core';
#Component({
selector: 'app-ag-grid-row-number',
templateUrl: './ag-grid-row-number.component.html',
styleUrls: ['./ag-grid-row-number.component.scss']
})
export class AgGridRowNumberComponent implements ICellRendererAngularComp {
rowNumber: number;
refresh(params: any): boolean {
return true;
}
agInit(params: import('ag-grid-community').ICellRendererParams): void {
this.rowNumber = params.rowIndex + 1;
}
afterGuiAttached?(params?: import('ag-grid-community').IAfterGuiAttachedParams): void {
}
}
In your view:
{{ rowNumber }}
finally in your list component:
public columnDefs = [
{headerName: 'row number', cellRendererFramework: AgGridRowNumberComponent},
... ]

Related

Tabulator custom date filter

On table creation, I am trying to HIDE a row of the table if a date is in the past. Appreciate the help big time! Unfortunately I can't figure out how to do this, and really could use the help. There is also a filter that I apply based on user drop down. I am using tabulator package as well as moment for date library
<script type="text/javascript" src="https://unpkg.com/tabulator-tables#4.9.3/dist/js/tabulator.min.js"></script>
<link href="https://unpkg.com/tabulator-tables#4.9.3/dist/css/materialize/tabulator_materialize.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<select id="filter-value">
<option value="">Select A Course</option>
<option value="">All</option>
<option value="Bronze">Bronze</option>
<option value="FirstAid">First Aid</option>
</select>
<script language="JavaScript">
//Define variables for input elements
var valueEl = document.getElementById("filter-value");
//Trigger setFilter function with correct parameters
function updateFilter() {
valueEl.disabled = false;
table.setFilter("course", "starts", valueEl.value);
// table.setFilter(filter, typeVal, valueEl.value);
// table.setFilter(filter, "like", valueEl.value);
}
//Update filters on value change
//document.getElementById("filter-field").addEventListener("change", updateFilter);
//document.getElementById("filter-type").addEventListener("change", updateFilter);
document.getElementById("filter-value").addEventListener("change", updateFilter);
//define some sample data
var tabledata = [
{ sortorder: "A100001", course: "Bronze Medallion with Emergency First Aid & CPR B Crash Course", price: "12", startdate: "Jan 21 2023" },
{ sortorder: "B300001", course: "Bronze Cross", price: "13", startdate: "May 12 1982", info: "Apr 4-May 2nd,<br /> 10am-4pm", pre: "" },
{ sortorder: "B300001", course: "Bronze Cross", price: "12", startdate: "Apr 12 1983", info: "", pre: "" },
{ sortorder: "C400001", course: "Christine Lobowski", price: "42", startdate: "May 22 1982", info: "", pre: "" },
{ sortorder: "A200001", course: "Bronze Medallion Recertification", price: "125", startdate: "Jan 8 1980", info: "", pre: "" },
{ sortorder: "D500001", course: "Margret Marmajuke", price: "16", startdate: "Jan 31 1999", info: "", pre: "" },
];
//create Tabulator on DOM element with id "example-table" RENDER THE TABLE
var table = new Tabulator("#example-table", {
height: 605, // set height of table (in CSS or here), this enables the Virtual DOM and improves render speed dramatically (can be any valid css height value)
data: tabledata, //assign data to table
layout: "fitDataFill",
groupBy: "course",
resizableColumns: false,
responsiveLayout: "collapse",
responsiveLayoutCollapseStartOpen: false,
columns: [ //Define Table Columns
{ formatter: "responsiveCollapse", width: 30, minWidth: 30, hozAlign: "center", resizable: false, headerSort: false },
{ title: "Sort Order", field: "sortorder" },
{ title: "Course", field: "course", width: 350, responsive: 0, headerSort: false },
{ title: "Price", field: "price", hozAlign: "left", headerSort: false },
{ title: "Start Date", field: "startdate", hozAlign: "center", headerSort: false, sorter: "date", cssClass:"date" },
{ title: "Course Information", field: "info", formatter: "html", headerSort: false },
{ title: "Pre-requisites", field: "pre", headerSort: false },
],
initialSort: [
{ column: "startdate", dir: "asc" }, //sort by this first asc desc
{ column: "sortorder", dir: "asc" }, //sort by this second you will see this on screen
],
initialFilter: [
{ field: "startdate", type: ">", value: "" }
],
});
You're comparing strings. I think any string will be '> ""' (or the opposite, no string will be '> ""' - either way, you're not going to get what you want)
Convert those dates to moment()'s (prolly with a mutator at data insertion might be best) and then use a datetime formatter to display them how you want and you will be able to compare/filter/sort them as numbers properly

actualSize of groupColumnDef in aggrid is wrong

I need to autosize the columns width based on content. All columns resize just fine except for the groupColumnDef, for some reason the actualSize value is a lot smaller than what it should be.
I did not set minWidth/width/maxWidth in the column def.
What could be the issue?
// grid.component.html
<div style="text-align:center">
ag-grid Example
</div>
<ag-grid-angular #agGrid style="width: 100%; height: 500px;" class="ag-theme-balham" [gridOptions]="gridOptions"
(gridReady)="onGridReady($event)">
</ag-grid-angular>
// employee.ts
export interface Employee {
name: string;
age: number;
country: string;
year: number;
date: number;
month: string;
ssn: number;
address: string;
zipCode: number;
occupation: string;
employer: string;
employerAddress: string;
mobNum: number;
}
// grid-component.ts
import { Component, OnInit } from '#angular/core';
import { GridOptions } from 'ag-grid-community';
import { Employee } from './models/employee';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class GridComponent implements OnInit {
gridOptions: GridOptions;
constructor() {}
ngOnInit() {
this.setGridOptions();
}
setGridOptions() {
this.gridOptions = {
suppressColumnVirtualisation: true
} as GridOptions;
}
onGridReady(params) {
this.setData();
}
setData() {
this.gridOptions.api.setColumnDefs(this.getColumnDefinitions());
this.gridOptions.api.setRowData(this.getData());
this.autoSizeAll();
this.gridOptions.api.refreshCells();
}
getColumnDefinitions(): Array<any> {
return [
{
field: 'name',
headerName: 'Name'
},
{
field: 'age',
headerName: 'Age'
},
{
field: 'country',
headerName: 'Country'
},
{
headerName: 'Birth Day',
children: [
{
headerName: 'Year',
field: 'year'
},
{
headerName: 'Month',
field: 'month'
},
{
headerName: 'Date',
field: 'date'
}
]
},
{
field: 'ssn',
headerName: 'Social Security Number'
},
{
field: 'address',
headerName: 'Address'
},
{
field: 'zipCode',
headerName: 'Zip Code'
},
{
headerName: 'Occupation Details',
children: [
{
field: 'occupation',
headerName: 'Occupation'
},
{
field: 'employer',
headerName: 'Employer'
},
{
field: 'employerAddress',
headerName: 'Employer Address'
},
]
},
{
field: 'mobNum',
headerName: 'Mobile Number'
}
];
}
getData(): Employee[] {
return [
{
name: 'Mary Smith',
age: 25,
country: 'Australia',
year: 1990,
date: 14,
month: 'March',
ssn: 1234542792102229,
address: '31 Rainbow Rd, Towers Hill, QLD 4820 31 Rainbow Rd, Towers Hill, QLD 4820',
zipCode: 11350,
occupation: 'Engineer',
employer: 'MicroSoft',
employerAddress: '245 Rainbow Rd, Microsoft, Towers Hill, QLD 4820',
mobNum: 7156662910
},
{
name: 'Jeff Martin',
age: 30,
country: 'UK',
year: 1987,
date: 24,
month: 'December',
ssn: 1234542792102229,
address: '31 Rainbow Rd, Towers Hill, QLD 4820',
zipCode: 11350,
occupation: 'UI/UX Designer',
employer: 'Facebook India',
employerAddress: '17 Rainbow Rd, Towers Hill, QLD 4820',
mobNum: 7158462910
}
];
}
autoSizeAll() {
const allColumnIds = [];
this.gridOptions.columnApi.getAllColumns().forEach((column: any) => {
allColumnIds.push(column.colId);
});
this.gridOptions.columnApi.autoSizeColumns(allColumnIds);
}
}
Here what I have done is
In html - Bind gridOptions and onGridReady event
In Component
Initialize the grid options and set suppressColumnVirtualisation = true.
When the grid is ready set data to the grid by setting the column definitions and then row data.
Get the column ids of all the columns in the grid and set them in grid api autoSizeColumns.
autoSizeColumns() looks only at the cells already rendered on the screen. Therefore the column width is set based on what it sees. This happens due to column virtualization. So the columns not visible on the screen will not auto size.
This behavior can be bypassed simply by setting suppressColumnVirtualisation = true
Note that grid use this column virtualization to improve the
performance of the grid when there are large amount of columns to be
rendered.

AG-Grid - how to access params.api for other grid options before `onGridReady` is triggered?

I'm using React.
I need access to params.api to create the required data for the columnDefs, but it seems the only way to access params.api is via a method passed to onGridReady - this is obviously too late. Is there any way of doing this?
Instead of using gridApi to set ColumnDefs, you should set it with AgGridReact component's columnDefs attribute.
Reference: Add ag-Grid to Your Project
class App extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{headerName: "Make", field: "make"},
{headerName: "Model", field: "model"},
{headerName: "Price", field: "price"}
],
rowData: [
{make: "Toyota", model: "Celica", price: 35000},
{make: "Ford", model: "Mondeo", price: 32000},
{make: "Porsche", model: "Boxter", price: 72000}
]
}
}
render() {
return (
<div
className="ag-theme-balham"
style={{
height: '500px',
width: '600px' }}
>
<AgGridReact
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}>
</AgGridReact>
</div>
);
}
}

Angular 6 - Add new row in AG Grid

I want to add a new element in AG Grid. I have a following model:
export class PersonModel {
cars: CarModel[];
}
The AG Grid has as rowData the array Cars of my model. But this array is not Observable. Now I want to add a new car when I click a button:
<button type="button" (click)="onClickCreateCar()">
And in my viewmodel:
onClickCreateCar() {
var emptyCar = new CarModel();
this.person.cars.push(emptyCar);
}
I can not see the new row in the grid because the array Cars is not observable. It is ok because the property of a model should not be observable. How do you fix the problem?
My AG-Grid definition:
<ag-grid-angular class="ag-theme-fresh" *ngIf="person" style="height: 100%; width: 100%;" [gridOptions]="gridOptions" [rowData]="person.cars" [columnDefs]="columnDefs">
For insert a new row into ag-grid you shouldn't use the rowData directly it will create\override existing object and all states would be reset, and anyway, there is a method for it setRowData(rows)
But I'd recommend to use updateRowData(transaction):
updateRowData(transaction) Update row data into the grid. Pass a transaction object with lists for add, remove and update.
As example:
gridApi.updateRowData({add: newRows});
for angular:
set id for html - selector (#agGrid in this example):
<ag-grid-angular
#agGrid
style="width: 650px; height: 500px;"
class="ag-theme-balham"
[rowData]="rowData"
[columnDefs]="columnDefs"
>
</ag-grid-angular>
and then define the viewchild with this id, import AgGridAngular like shown below, then you can use the ag-grid api in Angular
import {Component, OnInit, ViewChild} from '#angular/core';
import { AgGridAngular } from 'ag-grid-angular';
#Component({
selector: 'app-angular-handsometable',
templateUrl: './angular-handsometable.component.html',
styleUrls: ['./angular-handsometable.component.scss']
})
export class AngularHandsometableComponent implements OnInit {
#ViewChild('agGrid') agGrid: AgGridAngular;
columnDefs = [
{headerName: 'Make', field: 'make', sortable: true, filter: true, editable: true },
{headerName: 'Model', field: 'model', sortable: true, filter: true, editable: true },
{headerName: 'Price', field: 'price', sortable: true, filter: true, editable: true }
];
rowData = [
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 },
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 },
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 },
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 }
];
constructor() { }
ngOnInit() {
}
save() {
console.log( 'Save', this.rowData );
}
addRow() {
this.agGrid.api.updateRowData({
add: [{ make: '', model: '', price: 0 }]
});
}
}
Make sure your rowData array is getting updated with the new object you added and if its getting updated and its just a question of updating the grid view, you can explicitly call the refresh API's of the grid.
https://www.ag-grid.com/javascript-grid-api/#refresh
I visited ag-grid documentation and got rowData is simple array. If ag-grid not refreshing your dom then may be they want a fresh array. You can do like this:
this.person.cars = [this.person.cars.slice(0), yourNewCarObj];

ag-Grid, try to make Tree Demo work using own data

I like the ag-Grid because it's less buggy, fast and works with many frameworks.
So I tried the Tree Data, no need to tell the link between parents and children, simply lay down the data in structure, specify some options, Bingo! But, when I plug in my API, it tells me
"TypeError: rowData is undefined"
from inside of ag-grid.js even though Watch clearly shows it has the array. There are some answered Question here regarding customization with internal api. Mine is not.
I then use the official demo as a base, set up a Fiddler to grab the raw data in JSON replace demo data to make it hardcoded for a test to determine if it's problem with own API or something else. Here is the Plunker. Note it's totally based on the official javaScript Demo of Tree Data, Tree Data Example, the first one.
In case you don't want to see Plunker, here is my .js:
var columnDefs = [
{headerName: "Client", field: "Client", cellRenderer: 'group'},
{headerName: "Program", field: "Program"}
/*{headerName: "Group", field: "xgroup", cellRenderer: 'group'}, // cellRenderer: 'group'}, -- this "group" is one of the default value option for built-in cellRenderer function, not field.
//{headerName: "Athlete", field: "athlete"},
//{headerName: "Year", field: "year"},
{headerName: "Country", field: "country"}
*/
];
var myData = [
{
'Client': 'Goodle',
'Program': 'no grid',
'kids': []
},
{
'Client': 'Facebrook',
'Program': 'grids',
'kids': [
{
'Client': 'Facebrook',
'Program': 'ag-Grid'
},
{
'Client': 'Facebrook',
'Program': 'ui-grid'
}
]
}
/*{xgroup: 'Group A',
participants: [
/*{athlete: 'Michael Phelps', year: '2008', country: 'United States'},
{athlete: 'Michael Phelps', year: '2008', country: 'United States'},
{athlete: 'Michael Phelps', year: '2008', country: 'United States'}*/
/*]},
{xgroup: 'Group B', athlete: 'Sausage', year: 'Spaceman', country: 'Winklepicker',
participants: [
{athlete: 'Natalie Coughlin', year: '2008', country: 'United States'},
{athlete: 'Missy Franklin ', year: '2012', country: 'United States'},
{athlete: 'Ole Einar Qjorndalen', year: '2002', country: 'Norway'},
{athlete: 'Marit Bjorgen', year: '2010', country: 'Norway'},
{athlete: 'Ian Thorpe', year: '2000', country: 'Australia'}
]},
{xgroup: 'Group C',
participants: [
{athlete: 'Janica Kostelic', year: '2002', country: 'Crotia'},
{athlete: 'An Hyeon-Su', year: '2006', country: 'South Korea'}
]}*/
];
var gridOptions = {
columnDefs: columnDefs,
rowData: myData,
rowSelection: "single",
enableSorting: "true", unSortIcon: "true",
enableColResize: "true",
enableRangeSelection: "true",
suppressCellSelection: "false",
showToolPanel: "true",
supressCopyRowsToClipboard: true,
supressCellSelection: false,
getNodeChildDetails: getNodeChildDetails,
onGridReady: function(params) {
params.api.sizeColumnsToFit();
}
};
function getNodeChildDetails(rowItem) {
if (rowItem.Client) {
return {
group: true,
// open C be default
//expanded: rowItem.ClientName === 'Group C',
// provide ag-Grid with the children of this group
children: rowItem.kids,
// this is not used, however it is available to the cellRenderers,
// if you provide a custom cellRenderer, you might use it. it's more
// relavent if you are doing multi levels of groupings, not just one
// as in this example.
//field: 'group',
// the key is used by the default group cellRenderer
key: rowItem.Client
};
} else {
return null;
}
}
function onFilterChanged(value) {
gridOptions.api.setQuickFilter(value);
}
// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function() {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
});
HTML:
<html>
<head>
<!-- you don't need ignore=notused in your code, this is just here to trick the cache -->
<script src="https://ag-grid.com/dist/ag-grid/ag-grid.js"></script>
<script src="script.js"></script>
</head>
<body>
<input placeholder="Filter..." type="text"
onpaste="onFilterChanged(this.value)"
oninput="onFilterChanged(this.value)"
onchange="onFilterChanged(this.value)"
onkeydown="onFilterChanged(this.value)"
onkeyup="onFilterChanged(this.value)"/>
<div id="myGrid" class="ag-fresh" style="height: 450px; margin-top: 4px;"></div>
</body>
</html>
Need some experts!
You need to alter getNodeChildDetails to have this:
function getNodeChildDetails(rowItem) {
if (rowItem.Client && rowItem.kids && rowItem.kids.length > 0) {
As you have it you're telling the grid that any item with Client is a parent node, but what you really mean in your data is any item with Client AND kids is a parent.
Remember that the grid can have multiple levels so a child can be a parent too.