I'm trying to populate an ag-grid component with data coming from a Vuex getter. The only remarkable thing to mention is that this data coming from the getter is asynchronous. When I debug the grid component I can see that the data from the getter is there but the rowData property from the grid is still empty and I get this error in the console:
This warning:
runtime-core.esm-bundler.js:38 [Vue warn]: Unhandled error during execution of mounted hook at <Anonymous class="ag-theme-alpine" id="myGrid" columnDefs= {value: Array(7)} ... > at at <DesignSystem onVnodeUnmounted=fn ref=Ref< Proxy {callWebsocket: ƒ, sendMessage: ƒ, …} > > at at at <ConfigProvider theme-overrides= {common: {…}, Checkbox: {…}, Radio: {…}, Button: {…}, Switch: {…}} theme=null > at
This Error:
Uncaught (in promise) TypeError: this.gridOptions.columnDefs.forEach is not a function at GridOptionsWrapper2.checkColumnDefProperties (ag-grid-community.cjs.js:18300:37) at GridOptionsWrapper2.init (ag-grid-community.cjs.js:18240:18) at ag-grid-community.cjs.js:1113:76 at Array.forEach () at Context2.callLifeCycleMethodsOnBean (ag-grid-community.cjs.js:1113:24) at ag-grid-community.cjs.js:1096:70 at Array.forEach () at Context2.callLifeCycleMethods (ag-grid-community.cjs.js:1096:23) at Context2.wireBeans (ag-grid-community.cjs.js:975:14) at new Context2 (ag-grid-community.cjs.js:953:14)
Here's my component template:
<template>
<div class="test-container">
<ag-grid-vue
class="ag-theme-alpine"
id="myGrid"
:columnDefs="columnDefs"
domLayout="autoHeight"
unSortIcon="true"
@grid-ready="onGridReady"
:animateRows="true"
:rowData="rowData"
:defaultColDef="defaultColDef"
>
</ag-grid-vue>
</div>
</template>
And here's my setup function (I'm using Vue's 3 Composition API):
import { AgGridVue } from "ag-grid-vue3";
import { useActions, useGetters } from "vuex-composition-helpers";
import { reactive, onMounted, ref } from "vue";
export default {
components: {
AgGridVue,
},
setup() {
const gridApi = ref(null);
const onGridReady = (params) => {
gridApi.value = params.api;
};
const rowData = reactive([]);
const columnDefs = reactive({
value: [
{ field: "brandId", sortable: true, checkboxSelection: true },
{ field: "category" },
{ field: "channel", headerName: "Brand" },
{ field: "channelReferenceId", headerName: "Requested" },
{ field: "id", headerName: "Updated" },
{ field: "status", headerName: "Subject" },
{ field: "subject", headerName: "Requester" },
],
});
const defaultColDef = {
sortable: true,
filter: true,
flex: 1,
};
const { fetchTickets } = useActions({
fetchTickets: "tickets/fetchTickets",
});
const { tickets } = useGetters({
tickets: "tickets/getRowData",
});
function getTickets() {
fetchTickets();
}
getTickets();
onMounted(() => {
rowData.value = tickets.value;
});
return {
onGridReady,
columnDefs,
rowData,
defaultColDef,
tickets,
getTickets,
};
},
As you can see I'm using the vuex-composition-helpers in order to fetch Tickets from an API and then use a getter that will be the main source of data to populate the ag-grid.
I finally came up with a solution for this, for some reason (that to be honest I still don't understand) you have to pass the ag-grid prop value to the component as "rowData.value" (I thought you don't need to specify it that way on the template). So this is the way to implement the component in the template:
<ag-grid-vue
:icons="icons"
class="ag-theme-alpine"
id="myGrid"
:columnDefs="columnDefs.value"
domLayout="autoHeight"
unSortIcon="true"
@grid-ready="onGridReady"
:animateRows="true"
:rowData="rowData.value"
>
And then this is the setup function using composition API:
<script>
import { AgGridVue } from "ag-grid-vue3";
import { useActions, useGetters } from "vuex-composition-helpers";
import { reactive, onMounted, onBeforeMount, ref } from "vue";
export default {
components: {
AgGridVue,
},
setup() {
const gridApi = ref(null);
const onGridReady = (params) => {
gridApi.value = params.api;
gridApi.value.sizeColumnsToFit();
};
const rowData = reactive([]);
const columnDefs = reactive({
value: [
{ field: "brandId", sortable: true, checkboxSelection: true },
{ field: "channel", sortable: true, suppressSizeToFit: true },
{ field: "channelReferenceId", sortable: true },
{ field: "createdOn", sortable: true, suppressSizeToFit: true },
{ field: "status", sortable: true, suppressSizeToFit: true },
{ field: "subject", sortable: true },
],
});
const defaultColDef = {
sortable: true,
filter: true,
flex: 1,
};
const { fetchTickets } = useActions({
fetchTickets: "tickets/fetchTickets",
});
const { tickets } = useGetters({
tickets: "tickets/getRowData",
});
function getTickets() {
fetchTickets();
}
getTickets();
const icons = ref(null);
const columnApi = ref(null);
onBeforeMount(() => {
icons.value = {
sortAscending: '<i class="fas fa-sort-alpha-down text-blue-400"></i>',
sortDescending: '<i class="fas fa-sort-alpha-up text-blue-400"></i>',
sortUnSort: '<i class="fas fa-sort text-blue-400"></i>',
};
});
onMounted(() => {
rowData.value = tickets;
});
return {
onGridReady,
columnDefs,
tickets,
getTickets,
rowData,
defaultColDef,
icons,
columnApi,
};
},
};
</script>
Important things to note:
declare rowData as an array (not an object like the ag-grid website does):
const rowData = reactive([]);
After setting up all the getters, etc and fetching the information:
onMounted(() => {
rowData.value = tickets;
});