DEV Community

Explorer
Explorer

Posted on

Adding Client-Side Pagination to Joget Form Grids (Handsontable)

When working with Joget DX Form Grids that load 50+ rows, form rendering can slow down noticeably. Because Joget's standard Form Grid uses Handsontable under the hood, rendering every single row on a single screen creates DOM bloat.

In this guide, we'll write a simple JavaScript snippet that hooks into Joget's internal Handsontable instance (FormUtil.getField().data("hot")) to add fast, dynamic client-side pagination with numeric page controls (Prev 1 ... 4 5 6 ... 10 Next).


How It Works

  1. Accessing the Hot Instance: Joget stores the underlying Handsontable instance on the jQuery element data key "hot". We poll FormUtil.getField("your_grid_id")?.data("hot") until it's ready.
  2. Data Slicing: Instead of feeding all 100+ rows into the DOM grid simultaneously, the script slices the original array (hotInstance.getSourceData()) into 10-row chunks (rowsPerPage = 10).
  3. Dynamic Controls: Clicking next, previous, or any page number calls hotInstance.loadData(pageData) to swap out the visible rows instantly without a page refresh.

The Code

Place a Custom HTML field inside your Joget form (below your Form Grid) and paste the code below. Be sure to replace your_form_grid_field_id with your actual grid ID.

<div id="gridPaginationControls" style="margin-top: 10px; margin-bottom: 10px;">
  <button type="button" id="prevGridPage" class="form-button btn">Previous</button>
  <span id="gridPageNumbers"></span>
  <button type="button" id="nextGridPage" class="form-button btn">Next</button>
</div>

<style>
  #gridPaginationControls button {
    margin: 2px;
    padding: 4px 10px;
    font-size: 12px;
  }
  #gridPaginationControls .activePage {
    font-weight: bold;
    background-color: #007bff;
    color: #ffffff;
    border-color: #007bff;
  }
  #gridPaginationControls .pageDots {
    margin: 0 4px;
    user-select: none;
  }
</style>

<script>
$(document).ready(function () {
  const targetGridFieldId = "your_form_grid_field_id";

  function setupHandsontablePagination() {
    const $gridElem = FormUtil.getField(targetGridFieldId);
    const hotInstance = $gridElem ? $gridElem.data("hot") : null;

    // Retry if Handsontable hasn't finished initializing yet
    if (!hotInstance) {
      setTimeout(setupHandsontablePagination, 100);
      return;
    }

    const rowsPerPage = 10;
    let currentPage = 1;
    const fullDataSet = hotInstance.getSourceData();
    const totalRows = fullDataSet.length;
    const totalPages = Math.ceil(totalRows / rowsPerPage) || 1;

    function displayPage(pageNumber) {
      const startIndex = (pageNumber - 1) * rowsPerPage;
      const endIndex = startIndex + rowsPerPage;
      const pageRows = fullDataSet.slice(startIndex, endIndex);

      hotInstance.loadData(pageRows);
      currentPage = pageNumber;
      refreshControls();
    }

    function refreshControls() {
      $("#prevGridPage").prop("disabled", currentPage === 1);
      $("#nextGridPage").prop("disabled", currentPage === totalPages);

      let buttonsHtml = "";

      function appendButton(p) {
        if (p === currentPage) {
          buttonsHtml += `<button type="button" class="gridPageBtn activePage" data-page="${p}" disabled>${p}</button>`;
        } else {
          buttonsHtml += `<button type="button" class="gridPageBtn" data-page="${p}">${p}</button>`;
        }
      }

      if (totalPages <= 7) {
        for (let i = 1; i <= totalPages; i++) appendButton(i);
      } else {
        appendButton(1);
        if (currentPage > 3) buttonsHtml += `<span class="pageDots">…</span>`;

        const start = Math.max(2, currentPage - 1);
        const end = Math.min(totalPages - 1, currentPage + 1);
        for (let i = start; i <= end; i++) appendButton(i);

        if (currentPage < totalPages - 2) buttonsHtml += `<span class="pageDots">…</span>`;
        appendButton(totalPages);
      }

      $("#gridPageNumbers").html(buttonsHtml);
    }

    $("#prevGridPage").on("click", function (e) {
      e.preventDefault();
      if (currentPage > 1) displayPage(currentPage - 1);
    });

    $("#nextGridPage").on("click", function (e) {
      e.preventDefault();
      if (currentPage < totalPages) displayPage(currentPage + 1);
    });

    $(document).on("click", ".gridPageBtn", function (e) {
      e.preventDefault();
      const selectedPage = parseInt($(this).data("page"), 10);
      displayPage(selectedPage);
    });

    // Load initial page
    displayPage(1);
  }

  setupHandsontablePagination();
});
</script>
Enter fullscreen mode Exit fullscreen mode

Practical Tips

  • Form Submission Sync: Since getSourceData() returns references to the master dataset, user edits on any paginated page update the underlying master array automatically.
  • Adjusting Page Size: Change rowsPerPage = 10 to 20 or 25 depending on your form layout requirements.

Top comments (0)