In this post, we'll show you how to enable users to add new data rows using a pinned row at the top of the grid. This way new rows can be added right inside the grid without using separate UI just for adding new rows.

We've built a sample to illustrate this - please see it in action below:

Live Example

See the live sample in JavaScript below and links to the same sample in other frameworks just below it. In the live sample below, click inside the pinned row just below the header row and enter cell values to add a new record to the grid.

Contents

Configuring the pinned row

In order to display an empty pinned row at the top, we provide data for it by setting the pinnedTopRowData array as follows:

var pinnedTopRowData = {};

var gridOptions = {
  pinnedTopRowData: [pinnedTopRowData],
...
}

We start by providing an empty object to store the data for the new row to be added. As the user enters cell values for each column in the pinned row, AG Grid updates the pinnedTopRowData with the latest inputs.

Placeholders

In the cells of the pinned empty row we are displaying placeholders to indicate to the user that they can add a new row by typing there. We display the values of these placeholders using a value formatter in the default column definitions. This allows to format the cell values without affecting the underlying data.

Please see the implementation below:

var gridOptions = {
...
  defaultColDef: {
    flex: 1,
    width: 200,
    sortable: true,
    filter: true,
    resizable: true,
    editable: true,
    valueFormatter: (params) => {
      if (isEmptyPinnedCell(params)) {
        return createPinnedCellPlaceholder(params);
      }
    },
  },
...
}

The value formatter uses the isEmptyPinnedCell function to identify only empty top row cells and creates placeholder values for them using the createdPinnedCellPlaceholder function.

Styling

We have styled the pinned row so that it stands out. The getRowStyle callback allows us to style each row individually, and we use it to apply a separate style to the pinned top row used for adding new rows in the grid. See the code below:

var gridOptions = {
...
  getRowStyle: function (params) {
    if (params.node.rowPinned) {
      return { 'font-weight': 'bold', 'font-style': 'italic' };
    }
  },
...
};

This code produces the following styled version of the top pinned row:

Advanced features

Now that we've seen how to build a row with simple editors, let's look into how to set up advanced editors and renderers.

Let's look at how to display a dropdown inside the pinned row used for adding new rows. Say we'd like to allow the user to select the value of a Gender column using a dropdown editor.

See this defined below - the column uses a renderer to show the male/female icon and we're using the same renderer for the dropdown editor:

var columnDefs = [
    ...
  {
    field: 'gender',
    cellRenderer: 'genderCellRenderer',
    cellEditor: 'agRichSelectCellEditor',

    cellEditorParams: {
      values: ['Male', 'Female'],
      cellRenderer: 'genderCellRenderer',
    },
  },
...
]

This helps us build the dropdown shown below:

Date picker

Let's look into how we can display a date picker inside a date column. The sample above uses a custom date component, and you can see the code for this yourself as its implementation depends on the framework you're using .

See below a screenshot of the live example showing the date picker in the pinned row:

Listening to check if there is a new row to be added

We listen to changes in the top row so that when the user enters all the cell values in it, we add a new row to the bottom of the grid. We do this by listening to the cellEditingStopped event, as shown below:

onCellEditingStopped: (params) => {
    if (params.rowPinned === 'top') {
      if (isPinnedRowDataCompleted()) {
        let currentRowData = [];
        gridOptions.api.forEachNode(({ data }) => currentRowData.push(data));
        let newRowData = [...currentRowData, pinnedTopRowData];
        gridOptions.api.setRowData(newRowData);
        resetPinnedData();
        gridOptions.api.setPinnedTopRowData([pinnedTopRowData]);
      }
    }
  },

Note we ensure the pinned row data has entries in all cells. We do this in isPinnedRowDataCompleted function as shown below:

function isPinnedRowDataCompleted() {
  let keys = columnDefs.map((columnDef) => columnDef.field);
  for (let key of keys) {
    if (pinnedTopRowData[key] == null) {
      return false;
    }
  }
  return true;
}

Once isPinnedRowDataCompleted is true, we add the new data and reset the pinned row data.

Adding the new row to the grid

Once the user has finished entering new values in the pinned row, we add the new data row to the grid in the following way:

  • We copy the current row data shown in the grid
  • We add the new data row at the end of the current row data
  • We provide the updated data to the grid
let currentRowData = [];
gridOptions.api.forEachNode(({ data }) => currentRowData.push(data));
let newRowData = [...currentRowData, pinnedTopRowData];
gridOptions.api.setRowData(newRowData);

Note there are other ways to update AG Grid row data. To learn more, please check our documentation on Updating Data or check the example for the framework of your choice.

Resetting the pinned row

Once the new data row is added to the grid, we clear the top pinned row by assigning an empty object and passing it to the grid as shown below:

pinnedTopRowData = {};
gridOptions.api.setPinnedTopRowData([pinnedTopRowData]);

This allows the now empty pinned row to be used to add a new row of data.

Summary

This blogpost showed you how to add new rows to AG Grid using a pinned row on the top of the grid. This provides a safe way to add new data without creating additional UI.

We hope this can enhance your AG Grid applications and help you deliver a better user experience.

Happy Coding!