Our blazing fast Grid component built with pure JavaScript


Post by peachaws »

Dear Bryntum Team,

We are currently using Bryntum Grid v6.2.0 with Vue 3 and encountering an issue with sorting the datetime column when the grid is bound using mock AJAX data.

Issue:
We need to sort dates in the format: MM/dd/YYYY hh:mm AM/PM.

Although we convert the created_at field to a date object (new Date(item.created_at)), sorting still treats it as a string.

Interestingly, sorting works fine in the Tree Grid, but breaks in the Paging Grid.

Example Scenario for Better Understanding:

<bryntum-grid ref="childGrid" :config="gridConfig" :height="'80vh'" />
this.gridConfig = {
      tbar: {
        items: [
          {
            type: "Combo",
            label: "Item Per Page",
            items: this.itemSize,
            value: this.itemSize[1],
            editable: false,
            style: {
              float: "right",
              position: "absolute",
              top: 10,
              right: 10,
              width: "200px",
            },
            onChange: ({ value }) => {
              this.$refs.childGrid.instance.value.store.pageSize = value;
            },
          },
        ],
      },
      bbar: {
        type: "pagingtoolbar",
      },
      columns: [
        {
          order: 1,
          field: "Id",
          text: "#",
          width: 20,
          default: true,
          action: false,
        },
        {
          order: 2,
          field: "PageName",
          text: "Page Name",
          autoWidth: true,
          default: true,
          action: false,
        },
        {
          order: 3,
          field: "Suggestions",
          text: "Suggestions",
          autoWidth: true,
          default: true,
          action: false,
        },
        {
          order: 4,
          field: "SuggestionTypeDes",
          text: "Suggestion Type",
          autoWidth: true,
          default: true,
          action: false,
        },
        {
          order: 5,
          field: "username",
          text: "Suggested By",
          autoWidth: true,
          default: true,
          action: true,
        },
        {
          order: 6,
          text: "Date",
          field: "created_at",
          type: "date",
          autoWidth: true,
          default: false,
          action: false,
          format: "MM/DD/YY hh:mm A",
        },
        {
          order: 7,
          field: "sgstatus",
          text: "Current Status",
          autoWidth: true,
          default: true,
          action: false,
          filterable: {
            filterField: {
              type: "combo",
              valueField: "value",
              displayField: "label",
              cls: "issues-combo",
              picker: { cls: "ccstatus-list", allowGroupSelect: false },
              store: {
                data: [
                  { label: "Approved", value: "1" },
                  { label: "On-Hold", value: "2" },
                  { label: "Pending", value: "3" },
                  { label: "Denies", value: "0" },
                ],
              },
              multiSelect: false,
            },
          },
          renderer: ({ value, cellElement }) => {
            const cstatus = this.getCurrentStatus(value);
            let cssClass = "";
            if (cstatus === "Denies") {
              cssClass = "sg-denies";
            } else if (cstatus === "Approved") {
              cssClass = "sg-approve";
            } else if (cstatus === "On-Hold") {
              cssClass = "sg-onhold";
            } else {
              cssClass = "sg-waiting";
            }
            cellElement.classList.add(cssClass);

        return cstatus;
      },
    },
    {
      order: 11,
      field: "LastNote",
      text: "Last Note",
      autoWidth: true,
      default: true,
      action: false,
    },
    {
      text: "Notes",
      collapsible: true,
      collapsed: true,
      default: true,
      action: true,
      children: [
        {
          order: 10,
          field: "NotesGroup",
          text: "Notes Group",
          autoWidth: true,
          default: true,
          action: false,
        },
        {
          order: 8,
          field: "LastAddedBy",
          text: "Last Added By",
          autoWidth: true,
          default: true,
          action: false,
        },
        {
          order: 9,
          field: "AssignedTo",
          text: "Assigned To",
          autoWidth: true,
          default: true,
          action: false,
        },
      ],
    },
    {
      order: 12,
      field: "sgstatus",
      region: "right",
      text: "sgstatus",
      width: 120,
      default: true,
      action: false,
      type: "widget",
      widgets: [
        {
          type: "combo",
          items: this.statusOptions, // The data for your combo box
          readOnly: true, // Make it read-only
          editable: false,
          onSelect: ({ source: combo }) => {
            const { record } = combo.cellInfo;
            const originalValue = record.sg_status;
            if (originalValue !== combo.value) {
              this.$swal
                .fire({
                  title: "Are you sure?",
                  text: "You want to update the status!",
                  icon: "warning",
                  showCancelButton: true,
                  confirmButtonColor: "#3085d6",
                  cancelButtonColor: "#d33",
                  confirmButtonText: "Yes!",
                })
                .then((result) => {
                  if (result.isConfirmed) {
                    this.updateSgStatus(
                      combo.value,
                      record.Id,
                      record.parentIndex
                    );
                  } else {
                    combo.value = originalValue;
                  }
                });
            }
          },
        },
      ],
    },
    {
      order: 13,
      field: "filename",
      text: "File(s)",
      autoWidth: true,
      region: "right",
      filterable: false,
      sortable: false,
      callOnFunctions: true,
      default: true,
      action: false,
      renderer: ({ record, cellElement }) => {
        const link = record.data.link;
        const filenames = record.data.link
          ? record.data.link.split(",")
          : [];

        // Check if both link and at least one filename are not null or undefined
        if (link !== null && link !== undefined && filenames.length > 0) {
          cellElement.innerHTML = "";
          for (const filename of filenames) {
            let isPdf = filename.trim().toLowerCase().endsWith(".pdf");
            const buttonConfig = isPdf
              ? {
                  cls: "b-widget b-button b-raised b-rounded b-green",
                  icon: "b-icon b-fa-file-pdf",
                  tooltip: "Open PDF",
                  onClick: () => this.showmodeldocument(filename, record),
                }
              : {
                  cls: "b-widget b-button b-raised b-rounded b-green",
                  icon: "b-icon b-fa-image",
                  tooltip: "Open File",
                  onClick: () => this.showmodeldocument(filename, record),
                };
            // Create a button element using plain HTML
            const button = document.createElement("button");
            button.className = buttonConfig.cls;
            button.innerHTML = `<i class="${buttonConfig.icon}"></i>`;
            button.title = buttonConfig.tooltip;
            button.addEventListener("click", buttonConfig.onClick);
            // Clear the cell content before appending the button

            // Append the button element to the cell
            cellElement.appendChild(button);
          }
        } else {
          // If link or no filenames are provided, clear the cell content
          cellElement.innerHTML = "";
        }
      },
      style: "margin-right: 5px;",
    },
    {
      field: "show_edit",
      text: "Edit",
      width: 40,
      region: "right",
      default: true,
      action: true,
      type: "widget",
      widgets: [
        {
          type: "button",
          cls: "b-raised b-blue b-rounded",
          icon: "b-icon b-fa-pen",
          onClick: ({ source: btn }) => {
            const { record } = btn.cellInfo;
            this.review(record);
          },
        },
      ],
      renderer: ({ record }) => {
        if (!this.userHasPermission(["can view suggestions"])) {
          return "";
        }
      },
    },
    {
      field: "show_delete",
      text: "",
      width: 40,
      default: true,
      action: true,
      type: "widget",
      widgets: [
        {
          type: "button",
          cls: "b-raised b-red b-rounded",
          icon: "b-icon b-fa-trash",
          onClick: ({ source: btn }) => {
            const { record } = btn.cellInfo;
            this.deleteRecord(record.Id);
          },
        },
      ],
      renderer: ({ record }) => {
        if (!this.userHasPermission(["can delete suggestions"])) {
          return "";
        }
      },
    },
  ],
  features: {
    filterBar: true,
    cellMenu: false,
    cellEdit: false,
    stripe: true,
    quickFind: true,
    regionResize: true,
    rowExpander: {
      // This will put the expander button as the last column // Expander column configs
      column: {
        width: 120,
        align: "center",
        readOnly: true,
      },
      widget: {
        type: "container",
        cls: "action-panel",
        items: [
          {
            type: "textfield",
            ref: "txtnotes",
            flex: 3,
            placeholder: "Notes",
            value: "",
            width: 20,
          },
          {
            type: "combo",
            flex: 3,
            ref: "cmbgroup",
            label: "Notes  Group",
            items: this.selectedOption,
            placeholder: "Select Notes Group",

            editable: false,
            onSelect: ({ source: combo, record }) => {
              const selectedValue = combo?.value;

              // Avoid executing the logic if the value is null
              if (selectedValue === null || selectedValue === undefined) {
                return;
              }

              this.bindUsers(combo, selectedValue, record.parentIndex);
            },
          },
          {
            type: "combo",
            ref: "cmbuser",
            flex: 3,
            label: "Assigned To",
            valueField: "id",
            textField: "text",
            placeholder: "Select Employee",

            editable: false,
          },
          {
            type: "button",
            text: "Submit",
            flex: 1,
            ref: "btn1",
            cls: "b-raised b-blue",
            onClick: ({ source }) => {
              let vm = this;
              const { rowExpander } = source.up("grid").features,
                record = rowExpander.getExpandedRecord(source.owner);
              const notesData = source.closest("container").widgetMap;
              vm.onNoteSubmit(record, 0, notesData);
            },
          },
          {
            type: "button",
            ref: "btn2",
            flex: 1,
            text: "Submit & Notify",
            cls: "b-raised b-green",
            onClick: ({ source }) => {
              let vm = this;
              const { rowExpander } = source.up("grid").features,
                record = rowExpander.getExpandedRecord(source.owner);
              const notesData = source.closest("container").widgetMap;
              vm.onNoteSubmit(record, 1, notesData);
            },
          },
          {
            type: "grid",
            ref: "notesgrid",
            cls: "col-md-12 custom-notes-grid",
            height: 200,
            features: {
              headerMenu: false,
              cellEdit: false,
              cellMenu: false,
            },
            columns: [
              { text: "Assigned To", field: "NameAssign", flex: 1 },
              { text: "Note", field: "Des", flex: 1 },
              { text: "Added By", field: "Name", flex: 1 },
              {
                text: "Date",
                field: "DateUpdate",
                flex: 1,
                renderer: ({ record, value }) => {
                  if (!value) return "";

                  // Assume input is in UTC: "2025-05-26 05:44:23.407"
                  // Add 'Z' to tell JS it's a UTC timestamp
                  const utcString = value.replace(" ", "T") + "Z"; // --> "2025-05-26T05:44:23.407Z"
                  const d = new Date(utcString); // Now JS treats it as UTC and converts to local

                  const year = d.getFullYear();
                  const month = String(d.getMonth() + 1).padStart(2, "0");
                  const day = String(d.getDate()).padStart(2, "0");

                  let hours = d.getHours();
                  const minutes = String(d.getMinutes()).padStart(2, "0");
                  const ampm = hours >= 12 ? "pm" : "am";

                  hours = hours % 12 || 12;

                  const formattedDate = `${month}-${day}-${year} ${hours}:${minutes} ${ampm}`;

                  // Optional: set formatted date to record field
                  record.set("Date", formattedDate);

                  return formattedDate;
                },
              },
            ],
            store: {
              data: [],
            },
          },
        ],
      },
      listeners: {
        expand: async (source) => {
          try {
            const cmbGroup = source.widget.items.find(
              (item) => item.ref === "cmbgroup"
            );
            cmbGroup.items = this.selectedOption;

            const { record } = source;
            const notesgrid = source.widget.items.find(
              (item) => item.ref === "notesgrid"
            );
            const response = await this.getNotes(record);
            //bind the subgrid inside rowexpander
            notesgrid.store.data = response.data.content;
            console.log("Row expanded successfully.");
          } catch (error) {
            console.error("Error expanding row:", error);
          }
        },
      },
    },
  },
  store: {
    readUrl: "/pagedMockUrl",
    pageParamName: "page",
    sortParamName: "sort",
    filterParamName: "filter",
    pageSize: this.pageSize,
    autoLoad: false,
    proxy: true,
  },
};
CreateStore() {
      AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
        // Mock data generation logic
        const page = parseInt(params.page, 10);
        const pageSize = parseInt(params.pageSize, 10);
        const startIdx = (page - 1) * pageSize;

    let returnedData = this.gridData.map((item) => {
      return {
        ...item,
        Id: Number(item.Id),
      };
    });

    // Filter the data if filter parameter is passed
    if (params.filter) {
      returnedData = returnedData.filter(
        CollectionFilter.generateFiltersFunction(
          JSON.parse(params.filter).map((f) => {
            f.property = f.field;
            return new CollectionFilter(f);
          })
        )
      );
    }
    // Sort the data if sort parameter is passed
    if (params.sort) {
      const gridComponent = this.$refs.childGrid;
      returnedData.sort(
        gridComponent.instance.value.store.createSorterFn(
          JSON.parse(params.sort)
        )
      );
    }
    return {
      responseText: JSON.stringify({
        success: true,
        total: returnedData.length,
        data: returnedData.slice(startIdx, startIdx + pageSize),
      }),
    };
  });
},
sorting.png
sorting.png (141.18 KiB) Viewed 246 times

Can you please help us resolve this sorting issue for the Paging Grid? Any insights or workaround would be greatly appreciated!

Thanks in advance!


Post by tasnim »

Hi,

The issue you're experiencing with sorting the datetime column in the Paging Grid might be related to how the data is being processed and sent back from the mock AJAX data. When using paging, ensure that the data returned from the server is correctly formatted as Date objects, not strings.

Here are a few steps you can take to resolve the issue:

  1. Ensure Date Conversion: Make sure that the created_at field is converted to a Date object before sorting. You can do this in your mock data generation logic by using new Date(item.created_at).

  2. Check Sorter Function: Verify that the sorter function used in the mock data is correctly handling Date objects. The sorter should compare Date objects directly, not strings.

  3. Use DateColumn Type: Ensure that the column is configured with the correct type. You are using type: "date" which is correct, but make sure the data being passed to the grid is indeed Date objects.

  4. Debugging: Add console logs to check the type of the created_at field before sorting. This will help ensure that the data is being processed correctly.

Here's a simplified example of how you might ensure the data is correctly formatted:

AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
    const page = parseInt(params.page, 10);
    const pageSize = parseInt(params.pageSize, 10);
    const startIdx = (page - 1) * pageSize;

    let returnedData = this.gridData.map((item) => {
        return {
            ...item,
            Id: Number(item.Id),
            created_at: new Date(item.created_at) // Ensure conversion to Date object
        };
    });

    // Sorting logic
    if (params.sort) {
        const gridComponent = this.$refs.childGrid;
        returnedData.sort(
            gridComponent.instance.value.store.createSorterFn(
                JSON.parse(params.sort)
            )
        );
    }

    return {
        responseText: JSON.stringify({
            success: true,
            total: returnedData.length,
            data: returnedData.slice(startIdx, startIdx + pageSize),
        }),
    };
});

If the issue persists, please provide more details or a code snippet showing how the data is being processed and sorted. This will help in diagnosing the problem further.

Best regards,
Tasnim

How to ask for help? Please read our Support Policy


Post by peachaws »

tasnim wrote: Tue Jun 10, 2025 9:21 am

Hi,

The issue you're experiencing with sorting the datetime column in the Paging Grid might be related to how the data is being processed and sent back from the mock AJAX data. When using paging, ensure that the data returned from the server is correctly formatted as Date objects, not strings.

Here are a few steps you can take to resolve the issue:

  1. Ensure Date Conversion: Make sure that the created_at field is converted to a Date object before sorting. You can do this in your mock data generation logic by using new Date(item.created_at).

  2. Check Sorter Function: Verify that the sorter function used in the mock data is correctly handling Date objects. The sorter should compare Date objects directly, not strings.

  3. Use DateColumn Type: Ensure that the column is configured with the correct type. You are using type: "date" which is correct, but make sure the data being passed to the grid is indeed Date objects.

  4. Debugging: Add console logs to check the type of the created_at field before sorting. This will help ensure that the data is being processed correctly.

Here's a simplified example of how you might ensure the data is correctly formatted:

AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
    const page = parseInt(params.page, 10);
    const pageSize = parseInt(params.pageSize, 10);
    const startIdx = (page - 1) * pageSize;

    let returnedData = this.gridData.map((item) => {
        return {
            ...item,
            Id: Number(item.Id),
            created_at: new Date(item.created_at) // Ensure conversion to Date object
        };
    });

    // Sorting logic
    if (params.sort) {
        const gridComponent = this.$refs.childGrid;
        returnedData.sort(
            gridComponent.instance.value.store.createSorterFn(
                JSON.parse(params.sort)
            )
        );
    }

    return {
        responseText: JSON.stringify({
            success: true,
            total: returnedData.length,
            data: returnedData.slice(startIdx, startIdx + pageSize),
        }),
    };
});

If the issue persists, please provide more details or a code snippet showing how the data is being processed and sorted. This will help in diagnosing the problem further.

Hi Tasnim,

Thank you for your response. We attempted to follow your advice regarding the date object conversion. However, as shown in the code below, when we apply JSON.stringify, the date object is no longer recognized as such and is instead converted to a string. It would be very helpful if you could provide a demo example in Vue 3 with mocked AJAX. I appreciate any assistance you can offer.

responseText: JSON.stringify({

AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
    const page = parseInt(params.page, 10);
    const pageSize = parseInt(params.pageSize, 10);
    const startIdx = (page - 1) * pageSize;

let returnedData = this.gridData.map((item) => {
    return {
        ...item,
        Id: Number(item.Id),
        created_at: new Date(item.created_at) // Ensure conversion to Date object
    };
});

// Sorting logic
if (params.sort) {
    const gridComponent = this.$refs.childGrid;
    returnedData.sort(
        gridComponent.instance.value.store.createSorterFn(
            JSON.parse(params.sort)
        )
    );
}

return {
       responseText: JSON.stringify({
        success: true,
        total: returnedData.length,
        data: returnedData.slice(startIdx, startIdx + pageSize),
    }),
};
});

Post by alex.l »

Hi,

Make sure you set correct data model field types for your store https://bryntum.com/products/grid/docs/api/Core/data/Model

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by peachaws »

alex.l wrote: Tue Jun 10, 2025 4:51 pm

Hi,

Make sure you set correct data model field types for your store https://bryntum.com/products/grid/docs/api/Core/data/Model

Hi Marcio,
We have set the field types as date only, see below code snippet, but the issue happens when it's converting the data to JSON.stringify inside AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {}

{
          order: 6,
          text: "Date",
          field: "created_at",
          type: "date",
          autoWidth: true,
          default: false,
          action: false,
          format: "MM/DD/YY hh:mm A",
        },

Below is the sample example for you to check, help us with the way to make it work with the date format sorting.

<template>
  <CCard class="cash-schedules-table main-container p-0 m-0">
    <CCardBody>
      <CRow>
        <CCol>
          <bryntum-grid :config="myconfig" ref="childGrid" />
        </CCol>
      </CRow>
    </CCardBody>
  </CCard>
</template>

<script>
import { BryntumGrid } from "@bryntum/grid-vue-3";
import { AjaxHelper, CollectionFilter } from "@bryntum/grid";

export default {
  name: "CreditCardCollectionsExample",
  components: {
    BryntumGrid,
  },
  data() {
    const itemSize = [10, 20, 50];
    return {
      items: [
        {
          id: 21631,
          Brnid: "102",
          file_name: " Recon_2025_06_09_po_XYZ.csv",
          created_at_date: "2025-06-09T05:00:00.000Z",
          f_status: "Not Started",
          is_receipt_created: "2",
        },
        {
          id: 21627,
          Brnid: "102",
          file_name: " Recon_2025_06_06_po_ABC.csv",
          created_at_date: "2025-06-06T05:00:00.000Z",
          f_status: "Receipt Posted",
          is_receipt_created: "1",
        },
        // You can add more records here (copy from your big array)
      ],
      myconfig: {
        cls: "grid-container",
        height: "80vh",
        pageSize: itemSize[1],
        columns: [
          {
            field: "id",
            text: "ID",
            order: 1,
            autoWidth: true,
          },
          {
            field: "file_name",
            text: "File Name",
            order: 2,
            flex: 1,
          },
          {
            field: "f_status",
            text: "Status",
            order: 3,
            width: 150,
            renderer: ({ value }) => {
              let statusClass = value
                ? this.getBadge(value)
                : this.getBadge("Not Started");
              return {
                class: `badge bg-${statusClass}`,
                text: value,
              };
            },
          },
          {
            field: "created_at_date",
            text: "Downloaded At",
            order: 4,
            width: 140,
            type: "date",
            dateFormat: "YYYY-MM-DD",
          },
          {
            field: "",
            text: "Action",
            width: 70,
            order: 5,
            type: "widget",
            widgets: [
              {
                type: "button",
                cls: "b-raised b-blue",
                icon: "b-icon b-fa-eye",
                onClick: ({ source: btn }) => {
                  const { record } = btn.cellInfo;
                  this.viewFileDetails(record);
                },
              },
            ],
            renderer({ record, widgets }) {
              const data =
                record.data.is_receipt_created == 1 ||
                record.data.is_receipt_created == 2;
              widgets[0].hidden = !data;
              widgets[0].tooltip = "View";
            },
          },
        ],
        store: {
          readUrl: "/pagedMockUrl",
          pageParamName: "page",
          sortParamName: "sort",
          filterParamName: "filter",
          pageSize: itemSize[1],
          autoLoad: true,
          proxy: true,
        },
        bbar: {
          type: "pagingtoolbar",
        },
        tbar: {
          items: [
            {
              type: "Combo",
              label: "Item Per Page",
              items: itemSize,
              value: itemSize[1],
              editable: false,
              placeholder: "select",
              style: {
                float: "right",
                position: "absolute",
                top: 10,
                right: 10,
                width: "200px",
              },
              onChange: ({ value }) => {
                this.$refs.childGrid.instance.value.store.pageSize = value;
                this.$refs.childGrid.instance.value.store.currentPage = 1;
                this.$refs.childGrid.instance.value.store.load();
              },
            },
          ],
        },
        features: {
          filterBar: true,
          cellMenu: false,
          cellEdit: false,
        },
      },
    };
  },
  methods: {
    createStore() {
      AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
        const page = parseInt(params.page, 10) || 1;
        const pageSize =
          parseInt(params.pageSize, 10) || this.myconfig.store.pageSize;
        const startIdx = (page - 1) * pageSize;

    let returnedData = this.items.map((item) => {
      const dateObj = item.created_at_date
        ? new Date(item.created_at_date)
        : null;
      return {
        ...item,
        id: Number(item.id),
        created_at_date: dateObj,
      };
    });

    // Filter if present
    if (params.filter) {
      returnedData = returnedData.filter(
        CollectionFilter.generateFiltersFunction(
          JSON.parse(params.filter).map((f) => {
            f.property = f.field;
            return new CollectionFilter(f);
          })
        )
      );
    }

    // Sort if present
    if (params.sort) {
      const gridComponent = this.$refs.childGrid;
      returnedData.sort(
        gridComponent.instance.value.store.createSorterFn(
          JSON.parse(params.sort)
        )
      );
    }

    // Return response
    return {
      responseText: JSON.stringify({
        success: true,
        total: returnedData.length,
        data: returnedData.slice(startIdx, startIdx + pageSize),
      }),
    };
  });
},
getBadge(status) {
  switch (status) {
    case "Receipt Posted":
      return "success";
    case "Receipt Created":
      return "secondary";
    case "Started":
      return "warning";
    case "Not Started":
      return "danger";
    default:
      return "primary";
  }
},
viewFileDetails(record) {
  console.log("View record:", record);
  // Implement your routing logic if needed:
  // this.$router.push({ path: `/your-path/${record.id}` });
},
  },
  mounted() {
    this.createStore();
    // Initial load to force grid refresh
    this.$refs.childGrid.instance.value.store.load();
  },
};
</script>

Please provide the working example with ajax mocked URL with date field sorting.


Post by alex.l »

Hi,

The code you mentioned - is the code of column. I meant AjaxStore's data model field type. Please follow the links provided, it shows how to set types for data models.

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by peachaws »

alex.l wrote: Wed Jun 11, 2025 4:51 pm

Hi,

The code you mentioned - is the code of column. I meant AjaxStore's data model field type. Please follow the links provided, it shows how to set types for data models.

Hi Marcio,

We have tried the things as per the link provided but its not working for us. Can you please provide sample working example, with the ajaxstore implementation and datesorting ????
I hope you have checked the code we provided, we suspect that the issue lies where its being converted to the JSON.stringify...Now can you provide the solution for this, again I am pasting the same snippet to highlight.

// Return response
    return {
      responseText: JSON.stringify({
        success: true,
        total: returnedData.length,
        data: returnedData.slice(startIdx, startIdx + pageSize),
      }),
    };

Post by alex.l »

Hi,

I only can do visual check since you didn't post any runnable demos. Visually I see the problem in data typing that I've asked you to fix.
When you replaced data with JSON parsing, it might be that data model recognized it as a String, that's the point why it didn't work.

So, you need to define types for store data model https://bryntum.com/products/grid/docs/api/Core/data/Model#defining-fields

store : {
    fields : [
        { name : 'created_at', type : 'date' }
    ]
}

Can you please provide sample working example, with the ajaxstore implementation and datesorting ????

Please post data you used, I can assemble a demo for you.

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by peachaws »

alex.l wrote: Thu Jun 12, 2025 11:46 am

Hi,

I only can do visual check since you didn't post any runnable demos. Visually I see the problem in data typing that I've asked you to fix.
When you replaced data with JSON parsing, it might be that data model recognized it as a String, that's the point why it didn't work.

So, you need to define types for store data model https://bryntum.com/products/grid/docs/api/Core/data/Model#defining-fields

store : {
    fields : [
        { name : 'created_at', type : 'date' }
    ]
}

Can you please provide sample working example, with the ajaxstore implementation and datesorting ????

Please post data you used, I can assemble a demo for you.

Hi,

<template>
  <CCard class="cash-schedules-table main-container p-0 m-0">
    <CCardBody>
      <CRow>
        <CCol>
          <bryntum-grid :config="myconfig" ref="childGrid" />
        </CCol>
      </CRow>
    </CCardBody>
  </CCard>
</template>
<script>
import { BryntumGrid } from "@bryntum/grid-vue-3";
import { AjaxHelper, CollectionFilter } from "@bryntum/grid";
export default {
  name: "CreditCardCollectionsExample",
  components: {
    BryntumGrid,
  },
  data() {
    const itemSize = [10, 20, 50];
    return {
      items: [
        {
          id: 21631,
          Brnid: "102",
          file_name: " Recon_2025_06_09_po_XYZ.csv",
          created_at_date: "2025-06-09T05:00:00.000Z",
          f_status: "Not Started",
          is_receipt_created: "2",
        },
        {
          id: 21627,
          Brnid: "102",
          file_name: " Recon_2025_06_06_po_ABC.csv",
          created_at_date: "2025-06-06T05:00:00.000Z",
          f_status: "Receipt Posted",
          is_receipt_created: "1",
        },
        // You can add more records here (copy from your big array)
      ],
      myconfig: {
        cls: "grid-container",
        height: "80vh",
        pageSize: itemSize[1],
        columns: [
          {
            field: "id",
            text: "ID",
            order: 1,
            autoWidth: true,
          },
          {
            field: "file_name",
            text: "File Name",
            order: 2,
            flex: 1,
          },
          {
            field: "f_status",
            text: "Status",
            order: 3,
            width: 150,
            renderer: ({ value }) => {
              let statusClass = value
                ? this.getBadge(value)
                : this.getBadge("Not Started");
              return {
                class: `badge bg-${statusClass}`,
                text: value,
              };
            },
          },
          {
            field: "created_at_date",
            text: "Downloaded At",
            order: 4,
            width: 140,
            type: "date",
            dateFormat: "YYYY-MM-DD",
          },
          {
            field: "",
            text: "Action",
            width: 70,
            order: 5,
            type: "widget",
            widgets: [
              {
                type: "button",
                cls: "b-raised b-blue",
                icon: "b-icon b-fa-eye",
                onClick: ({ source: btn }) => {
                  const { record } = btn.cellInfo;
                  this.viewFileDetails(record);
                },
              },
            ],
            renderer({ record, widgets }) {
              const data =
                record.data.is_receipt_created == 1 ||
                record.data.is_receipt_created == 2;
              widgets[0].hidden = !data;
              widgets[0].tooltip = "View";
            },
          },
        ],
        store: {
          readUrl: "/pagedMockUrl",
          pageParamName: "page",
          sortParamName: "sort",
          filterParamName: "filter",
          pageSize: itemSize[1],
          autoLoad: true,
          proxy: true,
        },
        bbar: {
          type: "pagingtoolbar",
        },
        tbar: {
          items: [
            {
              type: "Combo",
              label: "Item Per Page",
              items: itemSize,
              value: itemSize[1],
              editable: false,
              placeholder: "select",
              style: {
                float: "right",
                position: "absolute",
                top: 10,
                right: 10,
                width: "200px",
              },
              onChange: ({ value }) => {
                this.$refs.childGrid.instance.value.store.pageSize = value;
                this.$refs.childGrid.instance.value.store.currentPage = 1;
                this.$refs.childGrid.instance.value.store.load();
              },
            },
          ],
        },
        features: {
          filterBar: true,
          cellMenu: false,
          cellEdit: false,
        },
      },
    };
  },
  methods: {
    createStore() {
      AjaxHelper.mockUrl("/pagedMockUrl", (url, params) => {
        const page = parseInt(params.page, 10) || 1;
        const pageSize =
          parseInt(params.pageSize, 10) || this.myconfig.store.pageSize;
        const startIdx = (page - 1) * pageSize;
    let returnedData = this.items.map((item) => {
      const dateObj = item.created_at_date
        ? new Date(item.created_at_date)
        : null;
      return {
        ...item,
        id: Number(item.id),
        created_at_date: dateObj,
      };
    });
    // Filter if present
    if (params.filter) {
      returnedData = returnedData.filter(
        CollectionFilter.generateFiltersFunction(
          JSON.parse(params.filter).map((f) => {
            f.property = f.field;
            return new CollectionFilter(f);
          })
        )
      );
    }
    // Sort if present
    if (params.sort) {
      const gridComponent = this.$refs.childGrid;
      returnedData.sort(
        gridComponent.instance.value.store.createSorterFn(
          JSON.parse(params.sort)
        )
      );
    }
    // Return response
    return {
      responseText: JSON.stringify({
        success: true,
        total: returnedData.length,
        data: returnedData.slice(startIdx, startIdx + pageSize),
      }),
    };
  });
},
getBadge(status) {
  switch (status) {
    case "Receipt Posted":
      return "success";
    case "Receipt Created":
      return "secondary";
    case "Started":
      return "warning";
    case "Not Started":
      return "danger";
    default:
      return "primary";
  }
},
viewFileDetails(record) {
  console.log("View record:", record);
  // Implement your routing logic if needed:
  // this.$router.push({ path: `/your-path/${record.id}` });
},
  },
  mounted() {
    this.createStore();
    // Initial load to force grid refresh
    this.$refs.childGrid.instance.value.store.load();
  },
};
</script>

This is the example you have asked for, earlier also we provided the same....Thank you and waiting for your positive response in the matter...


Post by alex.l »

Hi,

I didn't find data in your code, as well as runnable code.
Please see runnable demo attached, based on our paged demo for Vue. All I did: I added a date field into columns set, and specified data type for that field in store model. Looks like it worked well.
Let us know if any questions!

paged.zip
(196.72 KiB) Downloaded 4 times

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post Reply