How can I get custom paging to work on a dynamically built HTML table

I have an issue with a dynamically built HTML table and custom paging. The result set is around 30,000 records and I would like to introduce custom paging. The table loads fine initially with 10 records showing but when I click the next button, all rows disappear. I’m sure it is something trivial but my javascript knowledge is not to great. Any help would be much appreciated

HTML Markup

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AzureAutomation.Default" %>

<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
    <asp:ScriptManager runat="server" EnablePageMethods="true">
        <Scripts>
            <%--Framework Scripts--%>
            <asp:ScriptReference Name="MsAjaxBundle" />
            <asp:ScriptReference Name="jquery" />
            <asp:ScriptReference Name="bootstrap" />
        </Scripts>
    </asp:ScriptManager>

    <div class="jumbotron">
        <h1>Welcome to the Microsoft and Proofpoint automation tool</h1>
        <p class="lead">Reset Session Tokens, Reset MFA Registration, and Reset / Lockdown Accounts, all at the click of a button</p>
    </div>
    <div class="jumbotron">
        <div id="userTablePlaceholder" clientidmode="Static" runat="server"></div>
        <button id="prevPage" clientidmode="Static" runat="server">Previous</button>
        <button id="nextPage" clientidmode="Static" runat="server">Next</button>
    </div>

    <div class="row">
        <div class="col-md-4">
            <h2>Reset Session Token</h2>
            <p>This will reset the session token of the selected user.</p>
            <p>
                <asp:Button ID="btnResetSessionToken" CssClass="btn btn-default" runat="server" Text="Reset Session Token" OnClick="btnResetSessionToken_Click" />
            </p>
        </div>
        <div class="col-md-4">
            <h2>Reset MFA Registration</h2>
            <p>This will reset the MFA registration of the selected user.</p>
            <p>
                <asp:Button ID="btnResetMFARegistration" CssClass="btn btn-default" runat="server" Text="Reset MFA Registration" OnClick="btnResetMFARegistration_Click" />
            </p>
        </div>
        <div class="col-md-4">
            <h2>Reset Account</h2>
            <p>This will reset the account status of the selected user.</p>
            <p>
                <asp:Button ID="btnResetAccount" CssClass="btn btn-default" runat="server" Text="Reset Account" OnClick="btnResetAccount_Click" />
            </p>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4">
            <h2>Lockdown Account</h2>
            <p>This will completely lockdown the account of the selected user.</p>
            <p>
                <asp:Button ID="btnLockdownAccount" CssClass="btn btn-default" runat="server" Text="Lockdown Account" OnClick="btnLockdownAccount_Click" />
            </p>
        </div>
    </div>
    <script>
        document.addEventListener("DOMContentLoaded", function () {
            // Initial page index and page size
            var pageIndex = 0;
            var pageSize = 10;
            var totalRecords = <%= this.TotalRecords %>; // Assuming 'TotalRecords' is the total count returned from the server-side method
            var maxPageIndex = Math.ceil(totalRecords / pageSize) - 1;

            // Initial page display
            showPage(pageIndex, pageSize);
            updateButtonState();

            // Handle page navigation
            document.getElementById("nextPage").addEventListener("click", nextPageHandler);
            document.getElementById("prevPage").addEventListener("click", prevPageHandler);

            // Function to show/hide rows based on current page
            function showPage(pageIndex, pageSize) {
                var rows = document.querySelectorAll("#userTable tbody tr");
                var start = pageIndex * pageSize;
                var end = Math.min(start + pageSize, rows.length); // Ensure end does not exceed the total number of rows

                for (var i = 0; i < rows.length; i++) {
                    rows[i].style.display = (i >= start && i < end) ? "" : "none";
                }
            }

            // Function to handle next page button click
            function nextPageHandler() {
                event.preventDefault();
                if (pageIndex < maxPageIndex) {
                    pageIndex++;
                    showPage(pageIndex, pageSize);
                    updateButtonState();
                }
            }

            // Function to handle previous page button click
            function prevPageHandler() {
                event.preventDefault();
                if (pageIndex > 0) {
                    pageIndex--;
                    showPage(pageIndex, pageSize);
                    updateButtonState();
                }
            }

            // Function to update button state
            function updateButtonState() {
                document.getElementById("prevPage").disabled = (pageIndex === 0);
                document.getElementById("nextPage").disabled = (pageIndex === maxPageIndex);
            }
        });
    </script>
</asp:Content>

Code behind

using AzureAutomation.API;
using AzureAutomation.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.DirectoryServices;
using System.Linq;
using System.Text;
using System.Web.Configuration;
using System.Web.Script.Serialization;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Logging;
using Logging.NLog;

namespace AzureAutomation
{
    public partial class Default : Page
    {
        public int TotalRecords { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                var (searchResults, totalRecords) = UsersController.LoadActiveDirectoryAccounts();
                string userTableHtml = GenerateUserTable(searchResults, pageIndex: 0, pageSize: 10);
                userTablePlaceholder.InnerHtml = userTableHtml; // userTablePlaceholder is a placeholder for the HTML table in your Default.aspx
                TotalRecords = totalRecords;
            }


        }

        private string GenerateUserTable(List<SearchResult> searchResults, int pageIndex, int pageSize)
        {
            StringBuilder htmlBuilder = new StringBuilder();

            // Start the table
            htmlBuilder.Append("<table id='userTable' clientidmode="Static" class="table table-bordered table-striped">");
            htmlBuilder.Append("<thead><tr><th>User Principal Name</th><th>Display Name</th><th>SamAccount Name</th></tr></thead>");
            htmlBuilder.Append("<tbody>");

            // Calculate the start and end indexes for the current page
            int startIndex = pageIndex * pageSize;
            int endIndex = Math.Min(startIndex + pageSize, searchResults.Count);

            // Loop through the subset of searchResults for the current page and add rows to the table
            for (int i = startIndex; i < endIndex; i++)
            {
                var result = searchResults[i];
                htmlBuilder.Append("<tr>");
                htmlBuilder.Append("<td>").Append(result.Properties["userPrincipalName"][0]).Append("</td>");
                htmlBuilder.Append("<td>").Append(result.Properties["DisplayName"][0]).Append("</td>");
                htmlBuilder.Append("<td>").Append(result.Properties["samaccountname"][0]).Append("</td>");
                htmlBuilder.Append("</tr>");
            }

            // End the table
            htmlBuilder.Append("</tbody></table>");

            return htmlBuilder.ToString();
        }


    }
}

Data retrieval

public static (List<SearchResult> searchResults, int totalRecords) LoadActiveDirectoryAccounts()
    {
        List<SearchResult> allResults = new List<SearchResult>();
        var OUSplitList = OuList.Split('|').ToList();
        int totalRecords = 0; // Initialize the total count variable

        try
        {
            foreach (var OU in OUSplitList)
            {
                _logger.Info($"Loading AD accounts from {OU}");
                using (var dirEntry = GetDirectoryEntry(OU))
                {
                    string[] loadProps = { "DisplayName", "samaccountname", "userPrincipalName" };

                    using (var dirSearch = new DirectorySearcher(dirEntry, "(&(objectClass=user)(objectCategory=person))", loadProps))
                    {
                        dirSearch.PageSize = 1000;
                        dirSearch.Sort = new SortOption("samaccountname", SortDirection.Ascending);
                        var results = SafeFindAll(dirSearch);

                        allResults.AddRange(results);
                        totalRecords = allResults.Count; // Increment the total count by the count of results
                    }
                }
                _logger.Info($"Accounts loaded - {allResults.Count} ");
            }
        }
        catch (Exception ex)
        {
            throw new Exception();
        }

        return (allResults, totalRecords); // Return both the list of search results and the total count
    }

    private static DirectoryEntry GetDirectoryEntry(string domain)
    {
        return new DirectoryEntry("LDAP://" + domain, Globals.IamUser, Globals.IamPassword);
    }

    public static IEnumerable<SearchResult> SafeFindAll(DirectorySearcher searcher)
    {
        using (SearchResultCollection results = searcher.FindAll())
        {
            foreach (SearchResult usr in results)
            {
                yield return usr;
            }
        } // SearchResultCollection will be disposed here
    }

  • Please visit the help center, take the tour to see what and How to Ask. Do some research – search SO for answers. If you get stuck, post a minimal reproducible example of your attempt, noting input and expected output using the [<>] snippet editor.

    – 

  • Ok I see. I was under the assumption that you were trying to hide and show rows of pages that you already rendered to the DOM. But your GenerateUserTable() backend function only runs once on page load? When does this get called again for subsequent paginated pages?

    – 




Leave a Comment