![]() |
被表白的围巾 · Cannot read ...· 5 天前 · |
![]() |
老实的橙子 · 在小程序中把"哈哈"字符串转成Uint8Ar ...· 5 月前 · |
![]() |
个性的火柴 · 房屋抵押权与优先购买权并存时的处理-中新网· 6 月前 · |
![]() |
慷慨大方的胡萝卜 · 如何在Unity中删除字符串中的转义字符(符 ...· 6 月前 · |
![]() |
听话的香菜 · location.href和push的区别 ...· 8 月前 · |
![]() |
谦和的灌汤包 · Android开发连接sqlite并通过SQ ...· 9 月前 · |
onclick 按钮 date react |
https://fishedee.com/2022/05/13/AgGrid%E4%BD%BF%E7%94%A8%E7%BB%8F%E9%AA%8C%E6%B1%87%E6%80%BB/ |
![]() |
沉稳的鸭蛋
6 月前 |
这样一个完备的组件,它的API设计也是相当漂亮。如何在一个React的框架中,兼顾性能,同时保持API的灵活性,是一件不太容易的事情。
官网在 这里 ,整体文档与Demo丰富,是一个成熟的商业库。
AgGrid与普通AntdTable最大的不同在于:
从某种意义上说,AgGrid更像是一个传统的命令式UI框架。
AgGrid仅推荐在rowData上使用React的声明式的外部数据源,当向AgGrid传入新的rowData时,它通过以下步骤确定数据是否有更新:
因此,如何写入新数据
声明式和外部数据源的刷新方式,在AgGrid中并不是必要的操作,更多是迁就原来React开发者的开发方式。AgGrid中仅推荐对rowData使用这种方式刷新,尽量不要对colDef使用这种方式刷新。
在现有几乎所有的UI框架中,编辑都是一步操作。例如,我们有上下两个textArea,我们希望上面一个textarea的数据变化的时候,也会产生下面的textArea变化。
const [state,setState] = useState('');
return (
<textarea value={state} onChange={(e)=>{
setState(e.target.value);
<textarea value={state} onChange={(e)=>{
setState(e.target.value);
一个直观的做法是,对两个textArea共有一个外部数据源,当其中一个textarea的onChange事件触发的时候,重新render当前页面。这种做法有两个问题:
每一个键盘操作,都需要重新Render整个页面。当页面上有很多组件的时候,Render的操作就会触发得过于频繁。
展示与输入数据被固定为同一种样式,如果一个表格上每个cell都是可以编辑的,这个表格看上去就会很丑,因为每个单元格都是有边框包围起来的Input组件。另外,在实际应用中,我们希望非编辑状态的数字是3位逗号展示法,而编辑状态的数字是没有逗号的。
在实际应用中,我们更多使用两部编辑操作,仅兼顾了性能,同时更加美观。
每个单元格默认是在展示状态,这个时候可以用3位逗号展示法来展示数据。
当点击单元格的时候,单元格转换为编辑状态。处于编辑状态的单元格,当value发生变化的时候,不会触发其他单元格的关联更新。这样避免了,每次的键盘操作,都需要频繁的整页面Render操作。
当编辑状态的单元格丢失焦点的时候,输入组件的数据自动更新到表格中,并触发其他单元格的关联更新。丢失焦点的时候,才需要进行整页面的Render操作。
如果你仔细留意以上流程就会发现,Excel中的编辑也是相同的两步操作,而不是一步操作。
具体的代码可以看这里的第6节
0.4 付费
企业版需要付费,就其所提供的功能而言,这个价格并不贵,甚至说物超所值。而且,这样的组件功能几乎不会过时,凡是做Web管理系统的都离不开AgGrid的这些功能,这家公司的商业模式其实很棒,属于小而美的方向,唯一的缺陷在于盗版横行。
npm install ag-grid-community --save
npm install ag-grid-enterprise --save
npm install ag-grid-react --save
使用Package模式来安装全部依赖,
Ag-Grid提供了按需Module安装依赖的方式,可以大幅减少包大小,看这里
代码在这里
2.1 基础
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { ColDef, GetRowIdFunc } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: '9.23', date: '2022-01-02' },
{ id: 2, make: "Ford", model: "Mondeo", price: '31.2', date: '2021-01-02' },
{ id: 3, make: "Porsche", model: "Boxter", price: '188.7', date: '2022-03-02' }
const [columnDefs] = useState([
//headerName是名称
{ headerName: '品牌', field: 'make' },
//field是字段名
{ headerName: '型号', field: 'model' },
//定义col的ID,以及类型
{ colId: 'price1', headerName: '价格', field: 'price', type: 'numberColumn' },
//width没有定义,defaultColDef定义为170,dateColumn定义为200,最终值为200
{ headerName: '日期', field: 'date', type: 'dateColumn' },
//width是宽度,这里定义的width为500,不会被defaultColDef的width为170覆盖
{ colId: 'price2', headerName: '价格2', field: 'price', type: 'numberColumn', width: 500 },
//使用groupId与children,创建一个列分组
headerName: 'Medals',
groupId: 'medalsGroup',
children: [
{ colId: 'price3', headerName: '价格3', field: 'price', type: 'numberColumn' },
{ headerName: '型号', field: 'model' },
//hide为隐藏该列
{ headerName: '品牌', field: 'make', hide: true },
//列的属性默认属性,由columnDefs + defaultColDef(和defaultColGroupDef) + columnType合并为最终类型
//合并规则为:
//原columnDefs的值中非undefined的属性不会被覆盖,只有undefined的属性会被覆盖
//defaultColDef的优先级,比columnType的优先级要低
const defaultColDef = useMemo(() => {
return {
width: 170,
//可调宽度
resizable: true,
}, []);
const columnTypes = useMemo(() => {
return {
//定义数字类型
numberColumn: {
headerClass: 'ag-right-aligned-header',
cellClass: 'ag-right-aligned-cell'
//定义日期类型
dateColumn: {
width: 200,
}, []);
const getRowId: GetRowIdFunc = useCallback((props) => {
return props.data.id;
}, []);
const onGridReady = useCallback((params) => {
console.log('grid ready');
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
getRowId={getRowId}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
columnTypes={columnTypes}
onGridReady={onGridReady} />
export default App;
headerName,名称
field,字段名,
colId,列ID。如果colId不存在的话,默认使用field作为colId。
width,宽度
groupId,children,创建一个列分组
列的属性默认属性,由columnDefs +
defaultColDef(和defaultColGroupDef) +
columnType合并为最终类型,合并规则为:
原columnDefs的值中非undefined的属性不会被覆盖,只有undefined的属性会被覆盖
defaultColDef的优先级,比columnType的优先级要低
2.2 宽度
2.2.1 用户可调宽度
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { ColDef, ColumnResizedEvent, GetRowIdFunc } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: '9.23', date: '2022-01-02' },
{ id: 2, make: "Ford", model: "Mondeo", price: '31.2', date: '2021-01-02' },
{ id: 3, make: "Porsche", model: "Boxter", price: '188.7', date: '2022-03-02' }
const [columnDefs] = useState([
//minWidth最小宽度
{ headerName: '品牌', field: 'make', minWidth: 100 },
//maxWidth最大宽度
{ headerName: '型号', field: 'model', maxWidth: 300 },
//width当前宽度
{ colId: 'price1', headerName: '价格', field: 'price', type: 'numberColumn', width: 500 },
{ headerName: '日期', field: 'date', type: 'dateColumn' },
const defaultColDef = useMemo(() => {
return {
width: 170,
//可调宽度
resizable: true,
}, []);
const columnTypes = useMemo(() => {
return {
//定义数字类型
numberColumn: {
headerClass: 'ag-right-aligned-header',
cellClass: 'ag-right-aligned-cell'
//定义日期类型
dateColumn: {
width: 200,
}, []);
const getRowId: GetRowIdFunc = useCallback((props) => {
return props.data.id;
}, []);
const onGridReady = useCallback((params) => {
console.log('grid ready');
}, []);
const onColmnResized = useCallback((param: ColumnResizedEvent) => {
console.log("column resize", param.column);
if (param.finished) {
//只处理那些finish以后的事件
console.log("column resize finish", param.column);
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
getRowId={getRowId}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
columnTypes={columnTypes}
onGridReady={onGridReady}
onColumnResized={onColmnResized} />
export default App;
使用resizable,实现用户自定义宽度
使用onColumnResized,来接收宽度变化事件
可调宽度依然受到minWidth,maxWidth的影响
2.2.2 flex宽度
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { ColDef, ColumnResizedEvent, GetRowIdFunc } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: '9.23', date: '2022-01-02' },
{ id: 2, make: "Ford", model: "Mondeo", price: '31.2', date: '2021-01-02' },
{ id: 3, make: "Porsche", model: "Boxter", price: '188.7', date: '2022-03-02' }
const [columnDefs] = useState([
{ headerName: '品牌', field: 'make', width: 100 },
{ headerName: '型号', field: 'model', width: 200 },
//flex是自动获取当前的剩余宽度,使得刚好填充屏幕宽度
{ colId: 'price1', headerName: '价格', field: 'price', type: 'numberColumn', flex: 1 },
//flex为2,所以日期列的宽度,刚好为价格列宽度的2倍
{ headerName: '日期', field: 'date', type: 'dateColumn', flex: 2 },
const defaultColDef = useMemo(() => {
return {
width: 170,
//可调宽度
resizable: true,
}, []);
const columnTypes = useMemo(() => {
return {
//定义数字类型
numberColumn: {
headerClass: 'ag-right-aligned-header',
cellClass: 'ag-right-aligned-cell'
//定义日期类型
dateColumn: {
width: 200,
}, []);
const getRowId: GetRowIdFunc = useCallback((props) => {
return props.data.id;
}, []);
const onGridReady = useCallback((params) => {
console.log('grid ready');
}, []);
const onColmnResized = useCallback((param: ColumnResizedEvent) => {
console.log("column resize", param.column);
if (param.finished) {
//只处理那些finish以后的事件
console.log("column resize finish", param.column);
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
getRowId={getRowId}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
columnTypes={columnTypes}
onGridReady={onGridReady}
onColumnResized={onColmnResized} />
export default App;
flex是自动获取当前的剩余宽度,使得刚好填充屏幕宽度,和flex布局里面的flexGrow参数很相似,没啥好说的
2.2.3 自动宽度
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<React.CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete', width: 150, suppressSizeToFit: true },
field: 'age',
headerName: 'Age of Athlete',
width: 90,
minWidth: 50,
maxWidth: 150,
{ field: 'country', width: 120 },
{ field: 'year', width: 90 },
{ field: 'date', width: 110 },
{ field: 'sport', width: 110 },
{ field: 'gold', width: 100 },
{ field: 'silver', width: 100 },
{ field: 'bronze', width: 100 },
{ field: 'total', width: 100 },
const defaultColDef = useMemo<ColDef>(() => {
return {
resizable: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const sizeToFit = useCallback(() => {
//根据屏幕宽度,调整列宽,刚好为屏幕宽度
gridRef.current!.api.sizeColumnsToFit();
}, []);
const autoSizeAll = useCallback((skipHeader: boolean) => {
//根据数据内容长度,调整列宽,使得每列的内容都能显示得到,skipHeader为true代表不考虑列头名称的宽度
const allColumnIds: string[] = [];
gridRef.current!.columnApi.getAllColumns()!.forEach((column) => {
allColumnIds.push(column.getId());
gridRef.current!.columnApi.autoSizeColumns(allColumnIds, skipHeader);
}, []);
return (
<div style={containerStyle}>
<div className="button-bar">
<button onClick={sizeToFit}>Size to Fit</button>
<button onClick={() => autoSizeAll(false)}>Auto-Size All</button>
<button onClick={() => autoSizeAll(true)}>
Auto-Size All (Skip Header)
</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
AgGrid提供了自动调整宽度的两种方式:
sizeColumnsToFit,根据屏幕宽度,调整列宽,刚好为屏幕宽度
autoSizeColumns,根据数据内容长度,调整列宽,使得每列的内容都能显示得到,skipHeader为true代表不考虑列头名称的宽度
2.3 移动
2.3.1 用户可移动
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { ColDef, ColumnMovedEvent, ColumnResizedEvent, GetRowIdFunc } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: '9.23', date: '2022-01-02' },
{ id: 2, make: "Ford", model: "Mondeo", price: '31.2', date: '2021-01-02' },
{ id: 3, make: "Porsche", model: "Boxter", price: '188.7', date: '2022-03-02' }
const [columnDefs] = useState([
{ headerName: '品牌', field: 'make', minWidth: 100 },
headerName: '型号', field: 'model', maxWidth: 300,
//禁止该列拖着移动,但是允许被其他列推动着移动
suppressMovable: true
colId: 'price1', headerName: '价格', field: 'price', type: 'numberColumn', width: 500,
//禁止该列拖到Grid外面隐藏
lockVisible: true,
{ headerName: '日期', field: 'date', type: 'dateColumn' },
const defaultColDef = useMemo(() => {
return {
width: 170,
//默认就支持
}, []);
const columnTypes = useMemo(() => {
return {
//定义数字类型
numberColumn: {
headerClass: 'ag-right-aligned-header',
cellClass: 'ag-right-aligned-cell'
//定义日期类型
dateColumn: {
width: 200,
}, []);
const getRowId: GetRowIdFunc = useCallback((props) => {
return props.data.id;
}, []);
const onGridReady = useCallback((params) => {
console.log('grid ready');
}, []);
const onColmnMoved = useCallback((param: ColumnMovedEvent) => {
console.log("column move", param);
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '80%', marginTop: '10%', width: '100%' }}>
<AgGridReact
getRowId={getRowId}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
columnTypes={columnTypes}
onGridReady={onGridReady}
onColumnMoved={onColmnMoved}
//默认情况下,移动列有动画效果
//suppressColumnMoveAnimation={false}
//默认情况下,就支持拖动列来移动列位置,禁用的话需要使用suppressMovableColumns
//suppressMovableColumns={true}
//默认情况下,就支持将列拖出去屏幕以外来消失列,禁用的话需要使用suppressDragLeaveHidesColumns
//suppressDragLeaveHidesColumns={true}
export default App;
默认情况下,AgGrid允许用户自己移动列的顺序,以及将列移出Grid以外(隐藏该列)
suppressMovable,指定禁止该列拖着移动,但是允许被其他列推动着移动
lockVisible,指定禁止该列拖到Grid外面隐藏
suppressMovableColumns,全局禁止所有列移动
suppressDragLeaveHidesColumns,全局禁止所有列移动隐藏
suppressColumnMoveAnimation,全局禁止列移动的动画效果
2.3.2 锁紧列排序
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
const GridExample = () => {
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', dispaly: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete',
width: 200,
//该列总是在最左边
field: 'age', lockPosition: 'left', cellClass: 'locked-col',
width: 500
{ field: 'country', width: 500 },
{ field: 'year', width: 500 },
field: 'total',
//该列总是在最右边
lockPosition: 'right', cellClass: 'locked-col', width: 500
const defaultColDef = useMemo<ColDef>(() => {
return {
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
suppressDragLeaveHidesColumns={true}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
lockPosition,可以设置该列总是在最左侧,或者最右侧。注意,lock依然会被水平滚动条影响,消失到grid以外
2.4 固定
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
ColumnPinnedEvent,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
const GridExample = () => {
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', dispaly: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete',
width: 200,
//该列总是在最左边,固定左侧,有自己独立的滚动条
field: 'age', pinned: 'left', cellClass: 'locked-col',
width: 500
{ field: 'country', width: 500 },
{ field: 'year', width: 500 },
field: 'total',
//该列总是在最右边,固定右侧,有自己独立的滚动条
pinned: 'right', cellClass: 'locked-col', width: 500
const defaultColDef = useMemo<ColDef>(() => {
return {
//默认允许通过拖动方式,将部分列pinned到左侧或者右侧,使用lockPinned以后能禁止这种操作
//lockPinned:true
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onColumnPinned = useCallback((params: ColumnPinnedEvent) => {
console.log('column pinned', params);
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
suppressDragLeaveHidesColumns={true}
onGridReady={onGridReady}
onColumnPinned={onColumnPinned}
></AgGridReact>
export default GridExample;
pinned,与locked不同,它是固定在grid的左侧或者右侧,不受水平滚动条影响,不会移出到grid以外
AgGrid默认允许用户通过拖动的方式,将部分列动态固定在左侧,或者固定在右侧。
lockPinned,指定禁止该列通过拖动的方式固定
onColumnPinned,用户手动拖动导致固定列的事件
2.5 合并
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete', pinned: 'left' },
{ field: 'age', pinned: 'left' },
field: 'country',
//colSpan是一个函数,根据不同行,决定合并多少列
colSpan: function (params) {
const country = params.data.country;
if (country === 'Russia') {
// have all Russia age columns width 2
return 2;
} else if (country === 'United States') {
// have all United States column width 4
return 4;
} else {
// all other rows should be just normal
return 1;
{ field: 'year' },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 150,
resizable: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
使用colSpan就能实现,根据每一行数据,动态地合并多列
2.6 更新
2.6.1 全列更新
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
import { Button } from 'antd';
const GridExample = () => {
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<(ColDef | ColGroupDef)[]>([
// using default ColDef
{ field: 'athlete' },
{ field: 'sport' },
// using number column type
{ field: 'age', },
{ field: 'year', },
// using date and non-editable column types
{ field: 'date', width: 220 },
{ headerName: 'Gold', field: 'gold' },
{ headerName: 'Silver', field: 'silver' },
{ headerName: 'Bronze', field: 'bronze' },
headerName: 'Total',
field: 'total',
columnGroupShow: 'closed',
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 150,
resizable: true,
}, []);
const defaultColGroupDef = useMemo<Partial<ColGroupDef>>(() => {
return {
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const toggleSport = () => {
console.log(columnDefs);
let sportIndex = columnDefs.findIndex((single: any) => {
return single.field == 'sport';
let newColumnDefs: any;
if (sportIndex != -1) {
newColumnDefs = columnDefs.filter((single: any) => {
return single.field != 'sport';
} else {
newColumnDefs = [
...columnDefs,
{ field: 'sport' },
//将本地的ColumnDef获取了,然后用React的方式设置进去,这个方法也是可以的
setColumnDefs(newColumnDefs);
//这种方法不好的地方在于,当以UI方式进行Table分组、筛选或者调整顺序以后,AgGrid并没有对本地的ColumnDef进行修改。
//也就是说,setColumnDefs总是拿着陈旧的数据在修改
//而且,使用filter的方式直接删除列,会丢失原来列的状态信息,例如是原来的width信息
const toggleSport2 = () => {
//getColumnDefs是列信息里面经过最终合并后的数据,它反应的是最新的列信息数据
const oldColumnDefs = gridRef.current!.api.getColumnDefs()!;
console.log('columnDefs ', oldColumnDefs);
let newColumnDefs = oldColumnDefs.map((single: any) => {
if (single.field == 'sport') {
return {
...single,
hide: !single.hide,
} else {
return single;
* columnDefs的数据
* aggFunc: null
* colId: "athlete"
* editable: true
* field: "athlete"
* filter: "agTextColumnFilter"
* floatingFilter: true
* hide: undefined
* pinned: null
* pivot: false
* pivotIndex: null
* resizable: true
* rowGroup: false
* rowGroupIndex: null
* sort: null
* sortIndex: null
* sortable: true
* width: 150
//以命令的方式写入列信息,使用hide的方式,不会丢失原来列的状态信息
//update的比较方式,
gridRef.current!.api.setColumnDefs(newColumnDefs);
const toggleSport3 = () => {
//columnState比columnDefs少的内容有:
//editable,sortable,filter,floatFilter等等
/*columnState数据
* aggFunc: null
* colId: "athlete"
* flex: null
* hide: false
* pinned: null
* pivot: false
* pivotIndex: null
* rowGroup: false
* rowGroupIndex: null
* sort: null
* sortIndex: null
* width: 150
const savedState = gridRef.current!.columnApi.getColumnState();
console.log('columnState ', savedState);
savedState.forEach(single => {
if (single.colId == 'sport') {
single.hide = !single.hide;
gridRef.current!.columnApi.applyColumnState({
state: savedState
const gridRef = useRef<AgGridReact>(null);
return (
<div style={containerStyle}>
<Button onClick={toggleSport}>{'Toggle Sport列'}</Button>
<Button onClick={toggleSport2}>{'Toggle Sport列2'}</Button>
<Button onClick={toggleSport3}>{'Toggle Sport列3'}</Button>
<div style={{ flex: '1', boxSizing: 'border-box' }}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
defaultColGroupDef={defaultColGroupDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
尽量不要使用声明方式更新列,而是要用命令方式更新列,这样性能更好,也能获取到最新的列信息,不会丢掉列状态信息
setColumnDefs,需要传入全部列信息。搭配着getColumnDefs使用。
applyColumnState,可以只更新局部列信息,但是状态数量更少。搭配着getColumnState使用。
2.6.2 部分列更新
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
SideBarDef,
} from 'ag-grid-community';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete' },
{ field: 'age' },
{ field: 'country' },
{ field: 'sport' },
{ field: 'year' },
{ field: 'date' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const defaultColDef = useMemo<ColDef>(() => {
return {
sortable: true,
resizable: true,
width: 150,
enableRowGroup: true,
enablePivot: true,
enableValue: true,
}, []);
const sideBar = useMemo<
SideBarDef | string | string[] | boolean | null
>(() => {
return {
toolPanels: ['columns'],
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onBtSortAthlete = useCallback(() => {
//setColumnDefs,每次都要传入所有列,没有传入的列看成被删掉了
//applyColumnState,可以只传入一个列,没有传入的列看成保持不变
/*columnState数据
* aggFunc: null
* colId: "athlete"
* flex: null
* hide: false
* pinned: null
* pivot: false
* pivotIndex: null
* rowGroup: false
* rowGroupIndex: null
* sort: null
* sortIndex: null
* width: 150
//只对athlete列,设置为升序排列
gridRef.current!.columnApi.applyColumnState({
state: [{ colId: 'athlete', sort: 'asc' }],
}, []);
const onBtSortCountryThenSportClearOthers = useCallback(() => {
//对country,sport组合为升序排列
//defaultState用来设置其他列,表示为其他列的排序去掉
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'country', sort: 'asc', sortIndex: 0 },
{ colId: 'sport', sort: 'asc', sortIndex: 1 },
defaultState: { sort: null },
}, []);
const onBtClearAllSorting = useCallback(() => {
//所有列的排序去掉
gridRef.current!.columnApi.applyColumnState({
defaultState: { sort: null },
}, []);
const onBtRowGroupCountryThenSport = useCallback(() => {
//对country,sport列组合为分组
//将其他列设置为不分组
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'country', rowGroupIndex: 0 },
{ colId: 'sport', rowGroupIndex: 1 },
defaultState: { rowGroup: false },
}, []);
const onBtRemoveCountryRowGroup = useCallback(() => {
//对country列的分组去掉
gridRef.current!.columnApi.applyColumnState({
state: [{ colId: 'country', rowGroup: false }],
}, []);
const onBtClearAllRowGroups = useCallback(() => {
//对所有列的分组去掉
gridRef.current!.columnApi.applyColumnState({
defaultState: { rowGroup: false },
}, []);
const onBtOrderColsMedalsFirst = useCallback(() => {
//applyColumnState默认传入的列,不对顺序进行操作
//对grid的列顺序要与state顺序一致的时候,需要指定applyOrder
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'gold' },
{ colId: 'silver' },
{ colId: 'bronze' },
{ colId: 'total' },
{ colId: 'athlete' },
{ colId: 'age' },
{ colId: 'country' },
{ colId: 'sport' },
{ colId: 'year' },
{ colId: 'date' },
applyOrder: true,
}, []);
const onBtOrderColsMedalsLast = useCallback(() => {
//另外一个列排序
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'athlete' },
{ colId: 'age' },
{ colId: 'country' },
{ colId: 'sport' },
{ colId: 'year' },
{ colId: 'date' },
{ colId: 'gold' },
{ colId: 'silver' },
{ colId: 'bronze' },
{ colId: 'total' },
applyOrder: true,
}, []);
const onBtHideMedals = useCallback(() => {
//设置hide属性
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'gold', hide: true },
{ colId: 'silver', hide: true },
{ colId: 'bronze', hide: true },
{ colId: 'total', hide: true },
}, []);
const onBtShowMedals = useCallback(() => {
//设置hide属性
gridRef.current!.columnApi.applyColumnState({
state: [
{ colId: 'gold', hide: false },
{ colId: 'silver', hide: false },
{ colId: 'bronze', hide: false },
{ colId: 'total', hide: false },
}, []);
return (
<div style={containerStyle}>
<div className="test-header">
<table>
<tbody>
<td>Sort:</td>
<button onClick={onBtSortAthlete}>Sort Athlete</button>
<button onClick={onBtSortCountryThenSportClearOthers}>
Sort Country, then Sport - Clear Others
</button>
<button onClick={onBtClearAllSorting}>
Clear All Sorting
</button>
<td>Column Order:</td>
<button onClick={onBtOrderColsMedalsFirst}>
Show Medals First
</button>
<button onClick={onBtOrderColsMedalsLast}>
Show Medals Last
</button>
<td>Column Visibility:</td>
<button onClick={onBtHideMedals}>Hide Medals</button>
<button onClick={onBtShowMedals}>Show Medals</button>
<td>Row Group:</td>
<button onClick={onBtRowGroupCountryThenSport}>
Group Country then Sport
</button>
<button onClick={onBtRemoveCountryRowGroup}>
Remove Country
</button>
<button onClick={onBtClearAllRowGroups}>
Clear All Groups
</button>
</tbody>
</table>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
sideBar={sideBar}
rowGroupPanelShow={'always'}
pivotPanelShow={'always'}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
使用applyColumnState的关键点在:
使用state传入局部列的状态信息,这个可以不传
使用defaultState来指定其他列的状态信息,这个可以不传
使用applyOrder,来确定是否同步列顺序
2.6.3 列顺序无关更新
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
function getColumnDefsA() {
return [
{ field: 'athlete', headerName: 'A Athlete' },
{ field: 'age', headerName: 'A Age' },
{ field: 'country', headerName: 'A Country' },
{ field: 'sport', headerName: 'A Sport' },
{ field: 'year', headerName: 'A Year' },
{ field: 'date', headerName: 'A Date' },
{ field: 'gold', headerName: 'A Gold' },
{ field: 'silver', headerName: 'A Silver' },
{ field: 'bronze', headerName: 'A Bronze' },
{ field: 'total', headerName: 'A Total' },
function getColumnDefsB() {
return [
{ field: 'gold', headerName: 'B Gold' },
{ field: 'silver', headerName: 'B Silver' },
{ field: 'bronze', headerName: 'B Bronze' },
{ field: 'total', headerName: 'B Total' },
{ field: 'athlete', headerName: 'B Athlete' },
{ field: 'age', headerName: 'B Age' },
{ field: 'country', headerName: 'B Country' },
{ field: 'sport', headerName: 'B Sport' },
{ field: 'year', headerName: 'B Year' },
{ field: 'date', headerName: 'B Date' },
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const defaultColDef = useMemo<ColDef>(() => {
return {
initialWidth: 100,
sortable: true,
resizable: true,
filter: true,
}, []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>(getColumnDefsA());
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const setColsA = useCallback(() => {
gridRef.current!.api.setColumnDefs(getColumnDefsA());
}, []);
const setColsB = useCallback(() => {
gridRef.current!.api.setColumnDefs(getColumnDefsB());
}, []);
const clearColDefs = useCallback(() => {
gridRef.current!.api.setColumnDefs([]);
}, []);
return (
<div style={containerStyle}>
<div className="test-header">
<button onClick={setColsA}>Column Set A</button>
<button onClick={setColsB}>Column Set B</button>
<button onClick={clearColDefs}>Clear</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
defaultColDef={defaultColDef}
//默认情况下,setColumnDefs不仅会考虑列状态本身,还会考虑列的顺序
//如果,我们在调用setColumnDefs告知ag-grid,只需要考虑列状态,不需要列顺序,那么就打开maintainColumnOrder的开关就可以了
//maintainColumnOrder={true}
columnDefs={columnDefs}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
默认情况下,setColumnDefs不仅会考虑列状态本身,还会考虑列的顺序。如果,我们在调用setColumnDefs告知ag-grid,只需要考虑列状态,不需要考虑列顺序,那么就打开maintainColumnOrder的开关就可以了
2.6.4 列匹配与合并原则
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
ValueGetterParams,
} from 'ag-grid-community';
const athleteColumn = {
headerName: 'Athlete',
valueGetter: function (params: ValueGetterParams) {
return params.data.athlete;
function getColDefsMedalsIncluded() {
return [
//3.没有colId,也没有field的时候,使用object引用来标识这个列
athleteColumn,
//1,优先使用colId来标识这个列
colId: 'myAgeCol',
headerName: 'Age',
valueGetter: function (params: ValueGetterParams) {
return params.data.age;
//4,都不匹配的时候,ag-grid会认为这个是一个新的列,所以每次都会进行刷新
headerName: 'Country',
headerClass: 'country-header',
valueGetter: function (params: ValueGetterParams) {
return params.data.country;
//2,没有colId的时候,使用field来标识这个列
{ field: 'sport' },
{ field: 'year' },
{ field: 'date' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
function getColDefsMedalsExcluded() {
return [
athleteColumn,
colId: 'myAgeCol',
headerName: 'Age',
valueGetter: function (params: ValueGetterParams) {
return params.data.age;
headerName: 'Country',
headerClass: 'country-header',
valueGetter: function (params: ValueGetterParams) {
return params.data.country;
{ field: 'sport' },
{ field: 'year' },
{ field: 'date' },
//标识了这个列以后,ag-grid对属性的合并规则是:
//如果新属性的值为undefined,那么原来的属性值就保留,
//如果新属性的值为null,那么原来的属性值就要清除,例如清除sort,清除group等等
//如果新属性的值为具体值(非undefined且非null),那么原来的属性值就要被覆盖。
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const defaultColDef = useMemo<ColDef>(() => {
return {
initialWidth: 100,
sortable: true,
resizable: true,
}, []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>(
getColDefsMedalsIncluded()
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onBtExcludeMedalColumns = useCallback(() => {
gridRef.current!.api.setColumnDefs(getColDefsMedalsExcluded());
}, []);
const onBtIncludeMedalColumns = useCallback(() => {
gridRef.current!.api.setColumnDefs(getColDefsMedalsIncluded());
}, []);
return (
<div style={containerStyle}>
<div className="test-header">
<button onClick={onBtIncludeMedalColumns}>
Include Medal Columns
</button>
<button onClick={onBtExcludeMedalColumns}>
Exclude Medal Columns
</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
defaultColDef={defaultColDef}
columnDefs={columnDefs}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
列匹配的原则是:
1,优先使用colId来标识这个列
2,没有colId的时候,使用field来标识这个列
3,没有colId,也没有field的时候,使用object引用来标识这个列
4,都不匹配的时候,ag-grid会认为这个是一个新的列,所以每次都会进行刷新
列合并值得原则是:
如果新属性的值为undefined,那么原来的属性值就保留不变,
如果新属性的值为null,那么原来的属性值就要清除,例如清除sort,清除group等等
如果新属性的值为具体值(非undefined且非null),那么原来的属性值就要被覆盖。
3 筛选,排序,分组,支点
代码在这里
3.1 筛选
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { Button } from 'antd';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import moment from 'moment';
import { ColDef, FilterChangedEvent, ValueGetterFunc, ValueGetterParams } from 'ag-grid-community';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete',
filter: 'agTextColumnFilter',
colId: 'age2',
field: 'age',
maxWidth: 100,
filter: 'agNumberColumnFilter',
field: 'country',
filter: 'agTextColumnFilter',
//显示为country2,filter的数据为age
colId: 'country2',
field: 'country(以age来刷新)',
filter: 'agNumberColumnFilter',
filterValueGetter: (params: ValueGetterParams) => {
return params.data.age;
field: 'date',
filter: 'agNumberColumnFilter',
maxWidth: 100,
field: 'year',
filter: 'agDateColumnFilter',
filterParams: {
//agDateColumnFilter需要一个comparator配置,因为要对输入数据与内容数据进行比较
comparator: function (filterLocalDateAtMidnight: Date, cellValue: string) {
let left = moment(filterLocalDateAtMidnight);
let right = moment(cellValue, 'YYYY');
let result = left.year() - right.year();
if (result < 0) {
return 1;
} else if (result > 0) {
return -1;
} else {
return 0;
//默认inRange不包含两个边界点,需要指定这个选项
inRangeInclusive: true,
width: 100,
{ field: 'sport' },
{ field: 'gold', filter: 'agNumberColumnFilter' },
{ field: 'silver', filter: 'agNumberColumnFilter' },
{ field: 'bronze', filter: 'agNumberColumnFilter' },
{ field: 'total', filter: 'agNumberColumnFilter' },
const defaultColDef = useMemo(() => {
return {
minWidth: 150,
//所有列均可筛选
filter: true,
//要一个固定行,输入filter数据
floatingFilter: true,
}, []);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
const clearFilter = () => {
gridRef.current!.api.setFilterModel(null);
const getFilter = () => {
const filterModel = gridRef.current!.api.getFilterModel();
filterModel是object类型,key为colId,不是field,value为filter配置
搜索age为19的rows
"age2": {
"filterType": "number",
"type": "equals",
"filter": 19
搜索age为19岁和2岁的rows
"age2": {
"filterType": "number",
"operator": "AND",
"condition1": {
"filterType": "number",
"type": "equals",
"filter": 19
"condition2": {
"filterType": "number",
"type": "equals",
"filter": 2
搜索age为19至80的rows
"age2": {
"filterType": "number",
"type": "inRange",
"filter": 19,
"filterTo": 80
搜索age为19,country包含United的rows
"age2": {
"filterType": "number",
"type": "equals",
"filter": 19
"country": {
"filterType": "text",
"type": "contains",
"filter": "United"
console.log('filterModel', filterModel);
const setFilter = useCallback(() => {
gridRef.current!.api.setFilterModel({
"age2": {
"filterType": "number",
"type": "equals",
"filter": 19
}, []);
const addRows = useCallback(() => {
let rows = [];
gridRef.current!.api.forEachNode(single => {
rows.push(single.data);
rows.push({
athlete: 'FishGold',
age: 19,
country: new Date().toLocaleString(),
gridRef.current!.api.setRowData(rows);
}, []);
const onFilterChanged = useCallback((params: FilterChangedEvent) => {
console.log('filter Changed', params);
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh' }}>
<Button onClick={clearFilter}>{'清除筛选'}</Button>
<Button onClick={getFilter}>{'获取筛选数据'}</Button>
<Button onClick={setFilter}>{'剔除其他条件,只搜索19岁的群众'}</Button>
<Button onClick={addRows}>{'添加19岁的数据'}</Button>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
onFilterChanged={onFilterChanged}
></AgGridReact>
export default GridExample;
要点如下:
filter,指定filter的方式,默认为agTextColumnFilter。可以为字符串,也可以为一个简单的true
filterParams,agDateColumnFilter需要一个comparator配置,因为要对输入数据与内容数据进行比较。inRangeInclusive,默认inRange不包含两个边界点,需要指定这个选项
floatingFilter,展示一个固定行,输入filter数据
filterModel,是一个string,any的结果,其中,key为colId,不是field
filterValueGetter,可以自定义filter的来源数据与valueGetter的数据是不同的
API为:
getFilterModel,获取当前的filter配置
setFilterModel,设置当前的filter配置
onFilterChanged,filter变化时触发的事件
3.2 排序
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import {
ColDef,
ColumnRowGroupChangedEvent,
SortChangedEvent,
} from 'ag-grid-community';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
//对athlete asc,country asc的方式进行排序,sortIndex是排序的顺序
{ field: 'country', sortable: true, sort: 'asc', sortIndex: 1, hide: false },
{ field: 'athlete', sortable: true, sort: 'asc', sortIndex: 0, hide: false },
{ field: 'year' },
//分组后的合并函数
{ field: 'gold', aggFunc: 'sum' },
{ field: 'silver', aggFunc: 'sum' },
{ field: 'bronze', aggFunc: 'sum' },
{ field: 'total', aggFunc: 'sum' },
{ field: 'age' },
{ field: 'date' },
//sortable就是是否允许在UI中自定义排序
{ field: 'sport', sortable: true, },
const defaultColDef = useMemo(() => {
return {
flex: 1,
minWidth: 150,
resizable: true,
//所有列均可排序
sortable: true,
//没有排序的列,也要显示可以排序的图标
unSortIcon: true,
}, []);
const autoGroupColumnDef = useMemo(() => {
return {
headerName: 'MyGroup',
minWidth: 300,
}, []);
const gridRef = useRef<AgGridReact>(null);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
const clearSort = () => {
gridRef.current!.columnApi.applyColumnState({
defaultState: {
sort: null,
const getSort = () => {
const columnState = gridRef.current!.columnApi.getColumnState();
console.log('columnState', columnState);
const onSortChanged = useCallback((params: SortChangedEvent) => {
console.log('SortChangedEvent', params);
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100vh' }}>
<Button onClick={clearSort}>{'清除排序'}</Button>
<Button onClick={getSort}>{'获取排序'}</Button>
<div className="ag-theme-alpine" style={{ width: '100%', flex: '1' }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onSortChanged={onSortChanged}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
要点如下:
分组其实与排序很相似,只不过是将sort换成了rowGroup,sortIndex换成了rowGroupIndex。另外,AgGrid支持用户自定义分组,所以有enableRowGroup的配置,是否允许用户通过拖放列来对列进行分组。
Pivot,支点模式,是一个相当有用的分析工具,用来将行转为列。
API有:
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useRef, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { GetRowIdParams } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: 35000 },
{ id: 2, make: "Ford", model: "Mondeo", price: 32000 },
{ id: 3, make: "Porsche", model: "Boxter", price: 72000 }
const [columnDefs] = useState([
{ field: 'make' },
{ field: 'model' },
{ field: 'price' }
const defaultColDef = useMemo(() => {
return {
sortable: true,
}, []);
const gridRef = useRef<AgGridReact>(null);
const add = () => {
let maxId = -1;
rowData.forEach(single => {
if (maxId <= single.id) {
maxId = single.id;
let newRowData = [
...rowData,
id: maxId + 1,
make: "M" + maxId,
model: "C" + maxId,
price: maxId,
setRowData(newRowData);
let del = () => {
let selectedRows = gridRef.current!.api.getSelectedNodes();
let selectionRowIds = selectedRows.map(single => {
return single.data.id;
let newRowData = rowData.filter((single) => {
return selectionRowIds.indexOf(single.id) < 0;
console.log(rowData, selectionRowIds, newRowData);
setRowData(newRowData);
//需要指定getRowId,才能启用animateRows,而且在重新刷新数据的时候更少地触发render
const getRowId = useCallback((props: GetRowIdParams) => {
return props.data.id;
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100vh' }}>
<Button onClick={add}>{'添加一行'}</Button>
<Button onClick={del}>{'删除一行'}</Button>
<div className="ag-theme-alpine" style={{ width: '100%', flex: '1' }}>
<AgGridReact
ref={gridRef}
getRowId={getRowId}
rowData={rowData}
defaultColDef={defaultColDef}
rowSelection={'multiple'}
columnDefs={columnDefs}
animateRows={true}>
</AgGridReact>
export default App;
要点如下:
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
RowHeightParams,
} from 'ag-grid-community';
var swimmingHeight: number;
var groupHeight: number;
var russiaHeight: number;
function getData() {
return [
athlete: 'Ryan Lochte',
age: 27,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 1,
total: 5,
athlete: 'Yekaterina Lobaznyuk',
age: 17,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Gymnastics',
gold: 0,
silver: 2,
bronze: 1,
total: 3,
athlete: 'Ryan Lochte',
age: 20,
country: 'United States',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 1,
silver: 1,
bronze: 0,
total: 2,
athlete: 'Ericka Lorenz',
age: 23,
country: 'United States',
year: 2004,
date: '29/08/2004',
sport: 'Waterpolo',
gold: 0,
silver: 0,
bronze: 1,
total: 1,
athlete: 'Ericka Lorenz',
age: 19,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Waterpolo',
gold: 0,
silver: 1,
bronze: 0,
total: 1,
athlete: 'Nikita Lobintsev',
age: 23,
country: 'Russia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 0,
silver: 0,
bronze: 1,
total: 1,
athlete: 'Tatyana Logunova',
age: 24,
country: 'Russia',
year: 2004,
date: '29/08/2004',
sport: 'Fencing',
gold: 1,
silver: 0,
bronze: 0,
total: 1,
athlete: 'Tatyana Logunova',
age: 20,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Fencing',
gold: 1,
silver: 0,
bronze: 0,
total: 1,
athlete: 'Nelson Loyola',
age: 32,
country: 'Cuba',
year: 2000,
date: '01/10/2000',
sport: 'Fencing',
gold: 0,
silver: 0,
bronze: 1,
total: 1,
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'country', rowGroup: true },
{ field: 'athlete' },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const setSwimmingHeight = useCallback((height: number) => {
//修改了高度以后,需要使用restRowHeights,能触发ag-grid调用getRowHeight来更新高度
swimmingHeight = height;
gridRef.current!.api.resetRowHeights();
}, []);
const setGroupHeight = useCallback((height: number) => {
groupHeight = height;
gridRef.current!.api.resetRowHeights();
}, []);
const setRussiaHeight = useCallback((height: number) => {
// 对rowNode直接调用setRowHeight,并不能触发更新高度
//1.要么是用resetRowHeights,触发的是getRowHeight的回调
//2.要么是用onRowHeightChanged,手动批量通知ag-grid,它的rowNode中的rowHeight变更了
russiaHeight = height;
gridRef.current!.api.forEachNode(function (rowNode) {
//使用rowNode的触发是一次性的,当下一次的getRowHeight的数据还是旧数据的话,依然会用旧数据的高度
if (rowNode.data && rowNode.data.country === 'Russia') {
rowNode.setRowHeight(height);
gridRef.current!.api.onRowHeightChanged();
}, []);
//getRowHeight是一个回调,可以用来指定每个group,或者每一个行的高度
const getRowHeight = useCallback(
(params: RowHeightParams): number | undefined | null => {
if (params.node.group && groupHeight != null) {
//指定group的高度
return groupHeight;
} else if (
params.data &&
params.data.country === 'Russia' &&
russiaHeight != null
//指定data为Russia的高度
return russiaHeight;
} else if (
params.data &&
params.data.sport === 'Swimming' &&
swimmingHeight != null
//指定data为Swimming的高度
return swimmingHeight;
[groupHeight, russiaHeight, swimmingHeight]
return (
<div style={containerStyle}>
style={{
marginBottom: '5px',
fontFamily: 'Verdana, Geneva, Tahoma, sans-serif',
fontSize: '13px',
Top Level Groups:
<button onClick={() => setGroupHeight(42)}>42px</button>
<button onClick={() => setGroupHeight(75)}>75px</button>
<button onClick={() => setGroupHeight(125)}>125px</button>
<div style={{ marginTop: '5px' }}>
Swimming Leaf Rows:
<button onClick={() => setSwimmingHeight(42)}>42px</button>
<button onClick={() => setSwimmingHeight(75)}>75px</button>
<button onClick={() => setSwimmingHeight(125)}>125px</button>
<div style={{ marginTop: '5px' }}>
Russia Leaf Rows:
<button onClick={() => setRussiaHeight(42)}>42px</button>
<button onClick={() => setRussiaHeight(75)}>75px</button>
<button onClick={() => setRussiaHeight(125)}>125px</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
animateRows={true}
groupDefaultExpanded={1}
getRowHeight={getRowHeight}
></AgGridReact>
export default GridExample;
要配置每个行的不同高度有两种方式,
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
SideBarDef,
} from 'ag-grid-community';
function getData() {
var latinSentence =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu.';
var latinWords = latinSentence.split(' ');
var rowData = [];
function generateRandomSentence(row: any, col: any) {
var wordCount = ((row + 1) * (col + 1) * 733 * 19) % latinWords.length;
var parts = [];
for (var i = 0; i < wordCount; i++) {
parts.push(latinWords[i]);
var sentence = parts.join(' ');
return sentence + '.';
// create 100 rows
for (var i = 0; i < 100; i++) {
var item = {
rowNumber: 'Row ' + i,
autoA: generateRandomSentence(i, 1),
autoB: generateRandomSentence(i, 2),
autoC: generateRandomSentence(i, 3),
rowData.push(item);
return rowData;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
headerName: 'Row #',
field: 'rowNumber',
width: 120,
field: 'autoA',
width: 300,
//设置wrapText,会产生一个white-space: normal的CSS属性
wrapText: true,
//设置autoHeight,行高与内容高度有关
autoHeight: true,
headerName: 'A) Auto Height',
width: 300,
field: 'autoB',
//只设置了wrapText,但是没有autoHeight,因此仅有一行高度
wrapText: true,
headerName: 'B) Normal Height',
const defaultColDef = useMemo<ColDef>(() => {
return {
sortable: true,
resizable: true,
}, []);
const sideBar = useMemo<
SideBarDef | string | string[] | boolean | null
>(() => {
return {
toolPanels: [
id: 'columns',
labelDefault: 'Columns',
labelKey: 'columns',
iconKey: 'columns',
toolPanel: 'agColumnsToolPanel',
toolPanelParams: {
suppressRowGroups: true,
suppressValues: true,
suppressPivots: true,
suppressPivotMode: true,
suppressSideButtons: true,
suppressColumnFilter: true,
suppressColumnSelectAll: true,
suppressColumnExpandAll: true,
defaultToolPanel: 'columns',
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
// in this example, the CSS styles are loaded AFTER the grid is created,
// so we put this in a timeout, so height is calculated after styles are applied.
setTimeout(function () {
//初始化数据是空的,生成数据是后续加载的
setRowData(getData());
}, 500);
}, []);
* 自动高度的缺点是:
* 鼠标拖动垂直滚动条会有滞后,因为行高的计算需要在展示内容的时候动态计算出来,不能在屏幕外计算出来(虚拟滚动的特性)
* 显示数据会有回跳,因为行高是动态计算的,默认为一行,当前面行的高度发生变化了,当前行的数据就会往下跳
* 无法开启列的虚拟滚动,对于大量列的场景不适用
* 自动行高消耗较大的计算资源,切勿在所有列中设置autoHeight
* pinnedRow的动态行哥不能马上执行
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
sideBar={sideBar}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
自动高度,是根据列的内容来自动调整行高,要点如下:
自动高度的缺点是:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
RowDragEndEvent,
RowDragEvent,
} from 'ag-grid-community';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
//对该列增加一个手型按钮,可以拖动移行
{ field: 'athlete', rowDrag: true },
{ field: 'country' },
{ field: 'year', width: 100 },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
sortable: true,
filter: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onRowDragEnd = useCallback((params: RowDragEndEvent) => {
console.log('rowDrag end', params);
//拖动以后,这个数据依然是旧的
console.log('data', rowData?.slice(0, 10));
//forEachNode里面的才是新数据
const result: any[] = [];
params.api.forEachNode((single, index) => {
if (index <= 10) {
result.push(single.data);
console.log('allData', result);
}, [rowData]);
* rowDragManaged模式的限制点:
* 只能在Client模式中使用
* 无法在分页,排序,筛选,分组,支点模式中使用
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//拖动移行模式,将移行以后自动更新数据,默认是需要手动更新数据的
rowDragManaged={true}
onRowDragEnd={onRowDragEnd}
//移行的过程中,其他行不产生动画变化
suppressMoveWhenRowDragging={true}
animateRows={true}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
将移动对RowNode的变化,交给AgGrid来处理:
要注意的一点是,在移动行以后,AgGrid并没有修改rowData的引用,只修改了RowNode的顺序。所以,不要直接拿rowData的数据,而是重新拿forEachNode里面的数据。
rowDragManaged模式的限制点:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
GetRowIdFunc,
GetRowIdParams,
Grid,
GridOptions,
GridReadyEvent,
RowDragMoveEvent,
} from 'ag-grid-community';
function getData() {
return [
athlete: 'Michael Phelps',
age: 23,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 8,
silver: 0,
bronze: 0,
total: 8,
athlete: 'Michael Phelps',
age: 19,
country: 'United States',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 6,
silver: 0,
bronze: 2,
total: 8,
athlete: 'Michael Phelps',
age: 27,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 4,
silver: 2,
bronze: 0,
total: 6,
athlete: 'Natalie Coughlin',
age: 25,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 1,
silver: 2,
bronze: 3,
total: 6,
athlete: 'Aleksey Nemov',
age: 24,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Gymnastics',
gold: 2,
silver: 1,
bronze: 3,
total: 6,
athlete: 'Alicia Coutts',
age: 24,
country: 'Australia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
athlete: 'Missy Franklin',
age: 17,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 4,
silver: 0,
bronze: 1,
total: 5,
athlete: 'Ryan Lochte',
age: 27,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 1,
total: 5,
athlete: 'Allison Schmitt',
age: 22,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 1,
total: 5,
athlete: 'Natalie Coughlin',
age: 21,
country: 'United States',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 1,
total: 5,
athlete: 'Ian Thorpe',
age: 17,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 2,
bronze: 0,
total: 5,
athlete: 'Dara Torres',
age: 33,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 3,
total: 5,
athlete: 'Cindy Klassen',
age: 26,
country: 'Canada',
year: 2006,
date: '26/02/2006',
sport: 'Speed Skating',
gold: 1,
silver: 2,
bronze: 2,
total: 5,
athlete: 'Nastia Liukin',
age: 18,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
athlete: 'Marit Bjørgen',
age: 29,
country: 'Norway',
year: 2010,
date: '28/02/2010',
sport: 'Cross Country Skiing',
gold: 3,
silver: 1,
bronze: 1,
total: 5,
athlete: 'Sun Yang',
age: 20,
country: 'China',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
athlete: 'Kirsty Coventry',
age: 24,
country: 'Zimbabwe',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
athlete: 'Libby Lenton-Trickett',
age: 23,
country: 'Australia',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
athlete: 'Ryan Lochte',
age: 24,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
athlete: 'Inge de Bruijn',
age: 30,
country: 'Netherlands',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
athlete: 'Petria Thomas',
age: 28,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
athlete: 'Ian Thorpe',
age: 21,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
athlete: 'Inge de Bruijn',
age: 27,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
athlete: 'Gary Hall Jr.',
age: 25,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
athlete: 'Michael Klim',
age: 23,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 0,
total: 4,
athlete: "Susie O'Neill",
age: 27,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
athlete: 'Jenny Thompson',
age: 27,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
athlete: 'Pieter van den Hoogenband',
age: 22,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
athlete: 'An Hyeon-Su',
age: 20,
country: 'South Korea',
year: 2006,
date: '26/02/2006',
sport: 'Short-Track Speed Skating',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
athlete: 'Aliya Mustafina',
age: 17,
country: 'Russia',
year: 2012,
date: '12/08/2012',
sport: 'Gymnastics',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
athlete: 'Shawn Johnson',
age: 16,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
athlete: 'Dmitry Sautin',
age: 26,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Diving',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
athlete: 'Leontien Zijlaard-van Moorsel',
age: 30,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Cycling',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
athlete: 'Petter Northug Jr.',
age: 24,
country: 'Norway',
year: 2010,
date: '28/02/2010',
sport: 'Cross Country Skiing',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
athlete: 'Ole Einar Bjørndalen',
age: 28,
country: 'Norway',
year: 2002,
date: '24/02/2002',
sport: 'Biathlon',
gold: 4,
silver: 0,
bronze: 0,
total: 4,
athlete: 'Janica Kostelic',
age: 20,
country: 'Croatia',
year: 2002,
date: '24/02/2002',
sport: 'Alpine Skiing',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
athlete: 'Nathan Adrian',
age: 23,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 0,
total: 3,
athlete: 'Yannick Agnel',
age: 20,
country: 'France',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 0,
total: 3,
athlete: 'Brittany Elmslie',
age: 18,
country: 'Australia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 1,
silver: 2,
bronze: 0,
total: 3,
var immutableStore: any[] = getData();
var sortActive = false;
var filterActive = false;
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete', rowDrag: true },
{ field: 'country' },
{ field: 'year', width: 100 },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
sortable: true,
filter: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
// add id to each item, needed for immutable store to work
immutableStore.forEach(function (data, index) {
data.id = index;
setRowData(immutableStore);
}, []);
// listen for change on sort changed
const onSortChanged = useCallback(() => {
var colState = gridRef.current!.columnApi.getColumnState() || [];
sortActive = colState.some((c) => c.sort);
// suppress row drag if either sort or filter is active
var suppressRowDrag = sortActive || filterActive;
console.log(
'sortActive = ' +
sortActive +
', filterActive = ' +
filterActive +
', allowRowDrag = ' +
suppressRowDrag
//有sort或者有filter都需要禁止行拖动
gridRef.current!.api.setSuppressRowDrag(suppressRowDrag);
}, [filterActive]);
// listen for changes on filter changed
const onFilterChanged = useCallback(() => {
filterActive = gridRef.current!.api.isAnyFilterPresent();
// suppress row drag if either sort or filter is active
//有sort或者有filter都需要禁止行拖动
var suppressRowDrag = sortActive || filterActive;
console.log(
'sortActive = ' +
sortActive +
', filterActive = ' +
filterActive +
', allowRowDrag = ' +
suppressRowDrag
gridRef.current!.api.setSuppressRowDrag(suppressRowDrag);
}, [filterActive]);
const onRowDragMove = useCallback(
(event: RowDragMoveEvent) => {
var movingNode = event.node;
var overNode = event.overNode;
var rowNeedsToMove = movingNode !== overNode;
if (rowNeedsToMove) {
// the list of rows we have is data, not row nodes, so extract the data
var movingData = movingNode.data;
var overData = overNode!.data;
var fromIndex = immutableStore.indexOf(movingData);
var toIndex = immutableStore.indexOf(overData);
console.log('move data', fromIndex, toIndex);
var newStore = immutableStore.slice();
moveInArray(newStore, fromIndex, toIndex);
immutableStore = newStore;
gridRef.current!.api.setRowData(newStore);
gridRef.current!.api.clearFocusedCell();
function moveInArray(arr: any[], fromIndex: number, toIndex: number) {
var element = arr[fromIndex];
//这个性能较差
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
[immutableStore]
const getRowId = useCallback((params: GetRowIdParams) => {
return params.data.id;
}, []);
* unmanage的性能较差,不太建议使用
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
animateRows={true}
getRowId={getRowId}
onGridReady={onGridReady}
onSortChanged={onSortChanged}
onFilterChanged={onFilterChanged}
onRowDragMove={onRowDragMove}
></AgGridReact>
export default GridExample;
非受控移动,就是AgGrid只告诉移动的过程,但是不对RowNode和RowData进行更新,需要开发者手动更新。
非受控移动注意不要使用Immutable的更新方式,性能很差,需要用原地更新的方式。
'use strict';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
ICellRendererParams,
} from 'ag-grid-community';
import './style.css';
const CustomCellRenderer = (props: ICellRendererParams) => {
const myRef = useRef(null);
useEffect(() => {
//获取year的span,然后告诉ag-grid这个是一个拖动手柄
props.registerRowDragger(myRef.current!);
return (
<div className="my-custom-cell-renderer">
<div className="athlete-info">
<span>{props.data.athlete}</span>
<span>{props.data.country}</span>
<span className="year" ref={myRef}>{props.data.year}</span>
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete',
cellClass: 'custom-athlete-cell',
cellRenderer: CustomCellRenderer,
{ field: 'country' },
{ field: 'year', width: 100 },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
sortable: true,
filter: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowDragManaged={true}
animateRows={true}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
自定义拖动的组件
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
RowClassParams,
RowStyle,
} from 'ag-grid-community';
import './style.css';
//自定义渲染器
const CustomPinnedRowRenderer: React.FC<any> = (props) => {
return <span style={props.style}>{props.value}</span>;
function createData(count: number, prefix: string) {
var result = [];
for (var i = 0; i < count; i++) {
result.push({
athlete: prefix + ' Athlete ' + i,
age: prefix + ' Age ' + i,
country: prefix + ' Country ' + i,
year: prefix + ' Year ' + i,
date: prefix + ' Date ' + i,
sport: prefix + ' Sport ' + i,
return result;
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete',
cellRendererSelector: function (params) {
//pinned数据与普通数据,使用相同的列方式渲染,用node.rowPinned来区分
if (params.node.rowPinned) {
return {
component: CustomPinnedRowRenderer,
params: {
//自定义渲染器的默认参数
style: { color: 'blue' },
} else {
// rows that are not pinned don't use any cell renderer
return undefined;
field: 'age',
cellRendererSelector: function (params) {
if (params.node.rowPinned) {
return {
component: CustomPinnedRowRenderer,
params: {
style: { 'font-style': 'italic' },
} else {
// rows that are not pinned don't use any cell renderer
return undefined;
{ field: 'country' },
{ field: 'year' },
{ field: 'date' },
{ field: 'sport' },
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 200,
sortable: true,
filter: true,
resizable: true,
}, []);
const getRowStyle = useCallback(function (
params: RowClassParams
): RowStyle | undefined {
//行的style设置
if (params.node.rowPinned) {
return { 'font-weight': 'bold' };
const pinnedTopRowData = useMemo<any[]>(() => {
return createData(1, 'Top');
}, []);
const pinnedBottomRowData = useMemo<any[]>(() => {
return createData(1, 'Bottom');
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onPinnedRowTopCount = useCallback(() => {
var headerRowsToFloat = (document.getElementById('top-row-count') as any)
.value;
var count = Number(headerRowsToFloat);
var rows = createData(count, 'Top');
//命令方式地设置pinnedTopRowData,与pinnedBottomRowData
gridRef.current!.api.setPinnedTopRowData(rows);
}, []);
const onPinnedRowBottomCount = useCallback(() => {
var footerRowsToFloat = (document.getElementById('bottom-row-count') as any)
.value;
var count = Number(footerRowsToFloat);
var rows = createData(count, 'Bottom');
gridRef.current!.api.setPinnedBottomRowData(rows);
}, []);
return (
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
<span>Rows to Pin on Top:</span>
<select
onChange={onPinnedRowTopCount}
id="top-row-count"
style={{ marginLeft: '10px', marginRight: '20px' }}
<option value="0">0</option>
<option value="1" selected={true}>
</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<span>Rows to Pin on Bottom:</span>
<select
onChange={onPinnedRowBottomCount}
id="bottom-row-count"
style={{ marginLeft: '10px' }}
<option value="0">0</option>
<option value="1" selected={true}>
</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
getRowStyle={getRowStyle}
//pinnedTopRowData相当有用,它们与普通grid共用列,但是不同的数据源
//但是pinned数据,不参与排序,筛选,分组,选择
pinnedTopRowData={pinnedTopRowData}
pinnedBottomRowData={pinnedBottomRowData}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
固定行时一个相当有用的功能,常常用来显示合计行的信息。要点如下:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
ICellRendererComp,
ICellRendererParams,
RowSpanParams,
} from 'ag-grid-community';
import getData from './data';
import './style.css';
function rowSpan(params: RowSpanParams) {
if (params.data.show) {
return 4;
} else {
return 1;
const ShowCellRenderer: React.FC<any> = (props) => {
const cellBlank = !props.value;
if (cellBlank) {
return null;
return (
<div className="show-name">{props.value.name}</div>
<div className="show-presenter">{props.value.presenter}</div>
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'localTime' },
field: 'show',
cellRenderer: ShowCellRenderer,
//ag-grid的rowSpan是一个掩眼法,它没有真的在table里面使用rowSpan
//它是根据rowSpan的数值来设置每个单元格的高度
//例如,当rowSpan为2的时候,这个单元格的高度刚好为2倍的行高。而那个被rowSpan覆盖的单元格依然会被正常渲染出来
rowSpan: rowSpan,
cellClassRules: {
//所以,rowSpan都需要一个特别的操作,将rowSpan的单元格设置background为一个具体的颜色,来掩盖下面的单元格
//你可以试试更改show-cell为show-cell2的类名就知道了
//cellClassRules的意思是,value(指当前的show字段)不为空的时候,该单元格就赋予一个show-cell的类名
'show-cell': 'value !== undefined',
width: 200,
{ field: 'a' },
{ field: 'b' },
{ field: 'c' },
{ field: 'd' },
{ field: 'e' },
const defaultColDef = useMemo<ColDef>(() => {
return {
resizable: true,
width: 170,
}, []);
* AgGrid的rowSpan有很多限制,包括有:
* 不要在最后一行进行rowSpan,否则会产生span过高留有空行
* 需要设置cellClassRules来设置背景,保证rowSpan的下面行看不到
* 覆盖行肉眼上看不到,但是依然有效果,例如焦点转移的时候依然会转到它身上,(这简直太糟糕了,看不到的单元格还会占据焦点位置)
* 动态行高,自动行高都用不了
* 排序和筛选都有奇怪的展示效果
* 范围选择也会有奇怪的效果,不能正常工作
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//使用rowSpan的时候,我们都会加这个suppressRowTransform属性
//这个属性的意思,虚拟列表使用top来实现,而不是使用transform来实现
suppressRowTransform={true}
></AgGridReact>
export default GridExample;
AgGrid的合并行方法:
AgGrid的rowSpan有很多限制,包括有:
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useRef, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
import { GetRowIdParams } from 'ag-grid-community';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 2, make: "Ford", model: "Mondeo", price: '91.2', price2: 91.2 },
{ id: 1, make: "Toyota", model: "Celica", price: '3.23', price2: 3.23 },
{ id: 3, make: "Porsche", model: "Boxter", price: '188.7', price2: 188.7 }
const [columnDefs] = useState([
{ field: 'make' },
{ field: 'model' },
{ headerName: '价格(默认排序)', field: 'price' },
headerName: '价格(自定义排序)', field: 'price',
//注意这个comparator与filter中的comparator的不同
//sort的comparator是为了grid数组中的两两比较
//filter中的comparator的是为了与目标数据的比较
comparator: (valueA: any, valueB: any, nodeA: any, nodeB: any, isInverted: boolean) => {
let v1Number = Number.parseFloat(valueA);
let v2Number = Number.parseFloat(valueB);
if (v1Number == v2Number) return 0;
return (v1Number > v2Number) ? 1 : -1;
const defaultColDef = useMemo(() => {
return {
sortable: true,
}, []);
const gridRef = useRef<AgGridReact>(null);
const add = () => {
let maxId = -1;
rowData.forEach(single => {
if (maxId <= single.id) {
maxId = single.id;
const rand = Math.floor(Math.random() * 10000) / 100;
let newRowData = [
...rowData,
id: maxId + 1,
make: "M" + maxId,
model: "C" + maxId,
price: rand + '',
price2: rand,
setRowData(newRowData);
let del = () => {
let selectedRows = gridRef.current!.api.getSelectedNodes();
let selectionRowIds = selectedRows.map(single => {
return single.data.id;
let newRowData = rowData.filter((single) => {
return selectionRowIds.indexOf(single.id) < 0;
console.log(rowData, selectionRowIds, newRowData);
setRowData(newRowData);
//需要指定getRowId,才能启用animateRows,而且在重新刷新数据的时候更少地触发render
const getRowId = useCallback((props: GetRowIdParams) => {
return props.data.id;
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100vh' }}>
<Button onClick={add}>{'添加一行'}</Button>
<Button onClick={del}>{'删除一行'}</Button>
<div className="ag-theme-alpine" style={{ width: '100%', flex: '1' }}>
<AgGridReact
ref={gridRef}
getRowId={getRowId}
rowData={rowData}
defaultColDef={defaultColDef}
rowSelection={'multiple'}
columnDefs={columnDefs}
animateRows={true}
//默认不支持多列排序,需要用multiSortKey来指定
multiSortKey={'ctrl'}>
</AgGridReact>
export default App;
行排序的要点:
import styles from './index.less';
import { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.css';
//引入主题
import 'ag-grid-community/dist/styles/ag-theme-balham.css';
import { Button } from 'antd';
import { GetRowIdFunc, RowClassParams } from 'ag-grid-community';
import './style.css';
const App: React.FC<any> = () => {
const [rowData, setRowData] = useState([
{ id: 1, make: "Toyota", model: "Celica", price: 35000 },
{ id: 2, make: "Ford", model: "Mondeo", price: 32000 },
{ id: 3, make: "Porsche", model: "Boxter", price: 72000 }
const [columnDefs] = useState([
{ field: 'make' },
{ field: 'model' },
{ field: 'price' }
const add = () => {
let newRowData = [
...rowData,
id: rowData.length + 1,
make: "M1",
model: "C2",
price: 23000,
setRowData(newRowData);
//每行的style
const getRowStyle = (params: any) => {
if (params.node.rowIndex % 2 === 0) {
return { background: 'red' };
//每行的class
const getRowClass = (params: any) => {
if (params.node.rowIndex % 3 === 0) {
return 'my-shaded-effect';
const getRowId: GetRowIdFunc = useCallback((props) => {
return props.data.id;
}, []);
const rowClassRules = useMemo(() => {
//输入row,输出boolean,指定是否有这个class
return {
// apply green to 2008
'rag-green-outer': function (params: RowClassParams) {
let result = (params.data.make == 'M1');
console.log(params.data, result);
return result;
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100vh' }}>
<Button onClick={add}>{'添加一行'}</Button>
<div className="ag-theme-balham" style={{ flex: '1', width: '100%' }}>
<AgGridReact
getRowId={getRowId}
getRowStyle={getRowStyle}
getRowClass={getRowClass}
rowData={rowData}
rowClassRules={rowClassRules}
columnDefs={columnDefs}>
</AgGridReact>
export default App;
行样式的三种方法:
行样式都是配置在AgGrid的属性上的。
'use strict';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import React, { useCallback, useMemo, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import {
CellClassParams,
CellClassRules,
ColDef,
GridReadyEvent,
ICellRendererParams,
ValueParserParams,
} from 'ag-grid-community';
const ragCellClassRules: CellClassRules = {
'rag-green-outer': (params) => params.value === 2008,
'rag-amber-outer': (params) => params.value === 2004,
'rag-red-outer': (params) => params.value === 2000,
const cellStyle = (params: CellClassParams) => {
const color = numberToColor(params.value);
return {
backgroundColor: color,
const numberToColor = (val: number) => {
if (val === 0) {
return '#ffaaaa';
} else if (val == 1) {
return '#aaaaff';
} else {
return '#aaffaa';
const ragRenderer = (params: ICellRendererParams) => {
return <span className="rag-element">{params.value}</span>;
const numberParser = (params: ValueParserParams) => {
const newValue = params.newValue;
let valueAsNumber;
if (newValue === null || newValue === undefined || newValue === '') {
valueAsNumber = null;
} else {
valueAsNumber = parseFloat(params.newValue);
return valueAsNumber;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete' },
field: 'age',
maxWidth: 90,
valueParser: numberParser,
//输入cell的value,输出boolean,是否需要这个class
cellClassRules: {
'rag-green': 'x < 20',
'rag-amber': 'x >= 20 && x < 25',
'rag-red': 'x >= 25',
{ field: 'country' },
field: 'year',
maxWidth: 90,
valueParser: numberParser,
cellClassRules: ragCellClassRules,
cellRenderer: ragRenderer,
{ field: 'date', cellClass: 'rag-amber' },
field: 'sport',
//输入cell的value,输出cell的class
cellClass: (params: CellClassParams) => {
return params.value === 'Swimming' ? 'rag-green' : 'rag-amber';
field: 'gold',
valueParser: numberParser,
//cell的样式,一个固定的常量
cellStyle: {
// you can use either came case or dashes, the grid converts to whats needed
backgroundColor: '#aaffaa', // light green
field: 'silver',
valueParser: numberParser,
//cell的样式,一个函数,传入不同cell的value,输出样式
cellStyle: (params: CellClassParams) => {
const color = numberToColor(params.value);
return {
backgroundColor: color,
field: 'bronze',
valueParser: numberParser,
// same as above, but demonstrating dashes in the style, grid takes care of converting to/from camel case
cellStyle: cellStyle,
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
editable: true,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
单元格样式,要点:
单元格样式,都是配置在column上的
Client-Data是支持特性最为丰富的模式,我们应尽可能使用这种数据模式。
代码在 这里
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { ColDef, ColGroupDef, GetRowIdParams, Grid, GridOptions } from 'ag-grid-community';
import './style.css'
import getData from './getData';
import { Button } from 'antd';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<React.CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'firstName' },
{ field: 'lastName' },
{ field: 'gender' },
{ field: 'age' },
{ field: 'mood' },
{ field: 'country' },
{ field: 'address', minWidth: 550 },
const defaultColDef = useMemo<ColDef>(() => {
return {
//flex: 1,
minWidth: 110,
//全部单元格默认是可编辑的,数据会直接在rowData上进行修改
editable: true,
resizable: true,
sortable: true,
}, []);
const getAllData = useCallback(() => {
console.log('allRowData', rowData);
gridRef.current!.api.forEachNode((rowNode, index) => {
console.log('node ' + index + " : " + JSON.stringify(rowNode.data) + " is in the grid");
// iterate only nodes that pass the filter
//forEachNodeAfterFilter
//iterate only nodes that pass the filter and ordered by the sort order
//forEachNodeAfterFilterAndSort
//iterate through every leaf node in the grid
//forEachLeafNode
}, [rowData]);
const refreshAllData = useCallback(() => {
//将Ag-Grid看成是渲染器,不存放数据相关操作
setRowData(getData());
}, [rowData]);
const reverseData = useCallback(() => {
let newData = [...rowData].reverse();
setRowData(newData);
}, [rowData]);
const pushData = useCallback(() => {
let maxId = -1;
for (let i in rowData) {
if (rowData[i].id > maxId) {
maxId = rowData[i].id;
let id = maxId + 1;
let newData = [
...rowData,
id: id,
firstName: 'Fish_' + id,
lastName: 'Fish_' + id,
gender: 'Male',
age: id,
setRowData(newData);
}, [rowData]);
const popData = useCallback(() => {
let newData = rowData.filter((single, index) => {
return index != 0;
setRowData(newData);
}, [rowData]);
const removeSelected = useCallback(() => {
const selectedRowNodes = gridRef.current!.api.getSelectedNodes();
const selectedIds = selectedRowNodes.map(function (rowNode) {
//id总是为string类型
return rowNode.id;
const newData = rowData.filter((single) => {
return selectedIds.indexOf(single.id + '') < 0;
setRowData(newData);
}, [rowData]);
const allSetAge = useCallback(() => {
const newData = rowData.map((single) => {
let age = Math.floor(Math.random() * 100);
return {
...single,
age: age,
setRowData(newData);
}, [rowData]);
const getRowId = useCallback((params: GetRowIdParams) => {
return params.data.id;
}, []);
return (
<div style={containerStyle}>
<Button onClick={getAllData}>{'获取数据'}</Button>
<Button onClick={refreshAllData}>{'重置全部数据'}</Button>
<Button onClick={reverseData}>{'reverse数据'}</Button>
<Button onClick={pushData}>{'push数据'}</Button>
<Button onClick={popData}>{'pop数据'}</Button>
<Button onClick={removeSelected}>{'删除选中行'}</Button>
<Button onClick={allSetAge}>{'随机设置Age'}</Button>
<div className="grid-wrapper">
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//需要设置getRowId,才能得到更好的刷新效果
getRowId={getRowId}
rowSelection={'multiple'}
animateRows={true}
//默认的Enter键进入或者退出编辑状态,并不会去转移光标
//鼠标单击进入编辑状态,但是会丢失导航能力,并不好用
singleClickEdit={true}
//当表格丢失焦点的时候,自动停止编辑并保存,这点非常有用
stopEditingWhenCellsLoseFocus={true}
></AgGridReact>
export default GridExample;
外部数据源是React推荐的数据流模式,AgGrid也支持这种模式。另外:
外部数据源模式,在数据更新的时候,AgGrid也是采用类似React的方式进行合并更新
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { ColDef, ColGroupDef, GetRowIdParams, Grid, GridOptions } from 'ag-grid-community';
import './style.css'
import getData from './getData';
import { Button } from 'antd';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<React.CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'firstName' },
{ field: 'lastName' },
{ field: 'gender' },
{ field: 'age' },
{ field: 'mood' },
{ field: 'country' },
{ field: 'address', minWidth: 550 },
const defaultColDef = useMemo<ColDef>(() => {
return {
//flex: 1,
minWidth: 110,
//全部单元格默认是可编辑的,数据会直接在rowData上进行修改
editable: true,
resizable: true,
sortable: true,
}, []);
const getAllData = useCallback(() => {
let rowData: any[] = [];
gridRef.current!.api.forEachNode((rowNode, index) => {
rowData.push(rowNode.data);
return rowData;
}, []);
const refreshAllData = useCallback(() => {
//setRowData没有Immutable的限制,可以传递不同或者相同的引用进去也能更新数据
//但是这样做会导致可能会导致困惑,rowData里面的引用没有改变了,但是数据改变了
//另外,从当前的rowData拿数据也会产生困惑
//这其实是将Ag-Grid看成是数据容器,以命令的方式写入数据到数据容器中
//只不过当Ag-Grid的数据引用与初始rowData保持一致的话,我们能更少地出现bug
//gridRef.current!.api.setRowData(getData());
//更好的做法是,坚持不更改rowData的引用传递进去
let newData = getAllData();
let rows = getData();
newData.splice(0, newData.length);
for (let i in rows) {
newData.push(rows[i]);
gridRef.current!.api.setRowData(newData);
//另外一种办法是,坚持不是用api.setRowData,而是使用React的setRowData来设置数据,但是这样做需要Immutable的设置
//看index2的实现
}, []);
const reverseData = useCallback(() => {
let newData = getAllData();
newData.reverse();
gridRef.current!.api.setRowData(newData);
}, []);
const pushData = useCallback(() => {
let newData = getAllData();
let maxId = -1;
for (let i in newData) {
if (newData[i].id > maxId) {
maxId = newData[i].id;
let id = maxId + 1;
newData.push({
id: id,
firstName: 'Fish_' + id,
lastName: 'Fish_' + id,
gender: 'Male',
age: id,
gridRef.current!.api.setRowData(newData);
}, []);
const popData = useCallback(() => {
let newData = getAllData();
if (newData.length != 0) {
newData.splice(0, 1);
gridRef.current!.api.setRowData(newData);
}, []);
const removeSelected = useCallback(() => {
const selectedRowNodes = gridRef.current!.api.getSelectedNodes();
const selectedIds = selectedRowNodes.map(function (rowNode) {
return rowNode.id;
let newData = getAllData();
selectedIds.forEach(single => {
const delIndex = newData.findIndex((data) => {
return data.id == single;
if (delIndex != -1) {
newData.splice(delIndex, 1);
gridRef.current!.api.setRowData(newData);
}, []);
const allSetAge = useCallback(() => {
//修改一个单元格的信息时,需要将整个row的引用改掉
let newData = getAllData();
for (let i in newData) {
let single = newData[i];
//这样做是正确的,修改需要将整个row的引用改掉
newData[i] = {
...single,
age: Math.floor(Math.random() * 200),
gridRef.current!.api.setRowData(newData);
}, []);
const failAllSetAge = useCallback(() => {
//修改一个单元格的信息时,需要将整个row的引用改掉
let newData = getAllData();
for (let i in newData) {
let single = newData[i];
//这样做是错误的,因为key对应的object引用不变,所以Grid不进行修改
single.age = Math.floor(Math.random() * 200);
gridRef.current!.api.setRowData(newData);
}, []);
const getRowId = useCallback((params: GetRowIdParams) => {
return params.data.id;
}, []);
return (
<div style={containerStyle}>
<Button onClick={getAllData}>{'获取数据'}</Button>
<Button onClick={refreshAllData}>{'重置全部数据'}</Button>
<Button onClick={reverseData}>{'reverse数据'}</Button>
<Button onClick={pushData}>{'push数据'}</Button>
<Button onClick={popData}>{'pop数据'}</Button>
<Button onClick={removeSelected}>{'删除选中行'}</Button>
<Button onClick={allSetAge}>{'随机设置Age'}</Button>
<Button onClick={failAllSetAge}>{'错误随机设置Age'}</Button>
<div className="grid-wrapper">
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//需要设置getRowId,才能得到更好的刷新效果
getRowId={getRowId}
rowSelection={'multiple'}
animateRows={true}
//默认的Enter键进入或者退出编辑状态,并不会去转移光标
//鼠标单击进入编辑状态,但是会丢失导航能力,并不好用
singleClickEdit={true}
//当表格丢失焦点的时候,自动停止编辑并保存,这点非常有用
stopEditingWhenCellsLoseFocus={true}
></AgGridReact>
export default GridExample;
内部数据源是由React来存放数据自身,开发者在外部也不进行数据存储,这种方式对AgGrid的支持度最高,性能相对外部数据源也好一点。要点如下:
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
RowNodeTransaction,
} from 'ag-grid-community';
import getData from './getData2';
let newCount = 1;
function createNewRowData() {
const newData = {
make: 'Toyota ' + newCount,
model: 'Celica ' + newCount,
price: 35000 + newCount * 17,
zombies: 'Headless',
style: 'Little',
clothes: 'Airbag',
newCount++;
return newData;
function printResult(res: RowNodeTransaction) {
console.log('---------------------------------------');
if (res.add) {
res.add.forEach(function (rowNode) {
console.log('Added Row Node', rowNode);
if (res.remove) {
res.remove.forEach(function (rowNode) {
console.log('Removed Row Node', rowNode);
if (res.update) {
res.update.forEach(function (rowNode) {
console.log('Updated Row Node', rowNode);
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ flex: '1', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'make' },
{ field: 'model' },
{ field: 'price' },
{ field: 'zombies' },
{ field: 'style' },
{ field: 'clothes' },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
}, []);
const getRowData = useCallback(() => {
const rowData: any[] = [];
gridRef.current!.api.forEachNode(function (node) {
rowData.push(node.data);
console.log('Row Data:');
console.table(rowData);
}, []);
const clearData = useCallback(() => {
gridRef.current!.api.setRowData([]);
}, []);
const addItems = useCallback((addIndex: number | undefined) => {
const newItems = [
createNewRowData(),
createNewRowData(),
createNewRowData(),
//添加行
const res = gridRef.current!.api.applyTransaction({
add: newItems,
//addIndex为undefined的时候,就是尾部插入的意思
addIndex: addIndex,
printResult(res);
}, []);
const updateItems = useCallback(() => {
// update the first 2 items
const itemsToUpdate: any[] = [];
gridRef.current!.api.forEachNodeAfterFilterAndSort(function (
rowNode,
index
// only do first 2
if (index >= 2) {
return;
const data = rowNode.data;
data.price = Math.floor(Math.random() * 20000 + 20000);
itemsToUpdate.push(data);
//对于数据update,优先使用rowId来判断行,没有rowId就用对象引用来判断行,都没有的话就看成没有相同行
const res = gridRef.current!.api.applyTransaction({
//更新行
update: itemsToUpdate,
printResult(res);
}, []);
const onRemoveSelected = useCallback(() => {
const selectedData = gridRef.current!.api.getSelectedRows();
//删除行
const res = gridRef.current!.api.applyTransaction({
remove: selectedData,
printResult(res);
}, []);
return (
<div style={containerStyle}>
<div style={{ marginBottom: '4px' }}>
<button onClick={() => addItems(undefined)}>Add Items</button>
<button onClick={() => addItems(2)}>Add Items addIndex=2</button>
<button onClick={updateItems}>Update Top 2</button>
<button onClick={onRemoveSelected}>Remove Selected</button>
<button onClick={getRowData}>Get Row Data</button>
<button onClick={clearData}>Clear Data</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowSelection={'multiple'}
animateRows={true}
></AgGridReact>
export default GridExample;
无论是setRowData的内部数据源方式,还是React的外部数据源方式,他们都离不开传入一个新数组,然后由AgGrid来做diff操作更新。在一些对性能特别敏感的场景中,diff操作可能会成为操作的瓶颈,因此,AgGrid提供了更为底层的Transaction的事务提交方式。
applyTransaction,传入的都是data数据,而不是RowNode数据,但是避免了diff操作,性能更好。返回值是RowNode数据。
RowNode上也可以用setData,和setDataValue来做细粒度的更新操作,但是这种操作不是立即更新的。在数据更新以后,sort,filter,group,pivot这些操作都不会自动刷新,需要手动刷新,这里就没有Demo了。
AgGrid还提供了周期性异步刷新的工作方式,看 这里 ,能大幅提高数据更新速度,付出代价是数据的展示有少量延迟。这里也不做Demo了。
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef, ColGroupDef, GetRowIdParams, Grid, GridOptions, ICellRendererParams,
ValueGetterParams
} from 'ag-grid-community';
import './style.css'
import getData from './getData';
import { Button } from 'antd';
const RowIndexRender: React.FC<ICellRendererParams> = (props) => {
const rowIndex = props.rowIndex + 1;
console.log('RowIndexRender');
return <span>{rowIndex}</span>
const AllNameRender: React.FC<ICellRendererParams> = (props) => {
console.log('allNameRender');
const data = props.data;
const allName = 'AllName:[' + data.firstName + " " + data.lastName + "]";
return <span>{allName}</span>
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<React.CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
colId: 'checkbox',
checkboxSelection: true,
width: 100,
//无脏检查
headerName: '行号(错误示范)',
colId: 'rowId1',
cellRenderer: RowIndexRender,
//valueGetter作脏检查
headerName: '行号(valueGetter作脏检查,错误示范)',
colId: 'rowId2',
valueGetter: (params: ValueGetterParams) => {
console.log('checkRender', params);
return params.node?.rowIndex;
cellRenderer: RowIndexRender,
{ field: 'firstName', sortable: true, editable: true },
{ field: 'lastName', sortable: true, editable: true },
//原数据没有allName字段,自动脏检查会失败
colId: 'all1',
headerName: '全名(错误示范)',
field: 'allName',
cellRenderer: AllNameRender,
//用valueGetter来反馈ag-grid,脏检查使用的是valueGetter反馈的结果
colId: 'all2',
headerName: '全名(valueGetter作脏检查)',
valueGetter: (params: ValueGetterParams) => {
const data = params.data;
return data.firstName + "_" + data.lastName;
cellRenderer: AllNameRender,
//用valueGetter和equals来反馈ag-grid,脏检查使用的是equals反馈的结果
colId: 'all3',
headerName: '全名(equals作脏检查,错误示范)',
valueGetter: (params: ValueGetterParams) => {
const data = params.data;
return data;
equals: (left: any, right: any) => {
//left与right的类型来自于field,或者valueGetter的结果
if (left.firstName != right.firstName) {
return false;
if (left.lastName != right.lastName) {
return false;
return true;
cellRenderer: AllNameRender,
const defaultColDef = useMemo<ColDef>(() => {
return {
//flex: 1,
minWidth: 110,
resizable: true,
}, []);
const getAllData = useCallback(() => {
let rowData: any[] = [];
gridRef.current!.api.forEachNode((rowNode, index) => {
rowData.push(rowNode.data);
return rowData;
}, []);
const refreshAllData = useCallback(() => {
const allData = getData();
gridRef.current!.api.setRowData(allData);
}, []);
const setLastNameInPlace = useCallback(() => {
const rows = gridRef.current!.api.getSelectedRows();
rows.forEach(single => {
single.lastName = 'jj';
gridRef.current!.api.applyTransaction({
update: rows,
}, []);
const setLastNameNoInPlace = useCallback(() => {
const rows = gridRef.current!.api.getSelectedRows();
let updatedRows = rows.map(single => {
return {
...single,
lastName: 'jj',
gridRef.current!.api.applyTransaction({
update: updatedRows,
}, []);
const refreshForce = useCallback(() => {
gridRef.current!.api.refreshCells({
force: true,
}, []);
const refreshNotForce = useCallback(() => {
gridRef.current!.api.refreshCells({
}, []);
const getRowId = useCallback((params: GetRowIdParams) => {
return params.data.id;
}, []);
return (
<div style={containerStyle}>
<Button onClick={getAllData}>{'获取数据'}</Button>
<Button onClick={refreshAllData}>{'重置全部数据'}</Button>
<Button onClick={setLastNameInPlace}>{'原地选中行设置lastName(错误示范)'}</Button>
<Button onClick={setLastNameNoInPlace}>{'非原地选中行设置lastName'}</Button>
<Button onClick={refreshForce}>{'强制刷新'}</Button>
<Button onClick={refreshNotForce}>{'非强制刷新'}</Button>
<div className="grid-wrapper">
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
onGridReady={refreshAllData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
getRowId={getRowId}
rowSelection={'multiple'}
animateRows={true}
singleClickEdit={true}
stopEditingWhenCellsLoseFocus={true}
></AgGridReact>
export default GridExample;
AgGrid脏检查的流程:
refreshCells的force区别
系统自带操作的表示:
常见的坑:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
RowNode,
} from 'ag-grid-community';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'athlete', minWidth: 150,
//把该列加入一个选择框,方便进行勾选操作,特别是在多选操作的时候
checkboxSelection: true,
//头部是否有一个勾选全部的按钮
headerCheckboxSelection: true,
//当数据进入筛选模式的时候,勾选全部是指勾选所有数据,还是只勾选筛选后的数据
//headerCheckboxSelectionFilteredOnly: true,
{ field: 'age', maxWidth: 90 },
{ field: 'country', minWidth: 150 },
{ field: 'year', maxWidth: 90 },
{ field: 'date', minWidth: 150 },
{ field: 'sport', minWidth: 150 },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 100,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
const onSelectionChanged = useCallback(() => {
//selection changed的事件
//getSelectedRows来获取选中数据
//getSelectedNodes来获取选中的Node
var selectedRows = gridRef.current!.api.getSelectedRows();
var selectedRowsString = '';
var maxToShow = 5;
selectedRows.forEach(function (selectedRow, index) {
if (index >= maxToShow) {
return;
if (index > 0) {
selectedRowsString += ', ';
selectedRowsString += selectedRow.athlete;
if (selectedRows.length > maxToShow) {
var othersCount = selectedRows.length - maxToShow;
selectedRowsString +=
' and ' + othersCount + ' other' + (othersCount !== 1 ? 's' : '');
(document.querySelector(
'#selectedRows'
) as any).innerHTML = selectedRowsString;
}, []);
//设置该行是否可以被选择
const isRowSelectable = useCallback((rowNode: RowNode) => {
return rowNode.data.age > 25;
}, []);
const selectAllAmerican = useCallback(() => {
//通过node的setSelected来设置选中行
gridRef.current!.api.forEachNode(function (node) {
node.setSelected(node.data.country === 'United States');
}, []);
return (
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
Selection:
<span id="selectedRows"></span>
<button onClick={selectAllAmerican}>Select All American</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//rowSelection默认为单选,可以设置为多选
rowSelection={'multiple'}
onGridReady={onGridReady}
isRowSelectable={isRowSelectable}
onSelectionChanged={onSelectionChanged}
></AgGridReact>
export default GridExample;
要点如下:
选中显示是一个内部数据源的模式
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import moment from 'moment';
const GridExample = () => {
const gridRef = useRef<any>();
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState([
field: 'athlete',
filter: 'agTextColumnFilter',
//有filterParams的都不能自动刷新
filterParams: {
//弹出filter框的按钮有哪些,重置按钮,和应用按钮
buttons: ['reset', 'apply'],
//默认情况后,点击按钮并不会自动退出面板
field: 'age',
maxWidth: 100,
filter: 'agNumberColumnFilter',
filterParams: {
buttons: ['reset', 'apply'],
//点击filter按钮后是否自动关闭filter框,且应用执行
closeOnApply: true,
field: 'country',
filter: 'agTextColumnFilter',
filterParams: {
//Clear按钮与Reset按钮不同的是,Clear按钮只是清除面板数据,并不会更新Filter数据
buttons: ['clear', 'apply'],
field: 'year',
filter: 'agNumberColumnFilter',
filterParams: {
//Cancel按钮就是取消了
buttons: ['apply', 'cancel'],
closeOnApply: true,
maxWidth: 100,
headerName: '年',
field: 'year',
filter: 'agDateColumnFilter',
filterParams: {
comparator: function (filterLocalDateAtMidnight: Date, cellValue: string) {
let left = moment(filterLocalDateAtMidnight);
let right = moment(cellValue, 'YYYY');
let result = left.year() - right.year();
if (result < 0) {
return 1;
} else if (result > 0) {
return -1;
} else {
return 0;
//默认inRange不包含两个边界点,需要指定这个选项
inRangeInclusive: true,
maxWidth: 100,
{ field: 'sport' },
{ field: 'gold', filter: 'agNumberColumnFilter' },
{ field: 'silver', filter: 'agNumberColumnFilter' },
{ field: 'bronze', filter: 'agNumberColumnFilter' },
{ field: 'total', filter: 'agNumberColumnFilter' },
const defaultColDef = useMemo(() => {
return {
minWidth: 150,
filter: true,
floatingFilter: true,
}, []);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '80%', width: '80%' }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
要点如下:
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { Button } from 'antd';
const GridExample = () => {
const gridRef = useRef<any>();
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState([
field: 'athlete',
//设置按照集合来筛选
filter: 'agSetColumnFilter',
//默认会从数据里面拿,我们也可以手动指定
filterParams: {
buttons: ['reset', 'apply'],
//默认是由AgGrid自己抓取所有可能数据,也可以自己提供set的数据
//values: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
field: 'age',
maxWidth: 100,
filter: 'agNumberColumnFilter',
colId: 'country2',
field: 'country',
filter: 'agSetColumnFilter',
filterParams: {
//默认是多次勾选一个集合的,允许分批多次勾选,操作的步骤就应该是,先剔除所有勾选,然后逐次勾选集合
buttons: ['clear', 'apply'],
field: 'country',
filter: 'agSetColumnFilter',
filterParams: {
//这个是只筛选UI看到的集合,这个也好用,缺点是只能单选一个UI集合
//操作步骤应该是,直接输入text,然后勾选
applyMiniFilterWhileTyping: true,
field: 'year',
filter: 'agNumberColumnFilter',
maxWidth: 100,
field: 'sport',
//多筛选器
filter: 'agMultiColumnFilter',
filterParams: {
filters: [
filter: 'agTextColumnFilter',
filter: 'agSetColumnFilter',
filterParams: {
//默认是多次勾选一个集合的,这个是只筛选UI看到的集合,这个也好用,缺点是只能单选一个UI集合
applyMiniFilterWhileTyping: true,
{ field: 'gold', filter: 'agNumberColumnFilter' },
{ field: 'silver', filter: 'agNumberColumnFilter' },
{ field: 'bronze', filter: 'agNumberColumnFilter' },
{ field: 'total', filter: 'agNumberColumnFilter' },
const defaultColDef = useMemo(() => {
return {
minWidth: 150,
filter: true,
floatingFilter: true,
}, []);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
const reset = useCallback(() => {
gridRef.current.api.setFilterModel(null);
}, []);
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '80%', width: '80%' }}>
<Button onClick={reset}>重置筛选</Button>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
//加入侧边栏的筛选
sideBar={'filters'}
></AgGridReact>
export default GridExample;
集合筛选是AgGrid中的下拉列表筛选方式,要点如下:
可以将多个筛选器组合在一起,agMultiColumnFilter,看代码吧
AgGrid对单元格的数据,从field取出来以后,有多个步骤,分别为:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
ValueGetterParams,
} from 'ag-grid-community';
function createRowData() {
var rowData = [];
for (var i = 0; i < 100; i++) {
rowData.push({
a: Math.floor(i % 4),
b: Math.floor(i % 7),
return rowData;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(createRowData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
headerName: '#',
maxWidth: 100,
valueGetter: (params: ValueGetterParams) => {
//使用node参数
return params.node ? params.node.rowIndex : null;
{ field: 'a' },
{ field: 'b' },
headerName: 'A + B',
colId: 'a&b',
valueGetter: (params: ValueGetterParams) => {
//多个参数
return params.data.a + params.data.b;
headerName: 'A * 1000',
minWidth: 95,
valueGetter: (params: ValueGetterParams) => {
//单个参数
return params.data.a * 1000;
headerName: 'B * 137',
minWidth: 90,
valueGetter: (params: ValueGetterParams) => {
return params.data.b * 137;
headerName: 'Random',
minWidth: 90,
valueGetter: () => {
//随机数
//在跳出viewPort重新进入以后会刷新
return Math.floor(Math.random() * 1000);
headerName: 'Chain',
valueGetter: (params: ValueGetterParams) => {
//相当于a+b,然后*1000,没啥意义
return params.getValue('a&b') * 1000;
headerName: 'Const',
minWidth: 85,
valueGetter: () => {
return 99999;
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 75,
sortable: true,
// cellClass: 'number-cell'
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
></AgGridReact>
export default GridExample;
valueGetter就是从data中提取data value的方式。
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
ValueFormatterParams,
} from 'ag-grid-community';
function bracketsFormatter(params: ValueFormatterParams) {
return '(' + params.value + ')';
function currencyFormatter(params: ValueFormatterParams) {
return '£' + formatNumber(params.value);
//3位数字分割法
function formatNumber(number: number) {
// this puts commas into the number eg 1000 goes to 1,000,
// i pulled this from stack overflow, i have no idea how it works
return Math.floor(number)
.toString()
.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
function createRowData() {
var rowData = [];
for (var i = 0; i < 100; i++) {
rowData.push({
a: Math.floor(((i + 2) * 173456) % 10000),
b: Math.floor(((i + 7) * 373456) % 10000),
return rowData;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(createRowData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ headerName: 'A', field: 'a' },
{ headerName: 'B', field: 'b' },
//valueFormatter与valueGetter的不同在于,
//valueFormatter只对数据进行装饰处理,不改变数据的value,所以是用旧value来sort
//valueGetter确实是更改了数据本身,所以可以用新的value来sort,
{ headerName: '£A', field: 'a', valueFormatter: currencyFormatter },
{ headerName: '£B', field: 'b', valueFormatter: currencyFormatter },
{ headerName: '(A)', field: 'a', valueFormatter: bracketsFormatter },
{ headerName: '(B)', field: 'b', valueFormatter: bracketsFormatter },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
cellClass: 'number-cell',
resizable: true,
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
></AgGridReact>
export default GridExample;
valueFormatter是将data value转换到UI value的方式。
valueRender与headerComponent'use strict';
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
CellEditingStartedEvent,
CellEditingStoppedEvent,
ColDef,
ColGroupDef,
Grid,
GridOptions,
ICellRendererParams,
RowEditingStartedEvent,
RowEditingStoppedEvent,
} from 'ag-grid-community';
import GenderRenderer from './genderRenderer';
import MoodRenderer from './moodRenderer';
const ValueRender: React.FC<ICellRendererParams> = (props) => {
console.log('value', props.value);
console.log('title', (props as any).title);
console.log('ctx', props.context);
console.log('data', props.data);
console.log('rowIndex', props.rowIndex);
return (<div style={{ background: 'blue', color: 'white' }}>{props.value}</div>);
const SortingHeader = memo((props: any) => {
const [sortState, setSortState] = useState<string>();
const onClick = useCallback(() => {
props.progressSort();
}, []);
useEffect(() => {
const listener = () => {
if (props.column.isSortAscending()) {
setSortState('ASC');
} else if (props.column.isSortDescending()) {
setSortState('DESC');
} else {
setSortState(undefined);
props.column.addEventListener('sortChanged', listener);
return () => props.column.removeEventListener('sortChanged', listener);;
}, []);
return (
<span className="my-header" onClick={onClick}>
<img style={{ width: '30px', height: '30px' }} src="https://d1yk6z6emsz7qy.cloudfront.net/static/images/loading.gif" className="my-spinner" />
{props.displayName} {sortState}
</span>
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>([
{ value: 14, type: 'age' },
{ value: 'female', type: 'gender' },
{ value: 'Happy', type: 'mood' },
{ value: 21, type: 'age' },
{ value: 'male', type: 'gender' },
{ value: 'Sad', type: 'mood' },
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'value',
//指定该列的行头render
headerComponent: SortingHeader,
//对该列使用一个指定的render
cellRenderer: ValueRender,
cellRendererParams: {
title: "Fish",
headerName: 'Rendered Value',
field: 'value',
cellRendererSelector: (params: ICellRendererParams) => {
//根据cell的值不同,采用不用的Render
const moodDetails = {
component: MoodRenderer,
const genderDetails = {
component: GenderRenderer,
params: { values: ['Male', 'Female'] },
if (params.data.type === 'gender') return genderDetails;
else if (params.data.type === 'mood') return moodDetails;
else return undefined;
{ field: 'type' },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
context={{
'Name': "Cat",
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
></AgGridReact>
export default GridExample;
要点如下:
AgGrid的可编辑表格需要理解以下几点:
其实这种模式与Excel的默认设计也是一样的,可以仔细对比一下
要点如下:
其他的一些API
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
CellValueChangedEvent,
ColDef,
ColGroupDef,
Grid,
GridOptions,
ValueGetterParams,
ValueParserParams,
ValueSetterParams,
} from 'ag-grid-community';
function getData() {
var rowData = [];
var firstNames = ['Niall', 'John', 'Rob', 'Alberto', 'Bas', 'Dimple', 'Sean'];
var lastNames = [
'Pink',
'Black',
'White',
'Brown',
'Smith',
'Smooth',
'Anderson',
for (var i = 0; i < 100; i++) {
rowData.push({
a: Math.floor(Math.random() * 100),
b: Math.floor(Math.random() * 100),
firstName: firstNames[i % firstNames.length],
lastName: lastNames[i % lastNames.length],
return rowData;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
headerName: 'Name',
//getter从data中拉取
valueGetter: function (params: ValueGetterParams) {
return params.data.firstName + ' ' + params.data.lastName;
//从输入数据中拉取data,返回true代表有所改变,返回false代表没有改变
//valueSetter解决的是数据如何保存原始数据的问题,需要保存到数据的哪些字段中
//valueGetter是valueSetter的逆过程,从原始数据中提取数据
valueSetter: function (params: ValueSetterParams) {
var fullName = params.newValue;
var nameSplit = fullName.split(' ');
var newFirstName = nameSplit[0];
var newLastName = nameSplit[1];
var data = params.data;
if (data.firstName !== newFirstName || data.lastName !== newLastName) {
data.firstName = newFirstName;
data.lastName = newLastName;
// return true to tell grid that the value has changed, so it knows
// to update the cell
return true;
} else {
// return false, the grid doesn't need to update
return false;
headerName: 'FirstName',
field: 'firstName',
headerName: 'LastName',
field: 'lastName',
headerName: 'Age',
field: 'age',
//默认的输入数据为string,需要用parser转换为number
//valueParser主要解决的是数据如何获取的问题,从editorValue返回到value
//valueFormatter是valueParser的过程,它描述的是从value到showValue的转换
valueParser: function (params: ValueParserParams) {
return Number.parseInt(params.newValue);
headerName: 'B',
valueGetter: function (params: ValueGetterParams) {
return params.data.b;
valueSetter: function (params: ValueSetterParams) {
var newValInt = parseInt(params.newValue);
var valueChanged = params.data.b !== newValInt;
if (valueChanged) {
params.data.b = newValInt;
return valueChanged;
headerName: 'C.X',
valueGetter: function (params: ValueGetterParams) {
if (params.data.c) {
return params.data.c.x;
} else {
return undefined;
valueSetter: function (params: ValueSetterParams) {
if (!params.data.c) {
params.data.c = {};
params.data.c.x = params.newValue;
return true;
headerName: 'C.Y',
valueGetter: function (params: ValueGetterParams) {
if (params.data.c) {
return params.data.c.y;
} else {
return undefined;
valueSetter: function (params: ValueSetterParams) {
if (!params.data.c) {
params.data.c = {};
params.data.c.y = params.newValue;
return true;
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
resizable: true,
editable: true,
}, []);
const onCellValueChanged = useCallback((event: CellValueChangedEvent) => {
console.log('Data after change is', event.data);
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onCellValueChanged={onCellValueChanged}
></AgGridReact>
export default GridExample;
要点如下:
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { ICellEditorParams } from 'ag-grid-community';
import './style.css';
const KEY_BACKSPACE = 'Backspace';
const KEY_DELETE = 'Delete';
const KEY_ENTER = 'Enter';
const KEY_TAB = 'Tab';
export default forwardRef((props: ICellEditorParams, ref) => {
const createInitialState = () => {
let startValue;
if (props.key === KEY_BACKSPACE || props.key === KEY_DELETE) {
// if backspace or delete pressed, we clear the cell
startValue = '';
} else if (props.charPress) {
// if a letter was pressed, we start with the letter
startValue = props.charPress;
} else {
// otherwise we start with the current value
startValue = props.value;
return {
value: startValue,
const initialState = createInitialState();
const [value, setValue] = useState(initialState.value);
const refInput = useRef<HTMLInputElement>(null);
// focus on the input
useEffect(() => {
// get ref from React component
window.setTimeout(() => {
const eInput = refInput.current!;
eInput.focus();
eInput.select();
}, 0);
}, []);
/* Component Editor Lifecycle methods */
useImperativeHandle(ref, () => {
return {
// the final value to send to the grid, on completion of editing
getValue() {
return value;
// Gets called once before editing starts, to give editor a chance to
// cancel the editing before it even starts.
//默认情况下,输入字母和数字都会进入到编辑状态
//isCancelBeforeStart可以指定只有输入数字的时候才进入到编辑状态
isCancelBeforeStart() {
return false;
// Gets called once when editing is finished (eg if Enter is pressed).
// If you return true, then the result of the edit will be ignored.
//当数据大于某个值的时候,取消返回数据
isCancelAfterEnd() {
// will reject the number if it greater than 1,000,000
// not very practical, but demonstrates the method.
return false;
return (
<input
ref={refInput}
className={'my-input'}
value={value}
onChange={(event: any) => setValue(event.target.value)}
我们先自定义一个NumberEditor,注意如何初始化数据,以及使用useImperativeHandle来返回数据
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
CellEditingStartedEvent,
CellEditingStoppedEvent,
ColDef,
ColGroupDef,
Grid,
GridOptions,
ICellEditorParams,
RowEditingStartedEvent,
RowEditingStoppedEvent,
} from 'ag-grid-community';
import getData from './data';
import numberEditor from './numberEditor';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'name',
editable: true,
cellEditor: numberEditor,
//Enter事件默认会被Ag-Grid处理掉,不会冒泡到触发位置
//Enter事件默认会被Ag-Grid处理为进入和退出编辑状态的方法
//但是当cellEditor本身也需要去处理Enter事件的时候,就会出现问题
suppressKeyboardEvent: (params) => {
if (!params.editing) return false
if (params.event.key == 'Enter') {
return true;
} else {
return false;
field: 'age',
editable: true,
cellEditor: numberEditor,
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
}, []);
const onRowEditingStarted = useCallback((event: RowEditingStartedEvent) => {
console.log('never called - not doing row editing');
}, []);
const onRowEditingStopped = useCallback((event: RowEditingStoppedEvent) => {
console.log('never called - not doing row editing');
}, []);
const onCellEditingStarted = useCallback((event: CellEditingStartedEvent) => {
console.log('cellEditingStarted');
}, []);
const onCellEditingStopped = useCallback((event: CellEditingStoppedEvent) => {
console.log('cellEditingStopped');
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onRowEditingStarted={onRowEditingStarted}
onRowEditingStopped={onRowEditingStopped}
onCellEditingStarted={onCellEditingStarted}
onCellEditingStopped={onCellEditingStopped}
singleClickEdit={true}
//stopEditingWhenCellsLoseFocus={true}
></AgGridReact>
export default GridExample;
然后我们使用cellEditor来注入自己的Editor,注意,如果组件中需要处理Enter事件,那么就需要用suppressKeyboardEvent来避免Enter事件被AgGrid处理掉了。
10.4 enter切换焦点
代码看这里
使用Enter键切换焦点的方法,就不贴代码了。
10.5 外部数据源更新
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
CellEditRequestEvent,
ColDef,
ColGroupDef,
GetRowIdFunc,
GetRowIdParams,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
let rowImmutableStore: any[];
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'athlete', minWidth: 160 },
{ field: 'age' },
{ field: 'country', minWidth: 140 },
{ field: 'year' },
{ field: 'date', minWidth: 140 },
{ field: 'sport', minWidth: 160 },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 100,
editable: true,
}, []);
const getRowId = useCallback((params: GetRowIdParams) => params.data.id, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => {
data.forEach((item, index) => (item.id = index));
rowImmutableStore = data;
setRowData(rowImmutableStore);
}, []);
const onCellEditRequest = useCallback(
(event: CellEditRequestEvent) => {
//数据本身
const data = event.data;
//字段名
const field = event.colDef.field;
//字段值
const newValue = event.newValue;
const newItem = { ...data };
newItem[field!] = event.newValue;
console.log('onCellEditRequest, updating ' + field + ' to ' + newValue);
rowImmutableStore = rowImmutableStore.map((oldItem) =>
oldItem.id == newItem.id ? newItem : oldItem
setRowData(rowImmutableStore);
[rowImmutableStore]
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
getRowId={getRowId}
//默认editor是原地修改数据的,打开readOnlyEdit以后,数据不能原地修改,而是通过发送请求来修改
readOnlyEdit={true}
onGridReady={onGridReady}
//cellEditRequest,数据更改后事件
onCellEditRequest={onCellEditRequest}
></AgGridReact>
默认情况下,AgGrid会将新数据写入到data的字段本身,也就是原地更新。当打开readOnlyEdit开关以后,AgGrid就不会去直接修改数据,然后通过事件回调来通知数据被更改了,onCellEditRequest就是数据被更新的通知。
11 分组
11.1 分组显示
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState([
//对country列进行分组rowGroup为true,分组后该列不消失hide为false
{ field: 'country', rowGroup: true, hide: false },
{ field: 'athlete', rowGroup: true, hide: false },
{ field: 'year' },
//分组后的合并函数,聚合函数
{ field: 'gold', aggFunc: 'sum' },
{ field: 'silver', aggFunc: 'sum' },
{ field: 'bronze', aggFunc: 'sum' },
{ field: 'total', aggFunc: 'sum' },
{ field: 'age' },
{ field: 'date' },
{ field: 'sport' },
const defaultColDef = useMemo(() => {
return {
flex: 1,
minWidth: 150,
resizable: true,
}, []);
const autoGroupColumnDef = useMemo(() => {
return {
headerName: 'MyGroup',
minWidth: 300,
cellRendererParams: {
//默认在分组列显示数字
suppressCount: false,
}, []);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//生成的分组列
autoGroupColumnDef={autoGroupColumnDef}
//分组的显示方式,将所有分组合并到一列来显示,这个方法好看一点
groupDisplayType={'singleColumn'}
//分组后,分组列数据是否保留,hide为false的话不需要设置这一个
//showOpenedGroup={true}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
要点如下:
使用rowGroup与rowIndex,来设置默认分组
使用aggFunc,来设置分组后的聚合函数列
autoGroupColumnDef,分组后会自动新增一个分组列,显示当前的分组列信息
groupDisplayType,分组的显示方式有多种,将多个分组合并到一列(singleColumn),以及每个分组一个列(multiplyColumn)
11.2 顶部分组工具栏
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100%' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState();
const [columnDefs, setColumnDefs] = useState([
field: 'country',
//打开了enableRowGroup才能放入groupPanel中
enableRowGroup: true,
field: 'athlete',
enableRowGroup: true,
field: 'year',
enableRowGroup: true,
//分组后的合并函数
{ field: 'gold', aggFunc: 'sum' },
{ field: 'silver', aggFunc: 'sum' },
{ field: 'bronze', aggFunc: 'sum' },
{ field: 'total', aggFunc: 'sum' },
{ field: 'age' },
{ field: 'date' },
field: 'sport',
const defaultColDef = useMemo(() => {
return {
flex: 1,
minWidth: 150,
resizable: true,
sortable: true,
unSortIcon: true,
}, []);
const onGridReady = useCallback((params) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => setRowData(data));
}, []);
const autoGroupColumnDef = useMemo(() => {
return {
headerName: 'MyGroup',
minWidth: 300,
//似乎没啥用
// enables filtering on the group column
//filter: true,
}, []);
return (
<div style={{ display: 'flex', width: '100%', height: '100vh' }}>
<div className="ag-theme-alpine" style={{ height: '100%', width: '100%' }}>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//showOpenedGroup={true}
onGridReady={onGridReady}
autoGroupColumnDef={autoGroupColumnDef}
//打开自定义group面板,并保留列
//顶部的分组列,可以通过拖动列的方式自定义分组
rowGroupPanelShow={'always'}
suppressDragLeaveHidesColumns={true}
suppressMakeColumnVisibleAfterUnGroup={true}
//子合计,没啥用,别用
//groupIncludeFooter={true}
//主合计
groupIncludeTotalFooter={true}
animateRows={true}
></AgGridReact>
export default GridExample;
要点如下:
要点如下:
要点如下:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
IAggFunc,
IAggFuncParams,
SideBarDef,
} from 'ag-grid-community';
function oneTwoThreeFunc(params: IAggFuncParams) {
// this is just an example, rather than working out an aggregation,
// we just return 123 each time, so you can see in the example 22 is the result
return 123;
function xyzFunc(params: IAggFuncParams) {
// this is just an example, rather than working out an aggregation,
// we just return 22 each time, so you can see in the example 22 is the result
return 'xyz';
// sum function has no advantage over the built in sum function.
// it's shown here as it's the simplest form of aggregation and
// showing it can be good as a starting point for understanding
// hwo the aggregation functions work.
function sumFunction(params: IAggFuncParams) {
let result = 0;
params.values.forEach((value) => {
if (typeof value === 'number') {
result += value;
return result;
// min and max agg function. the leaf nodes are just numbers, like any other
// value. however the function returns an object with min and max, thus the group
// nodes all have these objects.
function minAndMaxAggFunction(params: IAggFuncParams) {
// this is what we will return
const result = {
min: null,
max: null,
//聚合函数,可以返回一个类型,但是要定义toString方法
// because we are returning back an object, this would get rendered as [Object,Object]
// in the browser. we could get around this by providing a valueFormatter, OR we could
// get around it in a customer cellRenderer, however this is a trick that will also work
// with clipboard.
toString: function () {
return '(' + this.min + '..' + this.max + ')';
// update the result based on each value
params.values.forEach((value) => {
const groupNode =
value !== null && value !== undefined && typeof value === 'object';
const minValue = groupNode ? value.min : value;
const maxValue = groupNode ? value.max : value;
// value is a number, not a 'result' object,
// so this must be the first group
result.min = min(minValue, result.min);
result.max = max(maxValue, result.max);
return result;
// the average function is tricky as the multiple levels require weighted averages
// for the non-leaf node aggregations.
function avgAggFunction(params: IAggFuncParams) {
// the average will be the sum / count
let sum = 0;
let count = 0;
params.values.forEach((value) => {
const groupNode =
value !== null && value !== undefined && typeof value === 'object';
if (groupNode) {
//在聚合函数中,判断是否为分组的方法
// we are aggregating groups, so we take the
// aggregated values to calculated a weighted average
sum += value.avg * value.count;
count += value.count;
} else {
// skip values that are not numbers (ie skip empty values)
if (typeof value === 'number') {
sum += value;
count++;
// avoid divide by zero error
let avg = null;
if (count !== 0) {
avg = sum / count;
// the result will be an object. when this cell is rendered, only the avg is shown.
// however when this cell is part of another aggregation, the count is also needed
// to create a weighted average for the next level.
const result = {
count: count,
avg: avg,
// the grid by default uses toString to render values for an object, so this
// is a trick to get the default cellRenderer to display the avg value
toString: function () {
return `${this.avg}`;
return result;
function roundedAvgAggFunction(params: IAggFuncParams) {
const result = avgAggFunction(params);
if (result.avg) {
result.avg = Math.round(result.avg * 100) / 100;
return result;
// similar to Math.min() except handles missing values, if any value is missing, then
// it returns the other value, or 'null' if both are missing.
function min(a: any, b: any) {
const aMissing = typeof a !== 'number';
const bMissing = typeof b !== 'number';
if (aMissing && bMissing) {
return null;
} else if (aMissing) {
return b;
} else if (bMissing) {
return a;
} else if (a > b) {
return b;
} else {
return a;
// similar to Math.max() except handles missing values, if any value is missing, then
// it returns the other value, or 'null' if both are missing.
function max(a: any, b: any) {
const aMissing = typeof a !== 'number';
const bMissing = typeof b !== 'number';
if (aMissing && bMissing) {
return null;
} else if (aMissing) {
return b;
} else if (bMissing) {
return a;
} else if (a < b) {
return b;
} else {
return a;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: 'country', rowGroup: true, hide: true },
{ field: 'year', rowGroup: true, hide: true },
// this column uses min and max func
{ headerName: 'minMax(age)', field: 'age', aggFunc: minAndMaxAggFunction },
// here we use an average func and specify the function directly
headerName: 'avg(age)',
field: 'age',
//指定默认的聚合函数
aggFunc: avgAggFunction,
//是否允许用户在sideBar自定义执行聚合函数
enableValue: true,
minWidth: 200,
headerName: 'roundedAvg(age)',
field: 'age',
aggFunc: roundedAvgAggFunction,
enableValue: true,
minWidth: 200,
// here we use a custom sum function that was registered with the grid,
// which overrides the built in sum function
headerName: 'sum(gold)',
field: 'gold',
aggFunc: 'sum',
enableValue: true,
// and these two use the built in sum func
headerName: 'abc(silver)',
field: 'silver',
aggFunc: '123',
enableValue: true,
headerName: 'xyz(bronze)',
field: 'bronze',
aggFunc: 'xyz',
enableValue: true,
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
filter: true,
sortable: true,
resizable: true,
}, []);
const autoGroupColumnDef = useMemo<ColDef>(() => {
return {
headerName: 'Athlete',
field: 'athlete',
minWidth: 250,
}, []);
//定义自己的分组函数,分组函数只能对单列进行操作
const aggFuncs = useMemo<{
[key: string]: IAggFunc;
}>(() => {
return {
// this overrides the grids built in sum function
sum: sumFunction,
// this adds another function called 'abc'
'123': oneTwoThreeFunc,
// and again xyz
xyz: xyzFunc,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
// we could also register functions after the grid is created,
// however because we are providing the columns in the grid options,
// it will be to late (eg remove 'xyz' from aggFuncs, and you will
// see the grid complains).
params.api.addAggFunc('xyz', xyzFunc);
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
enableRangeSelection={true}
//默认在HeaderName中包含了聚合函数的名称,可以指定用这个来显示原名称
suppressAggFuncInHeader={true}
//定义自己的聚合函数,用户可以在sideBar中选择
aggFuncs={aggFuncs}
sideBar={true}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
要点如下:
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
IAggFuncParams,
ValueFormatterParams,
ValueGetterParams,
} from 'ag-grid-community';
const numberFormatter: (params: ValueFormatterParams) => string = (
params: ValueFormatterParams
) => {
if (!params.value || params.value === 0) return '0';
return '' + Math.round(params.value * 100) / 100;
function ratioValueGetter(params: ValueGetterParams) {
if (!(params.node && params.node.group)) {
// no need to handle group levels - calculated in the 'ratioAggFunc'
return createValueObject(params.data.gold, params.data.silver);
function ratioAggFunc(params: IAggFuncParams) {
let goldSum = 0;
let silverSum = 0;
params.values.forEach((value) => {
if (value && value.gold) {
goldSum += value.gold;
if (value && value.silver) {
silverSum += value.silver;
return createValueObject(goldSum, silverSum);
function createValueObject(gold: number, silver: number) {
return {
gold: gold,
silver: silver,
//输出的时候才做除法操作
toString: () => `${gold && silver ? gold / silver : 0}`,
function ratioFormatter(params: ValueFormatterParams) {
if (!params.value || params.value === 0) return '';
return '' + Math.round(params.value * 100) / 100;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'country',
rowGroup: true,
hide: true,
//该列不允许在sideBar中进行手工调整
suppressColumnsToolPanel: true,
field: 'sport',
rowGroup: true,
hide: true,
suppressColumnsToolPanel: true,
field: 'year',
pivot: true,
hide: true,
suppressColumnsToolPanel: true,
{ field: 'gold', aggFunc: 'sum', valueFormatter: numberFormatter },
{ field: 'silver', aggFunc: 'sum', valueFormatter: numberFormatter },
headerName: 'Ratio',
colId: 'goldSilverRatio',
//多列聚合,首先要做的是,将多列数据转换为object数据
valueGetter: ratioValueGetter,
//然后,指定我们的聚合函数,注意聚合函数的输入和输出都是object
aggFunc: ratioAggFunc,
//最后将结果展示出来,将object,转换为string
valueFormatter: ratioFormatter,
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
sortable: true,
filter: true,
}, []);
const autoGroupColumnDef = useMemo<ColDef>(() => {
return {
minWidth: 220,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => setRowData(data));
}, []);
return (
<div style={containerStyle}>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
suppressAggFuncInHeader={true}
onGridReady={onGridReady}
sideBar={true}
></AgGridReact>
export default GridExample;
大部分的聚合函数都是关于单列里面的数据,例如是sum,max,min,avg。但是少量情况下,聚合函数的计算是基于多个列的。例如计算订单的平均单价,它需要计算订单总金额的sum,以及订单总数量的sum,然后两者相除。
多列聚合函数,AgGrid使用一个较为取巧的办法来实现,没有提供额外的机制。
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
GetDataPath,
Grid,
GridOptions,
} from 'ag-grid-community';
import { getData } from './data';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
//显示数据只有jobTitle与employmentType两个列
{ field: 'jobTitle' },
{ field: 'employmentType' },
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
}, []);
//自动生成的分组列,每个元素为dataPath里面的数据
const autoGroupColumnDef = useMemo<ColDef>(() => {
return {
headerName: 'Organisation Hierarchy',
minWidth: 300,
cellRendererParams: {
suppressCount: true,
}, []);
//返回数据的树形地址
const getDataPath = useCallback((data: any) => {
return data.orgHierarchy;
}, []);
const onFilterTextBoxChanged = useCallback(() => {
gridRef.current!.api.setQuickFilter(
(document.getElementById('filter-text-box') as any).value
}, []);
return (
<div style={containerStyle}>
<div className="example-wrapper">
<div style={{ marginBottom: '5px' }}>
<input
type="text"
id="filter-text-box"
placeholder="Filter..."
onInput={onFilterTextBoxChanged}
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
//定义自动生成的列
autoGroupColumnDef={autoGroupColumnDef}
//打开treeData,getDataPath,getDataPath是返回每个row树形数据的地址
treeData={true}
animateRows={true}
//默认展开所有group
groupDefaultExpanded={-1}
getDataPath={getDataPath}
></AgGridReact>
export default GridExample;
AgGrid对树形数据的支持更为彻底,输入的是一个扁平的rowData数据,输出展示的是树形的数据。前提是,每个rowData都提供了自己的完整路径信息。
'use strict';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
GetDataPath,
GetRowIdFunc,
GetRowIdParams,
Grid,
GridOptions,
ICellRendererComp,
ICellRendererParams,
RowNode,
} from 'ag-grid-community';
import { getData } from './data2';
declare var window: any;
function getNextId() {
if (!window.nextId) {
window.nextId = 15;
} else {
window.nextId++;
return window.nextId;
const FileCellRenderer: React.FC<ICellRendererParams> = (props) => {
var icon = getFileIcon(props.value);
const value = props.value;
return (<div>
{icon ? <span>
<i className={icon}></i>
<span className="filename"></span>{value}
</span>
: value}
</div>);
//删除的时候,连子树都一起删除
function getRowsToRemove(node: RowNode) {
var res: any[] = [];
const children = node.childrenAfterGroup || [];
for (var i = 0; i < children.length; i++) {
res = res.concat(getRowsToRemove(children[i]));
// ignore nodes that have no data, i.e. 'filler groups'
return node.data ? res.concat([node.data]) : res;
function isSelectionParentOfTarget(selectedNode: RowNode, targetNode: RowNode) {
var children = selectedNode.childrenAfterGroup || [];
for (var i = 0; i < children.length; i++) {
if (targetNode && children[i].key === targetNode.key) return true;
isSelectionParentOfTarget(children[i], targetNode);
return false;
//设置node的filePath,递归设置
function getRowsToUpdate(node: RowNode, parentPath: string[]) {
var res: any[] = [];
var newPath = parentPath.concat([node.key!]);
if (node.data) {
// groups without data, i.e. 'filler groups' don't need path updated
node.data.filePath = newPath;
var children = node.childrenAfterGroup || [];
for (var i = 0; i < children.length; i++) {
var updatedChildRowData = getRowsToUpdate(children[i], newPath);
res = res.concat(updatedChildRowData);
// ignore nodes that have no data, i.e. 'filler groups'
return node.data ? res.concat([node.data]) : res;
function getFileIcon(name: string) {
return endsWith(name, '.mp3') || endsWith(name, '.wav')
? 'far fa-file-audio'
: endsWith(name, '.xls')
? 'far fa-file-excel'
: endsWith(name, '.txt')
? 'far fa-file'
: endsWith(name, '.pdf')
? 'far fa-file-pdf'
: 'far fa-folder';
function endsWith(str: string | null, match: string | null) {
var len;
if (str == null || !str.length || match == null || !match.length) {
return false;
len = str.length;
return str.substring(len - match.length, len) === match;
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: '100%', height: '100vh' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
const [rowData, setRowData] = useState<any[]>(getData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
field: 'dateModified',
minWidth: 250,
comparator: (d1, d2) => {
return new Date(d1).getTime() < new Date(d2).getTime() ? -1 : 1;
field: 'size',
aggFunc: 'sum',
valueFormatter: (params) => {
return params.value
? Math.round(params.value * 10) / 10 + ' MB'
: '0 MB';
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
filter: true,
sortable: true,
resizable: true,
}, []);
const autoGroupColumnDef = useMemo<ColDef>(() => {
return {
headerName: 'Files',
minWidth: 330,
cellRendererParams: {
suppressCount: true,
//自定义一个FileCellRenderer
innerRenderer: FileCellRenderer,
}, []);
const getDataPath = useCallback((data: any) => {
return data.filePath;
}, []);
const getRowId = useCallback((params: GetRowIdParams) => {
return params.data.id;
}, []);
const addNewGroup = useCallback(() => {
//添加到一个固定的filePath位置
var newGroupData = [
id: getNextId(),
filePath: ['Music', 'wav', 'hit_' + new Date().getTime() + '.wav'],
dateModified: 'Aug 23 2017 11:52:00 PM',
size: 58.9,
gridRef.current!.api.applyTransaction({ add: newGroupData });
}, []);
const removeSelected = useCallback(() => {
var selectedNode = gridRef.current!.api.getSelectedNodes()[0]; // single selection
if (!selectedNode) {
console.warn('No nodes selected!');
return;
//删除该node以及node下面children的数据
gridRef.current!.api.applyTransaction({
remove: getRowsToRemove(selectedNode),
}, []);
const moveSelectedNodeToTarget = useCallback((targetRowId: string) => {
var selectedNode = gridRef.current!.api.getSelectedNodes()[0]; // single selection
if (!selectedNode) {
console.warn('No nodes selected!');
return;
var targetNode = gridRef.current!.api.getRowNode(targetRowId)!;
var invalidMove =
selectedNode.key === targetNode.key ||
isSelectionParentOfTarget(selectedNode, targetNode);
if (invalidMove) {
console.warn('Invalid selection - must not be parent or same as target!');
return;
var rowsToUpdate = getRowsToUpdate(selectedNode, targetNode.data.filePath);
gridRef.current!.api.applyTransaction({ update: rowsToUpdate });
}, []);
return (
<div style={containerStyle}>
<div className="example-wrapper">
<div style={{ marginBottom: '5px' }}>
<button onClick={addNewGroup}>Add New Group</button>
<button onClick={() => moveSelectedNodeToTarget('9')}>
Move Selected to 'stuff'
</button>
<button onClick={removeSelected}>Remove Selected</button>
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
treeData={true}
animateRows={true}
groupDefaultExpanded={-1}
rowSelection={'single'}
getDataPath={getDataPath}
getRowId={getRowId}
></AgGridReact>
export default GridExample;
要点如下:
在 这里 ,AgGrid也提供了如何对树形数据进行拖动的实现。
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
} from 'ag-grid-community';
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<(ColDef | ColGroupDef)[]>([
headerName: 'Group A',
children: [
{ field: 'athlete', minWidth: 200 },
{ field: 'country', minWidth: 200 },
headerName: 'Group B',
children: [
{ field: 'sport', minWidth: 150 },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const defaultColDef = useMemo<ColDef>(() => {
return {
sortable: true,
filter: true,
resizable: true,
minWidth: 100,
flex: 1,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/small-olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => {
setRowData(data);
}, []);
const onBtExport = useCallback(() => {
//exportDataAsExcel直接导出
gridRef.current!.api.exportDataAsExcel();
//getDataAsExcel返回Blob对象
//gridRef.current!.api.getDataAsExcel();
}, []);
return (
<div style={containerStyle}>
<button
onClick={onBtExport}
style={{ marginBottom: '5px', fontWeight: 'bold' }}
Export to Excel
</button>
<div className="grid-wrapper">
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
></AgGridReact>
export default GridExample;
AgGrid提供了获取Excel的blob功能
使用AgGrid的导出功能,好处在于,所见即所得的导出Excel,不需要额外的代码开发。
'use strict';
import React, { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import { render } from 'react-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import {
ColDef,
ColGroupDef,
Grid,
GridOptions,
GridReadyEvent,
ExcelCell,
ExcelStyle,
} from 'ag-grid-community';
const getRows: () => ExcelCell[][] = () => [
//第一个空行
//第二个行
styleId: 'coverHeading',
data: {
value: 'Here is a comma, and a some "quotes".',
type: 'String'
//第三个行
data: {
value:
'They are visible when the downloaded file is opened in Excel because custom content is properly escaped.',
type: 'String',
data: { value: 'this cell:', type: 'String' },
//合并两列
mergeAcross: 1
styleId: 'coverText',
data: {
value: 'is empty because the first cell has mergeAcross=1',
type: 'String',
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo<CSSProperties>(() => ({ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }), []);
const gridStyle = useMemo(() => ({ height: '100%', width: '100%', flex: '1' }), []);
const [rowData, setRowData] = useState<any[]>();
const [columnDefs, setColumnDefs] = useState<(ColDef | ColGroupDef)[]>([
headerName: 'Group A',
children: [
{ field: 'athlete', minWidth: 200 },
{ field: 'country', minWidth: 200 },
headerName: 'Group B',
children: [
{ field: 'sport', minWidth: 150 },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
const excelStyles = useMemo<ExcelStyle[]>(() => {
return [
id: 'coverHeading',
font: {
italic: true,
size: 26,
bold: true,
underline: 'Single',
id: 'coverText',
font: {
size: 14,
}, []);
const defaultColDef = useMemo<ColDef>(() => {
return {
sortable: true,
filter: true,
resizable: true,
minWidth: 100,
flex: 1,
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch('https://www.ag-grid.com/example-assets/small-olympic-winners.json')
.then((resp) => resp.json())
.then((data: any[]) => {
setRowData(data);
}, []);
const onBtExport = useCallback(() => {
//exportDataAsExcel直接导出
gridRef.current!.api.exportDataAsExcel({
sheetName: "表单名",
prependContent: getRows(),
appendContent: getRows(),
//getDataAsExcel返回Blob对象
//gridRef.current!.api.getDataAsExcel();
}, []);
return (
<div style={containerStyle}>
<button
onClick={onBtExport}
style={{ marginBottom: '5px', fontWeight: 'bold' }}
Export to Excel
</button>
<div className="grid-wrapper">
<div style={gridStyle} className="ag-theme-alpine">
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
excelStyles={excelStyles}
></AgGridReact>
export default GridExample;
要点如下:
额外的内容就是一个简单的二维数组
看11.4有聚合图表的功能,不多说了
AgGrid有跳转到指定行或者指定列的方法。
一个重要,但没有做Demo的功能是,多表格对齐,看 这里
以上介绍的仅为AgGrid的主要功能,大概还有40%的功能没有覆盖到。从这次学习中,我们得到了:
![]() |
个性的火柴 · 房屋抵押权与优先购买权并存时的处理-中新网 6 月前 |