Ag-grid editable grid adding new row dynamically

1k Views Asked by At

I have an editable AgGrid in my functional component as below. On the last column, I have buttons to Add/Remove rows. Now I want the Add row to be displayed only for the last row. I am using cellRenderer for the last column.

With the below change, I get the Add button for the last row (i.e. on 2nd row in my case) on initial render. But if I click on Add for this 2nd row, while I get the Add button for the new 3rd row, but it does not get removed for the 2nd row. not sure if I am implementing this in the wrong way.

const MyCmp = (props) => {

    const getData = () => {
        return [{
            id: 0,
            firstName: 'F1',
            lastName: 'L1'
        }, {
            id: 1,
            firstName: 'F2',
            lastName: 'L2',
        }];
    }

    const [myCols, setMyCols] = useState(null);
    const [gridData, setGridData] = useState(getData());

    const [gridApi, setGridApi] = useState('');  

    let cellRules = {
        'rag-red': params => {
            if (params.data.lastName === 'INVALID_VAL') {
                return true;
            }
        }
    };

    const handleGridReady = (params) => {
        setGridApi(params.api);
        setMyCols([{
            headerName: 'F Name',
            field: 'firstName',
            editable: true
        }, {
            headerName: 'L Name',
            field: 'lastName',
            cellClassRules: cellRules,
            editable: true
        }, {
            headerName: '',
            field: 'buttonCol',
            cellRenderer: 'customColRenderer',
            cellRendererParams: {
                addItems: addItems
            }
        }]
        );
    };

    const createNewRowData = () => {
        const newData = {
            id: newCount,
            firstName: '',
            lastName: ''
        };
        newCount++;
        return newData;
    }

    let newCount = getData().length;

    const addItems = (addIndex, props) => {
        const newItems = [createNewRowData()];
        const res = props.api.applyTransaction({
            add: newItems,
            addIndex: addIndex,
        });

        setGridData(...gridData, res.add[0].data); // IS THIS CORRECT ?

        if (props.api.getDisplayedRowCount() > props.api.paginationGetPageSize()) {
            props.api.paginationGoToPage(parseInt((props.api.getDisplayedRowCount() / props.api.paginationGetPageSize())) + 1);
        }
    }

    const onCellClicked = (e) => {
    }

    const frameworkComponents = () => {
        return {customColRenderer: customColRenderer}
    }

    return (
        <>
            <MyAgGrid
                    id="myGrid"
                    columnDefs={myCols}
                    rowData={gridData}
                    frameworkComponents={{customColRenderer: customColRenderer}}
                    {...props}
                />
        </>
    )
}

My customColRenderer is as below;

export default (props) => {
        let isLastRow = (props.rowIndex === (props.api.getDisplayedRowCount() -1)) ? true: false;
        const addItems = (addIndex) => {
            props.addItems(addIndex, props);
        }

        return (
            <span>
                {isLastRow ? <button onClick={() => addItems()}>Add</button> : null}
                <span><button onClick={() => props.api.applyTransaction({remove: props.api.getSelectedRows()})}>Remove</button>
            </span>
        );
};
1

There are 1 best solutions below

0
On

Within the AgGrid React internals a transaction is generated automatically when your rowData is updated, as such you can choose to apply the transaction either through the api, or by updating your state - you shouldn't need to do both (as you're currently doing). Generally with React I'd suggest updating the state to keep your state true to the data displayed in the grid, however that can depend on use case.

As for the further issue of your cell not updating - that'll be due to the fact AgGrid has detected no change in the 'value' of the cell, as it attempts to reduce the amount of unnecessary cell rendering done.

You could attempt to call:

api.refreshCells({ force: true });

After your api call applying the transaction (I'm not sure where this would need to happen using the setState approach).