---
title: "Saving Values"
framework: vue
version: "36.1.0"
---

# Saving Values

After editing a cell, the grid normally inserts the new value into your data using the column definition `field` attribute. This covers the most common case, where the grid owns the data state and treats the data as mutable.

This page discusses alternatives to this approach.

[Value Setters](https://www.ag-grid.com/vue-data-grid/value-setters/#value-setter) provides an alternative to using `field` for setting the data. Use `valueSetter` if you want the grid to manage the data (ie update the data inline) but you want to update in a way other than using `field`. This is useful if you are not using `field`, or somehow need to manipulate the data in another way (e.g. the data item isn't a simple key / value pair map, but contains a more complex structure).

[Read Only Edit](https://www.ag-grid.com/vue-data-grid/value-setters/#read-only-edit) stops the grid from updating data, and relies on the application to make the update after the edit is complete. Use this if you want to manage the grid data state externally, such as in a Redux store.

## Value Setter

A Value Setter is the inverse of a [Value Getter](https://www.ag-grid.com/vue-data-grid/value-getters/). Where the value getter allows getting values from your data using a function rather than a field, the value setter allows you to set values into your data using a function rather than specifying a field.

```ts
<ag-grid-vue
    :columnDefs="columnDefs"
    /* other grid options ... */>
</ag-grid-vue>

this.columnDefs = [
    // Option 1: using field for getting and setting the value
    { field: 'name' },

    // Options 2: using valueGetter and valueSetter - value getter used to get data
    {
        valueGetter: params => {
            return params.data.name;
        },
        valueSetter: params => {
            params.data.name = params.newValue;
            return true;
        }
    }
];
```

A value setter should return `true` if the value was updated successfully and `false` if the value was not updated (including if the value was not changed). When you return `true`, the grid knows it must refresh the cell.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `valueSetter` | `string \| ValueSetterFunc` |  |  | Function or [expression](https://www.ag-grid.com/vue-data-grid/cell-expressions/#column-definition-expressions). Custom function to modify your data based off the new value for saving. Return `true` if the data changed. |

The example below demonstrates value setters working alongside value getters (value setters are typically only used alongside value getters). Note the following:

- All columns are editable. After an edit, the example prints the updated row data to the console to show the impact of the edit.
- Column Name uses `valueGetter` to combine the values from the two attributes `firstName` and `lastName` (separated by a space) and `valueSetter` is used to break the value up into the two same attributes.
- Column A uses `field` for both getting and setting the value. This is the simple case for comparison.
- Column B uses `valueGetter` and `valueSetter` instead of field for getting and setting the value.
- Column C.X and C.Y use `valueGetter` to get the value from an embedded object. They then use `valueSetter` to set the value into the embedded object while also making sure the correct structure exists (this structure creation would not happen if using field).

#### Value Setters

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  CellValueChangedEvent,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  ValueGetterParams,
  ValueSetterParams,
  enableDevValidations,
} from "ag-grid-community";
import { getData } from "./data";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextEditorModule,
  NumberEditorModule,
  ClientSideRowModelModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"
      @cell-value-changed="onCellValueChanged"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<ColDef[]>([
      {
        headerName: "Name",
        valueGetter: (params: ValueGetterParams) => {
          return params.data.firstName + " " + params.data.lastName;
        },
        valueSetter: (params: ValueSetterParams) => {
          const fullName = params.newValue || "";
          const nameSplit = fullName.split(" ");
          const newFirstName = nameSplit[0];
          const newLastName = nameSplit[1];
          const 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: "A",
        field: "a",
      },
      {
        headerName: "B",
        valueGetter: (params: ValueGetterParams) => {
          return params.data.b;
        },
        valueSetter: (params: ValueSetterParams) => {
          const newVal = params.newValue;
          const valueChanged = params.data.b !== newVal;
          if (valueChanged) {
            params.data.b = newVal;
          }
          return valueChanged;
        },
        cellDataType: "number",
      },
      {
        headerName: "C.X",
        valueGetter: (params: ValueGetterParams) => {
          if (params.data.c) {
            return params.data.c.x;
          } else {
            return undefined;
          }
        },
        valueSetter: (params: ValueSetterParams) => {
          const newVal = params.newValue;
          if (!params.data.c) {
            params.data.c = {};
          }
          const valueChanged = params.data.c.x !== newVal;
          if (valueChanged) {
            params.data.c.x = newVal;
          }
          return valueChanged;
        },
        cellDataType: "number",
      },
      {
        headerName: "C.Y",
        valueGetter: (params: ValueGetterParams) => {
          if (params.data.c) {
            return params.data.c.y;
          } else {
            return undefined;
          }
        },
        valueSetter: (params: ValueSetterParams) => {
          const newVal = params.newValue;
          if (!params.data.c) {
            params.data.c = {};
          }
          const valueChanged = params.data.c.y !== newVal;
          if (valueChanged) {
            params.data.c.y = newVal;
          }
          return valueChanged;
        },
        cellDataType: "number",
      },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      editable: true,
    });
    const rowData = ref<any[] | null>(getData());

    function onCellValueChanged(event: CellValueChangedEvent) {
      console.log("Data after change is", event.data);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
      onCellValueChanged,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Value Setters](https://www.ag-grid.com/examples/value-setters/example-setters/vue3)

## Read Only Edit

Read Only Edit is a mode in the grid whereby Cell Editing will not update the data inside the grid. Instead the grid fires `cellEditRequest` events allowing the application to process the update request. To enable this mode, set the grid property `readOnlyEdit=true`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cellEditRequest` | `CellEditRequestEvent` |  |  | Value has changed after editing. Only fires when `readOnlyEdit=true`. |

```ts
<ag-grid-vue
    :readOnlyEdit="true"
    @cell-edit-request="onCellEditRequest"
    /* other grid options ... */>
</ag-grid-vue>

this.onCellEditRequest = event => {
    console.log('Cell Editing updated a cell, but the grid did nothing!');
    // the application should update the data somehow
};
```

The example below has Cell Editing enabled, however the editing does nothing because `readOnlyEdit=true` is set. The application listens for `cellEditRequest` event and prints to the console. As the application does not try to update the data, the cell keeps its old value, giving the impression that editing is not working.

#### Read Only Edit - Not Implemented

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  CellEditRequestEvent,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="example-wrapper">
      <div class="example-header">
        <span>Example only logs cellEditRequests to the console, so edits are not saved to the grid.</span>
      </div>
      <ag-grid-vue
        style="width: 100%; height: 100%;"
        @grid-ready="onGridReady"
        :columnDefs="columnDefs"
        :defaultColDef="defaultColDef"
        :readOnlyEdit="true"
        :rowData="rowData"
        @cell-edit-request="onCellEditRequest"></ag-grid-vue>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<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 = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      editable: true,
    });
    const rowData = ref<IOlympicData[]>(null);

    function onCellEditRequest(event: CellEditRequestEvent) {
      console.log("onCellEditRequest, new value = " + event.newValue);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
      onCellEditRequest,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Read Only Edit - Not Implemented](https://www.ag-grid.com/examples/value-setters/read-only/vue3)

This next example extends the above by getting the application to update the data.

1. The application listens for `cellEditRequest` and updates the Row Data.
2. The Row Data has IDs and `getRowId` is implemented. This allows the grid to only refresh the desired row after new Row Data is set.

#### Read Only Edit - Row Data

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  CellEditRequestEvent,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicDataWithId } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
]);

let rowImmutableStore: any[];

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :getRowId="getRowId"
      :readOnlyEdit="true"
      :rowData="rowData"
      @cell-edit-request="onCellEditRequest"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicDataWithId> | null>(null);
    const columnDefs = ref<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 = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      editable: true,
    });
    const getRowId = ref<GetRowIdFunc>((params: GetRowIdParams) =>
      String(params.data.id),
    );
    const rowData = ref<IOlympicDataWithId[]>(null);

    function onCellEditRequest(event: CellEditRequestEvent) {
      const data = event.data;
      const field = event.colDef.field;
      const newValue = event.newValue;
      const oldItem = rowImmutableStore.find((row) => row.id === data.id);
      if (!oldItem || !field) {
        return;
      }
      const newItem = { ...oldItem };
      newItem[field] = newValue;
      console.log("onCellEditRequest, updating " + field + " to " + newValue);
      rowImmutableStore = rowImmutableStore.map((oldItem) =>
        oldItem.id == newItem.id ? newItem : oldItem,
      );
      gridApi.value!.setGridOption("rowData", rowImmutableStore);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        data.forEach((item, index) => (item.id = index));
        rowImmutableStore = data;
        params.api!.setGridOption("rowData", rowImmutableStore);
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      getRowId,
      rowData,
      onGridReady,
      onCellEditRequest,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Read Only Edit - Row Data](https://www.ag-grid.com/examples/value-setters/read-only-row-data/vue3)

This final example is similar to before, except it uses Transactions to update the data after the edit rather than updating the whole Row Data.

#### Read Only Edit - Transactions

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  CellEditRequestEvent,
  ClientSideRowModelApiModule,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicDataWithId } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelApiModule,
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :getRowId="getRowId"
      :readOnlyEdit="true"
      :rowData="rowData"
      @cell-edit-request="onCellEditRequest"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicDataWithId> | null>(null);
    const columnDefs = ref<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 = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      editable: true,
    });
    const getRowId = ref<GetRowIdFunc>(
      (params: GetRowIdParams) => params.data.id,
    );
    const rowData = ref<IOlympicDataWithId[]>(null);

    function onCellEditRequest(event: CellEditRequestEvent) {
      const oldData = event.data;
      const field = event.colDef.field;
      const newValue = event.newValue;
      const newData = { ...oldData };
      newData[field!] = event.newValue;
      console.log("onCellEditRequest, updating " + field + " to " + newValue);
      const tx = {
        update: [newData],
      };
      event.api.applyTransaction(tx);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        data.forEach((item, index) => (item.id = String(index)));
        rowData.value = data;
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      getRowId,
      rowData,
      onGridReady,
      onCellEditRequest,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Read Only Edit - Transactions](https://www.ag-grid.com/examples/value-setters/read-only-transactions/vue3)
