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];
Related
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.
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>
);
}
}
I want to use by default all the datas from rowData, I do not want use specific values. Do you know how to unify columnDefs for both examples?
1st example:
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 }
];
2nd example:
columnDefs = [
{headerName: 'name', field: 'name' },
{headerName: 'phone', field: 'phone' },
];
rowData = [
{ name: 'Manuel', phone: 35000 },
{ name: 'Maria', phone: 32000 },
{ name: 'John', phone: 72000 }
];
I have one idea, but dont exactly know if it is more elegant.
var columnDefs = [
{ headerName: "make", field: "make" },
{ headerName: "model", field: "model" },
{ headerName: "price", field: "price" }
];
var rowData = [
{ make: "Toyota", model: "Celica", price: 35000 },
{ make: "Ford", model: "Mondeo", price: 32000 },
{ make: "Porsche", model: "Boxter", price: 72000 }
];
genColums() {
let columsDef = [];
for (var prop in rowData[0]) {
columsDef.push({
headerName: prop,
field: prop
});
}
console.log(columsDef);
}
this.genColums();
PS. this work, if all data same, in each object
SOLVED: turns out I was importing the incorrect CSS which causes some pretty severe issues, read answer for more details
After implementing ag-grid, data is showing but I am unable to scroll at all.
I made a stackblitz here: https://stackblitz.com/edit/angular-ytr1jj.
The stackblitz works as expected but, for some reason, the exact same code fails to scroll in my app.
I have tried multiple ways of rendering the rows.
I have tried messing with the CSS to see if there is anything causing an overlap in the scrollbar.
so far no luck. I think it might have something to do with "pointer events"...
Update: I think that the virtual scroll requires the mouse event to be captured on the row, and for some reason the mouse event is not being captured and therefore can't scroll
<ag-grid-angular
#agGrid
style="width: 100%; height: 600px;"
class="ag-theme-balham"
[rowData]="rowData"
[columnDefs]="columnDefs"
>
</ag-grid-angular>
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: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 }
];
UPDATE 2: I am able to get the scrollbar to scroll if i set pointer-events: none to .ag-center-cols-viewport. the html seems to be updating on scroll and the row-index row-id etc is updating as it scrolls, however the table view seems to not to not show the current rows being inserted into the ref="ag-center-cols-viewport" (they are appearing in the html but they are not visible)
ANSWER
okay so, this is a bit ridiculous but here it goes
BAD
#import "~ag-grid/dist/styles/ag-grid.css";
#import "~ag-grid/dist/styles/ag-theme-material.css";
GOOD
#import 'ag-grid-community/dist/styles/ag-grid.css';
#import 'ag-grid-community/dist/styles/ag-theme-material.css';
After checking my package.json again I noticed I had 3 things of ag-grid under dependencies
"ag-grid",
"ag-grid-angular",
"ag-grid-community"
The CSS I was importing was from the old version of "ag-grid".
After removing "ag-grid" from package.json I imported the proper CSS and everything worked immediately...
Final Good Code
// package.json
"ag-grid-angular": "^20.0.0",
"ag-grid-community": "^20.0.0",
// _vendor.scss where I import most my node_modules css
// #import "~ag-grid/dist/styles/ag-grid.css";
// #import "~ag-grid/dist/styles/ag-theme-material.css";
#import 'ag-grid-community/dist/styles/ag-grid.css';
#import 'ag-grid-community/dist/styles/ag-theme-material.css';
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},
... ]