Implementing Multi-Selection in jqGrid within NFine Framework

The NFine framework does not include built-in multi-selection functionality. After implementing it, I realized that such features are needed only occasionally, so future enhancements will be applied selectively to maintain consistency across the system.

Since the implementation leverages jqGrid's plugin capabilities, a few API calls were sufficient. Below is the essential code modification.

/* Adjust checkbox vertical alignment and increase width by 31px */
#gridList tr td input[type=checkbox] {
    margin-top: 9px;
}
function btn_details() {
    const selectedKeys = $("#gridList").jqGridRowValueMul();
    if (selectedKeys.toString().includes(',')) {
        $.modalAlert("Please select only one item for viewing.", "error");
        return;
    }
    $.modalOpen({
        id: "Details",
        title: "View Material",
        url: "/ProjectBudget/GoodsInf/Details?keyValue=" + selectedKeys,
        width: "800px",
        height: "650px",
        btn: null
    });
}

Modify framework-ui.js by adding two new methods. These can be invoked on pages requiring multi-selection.

// Retrieve values of multiple selected rows
$.fn.jqGridRowValueMul = function () {
    const $grid = $(this);
    return $grid.jqGrid("getGridParam", "selarrrow");
};
// Enhanced grid initialization supporting multi-selection
$.fn.dataGridMul = function (options) {
    const defaults = {
        datatype: "json",
        autowidth: true,
        rownumbers: true,
        shrinkToFit: false,
        gridview: true
    };
    const settings = $.extend(defaults, options);
    const $container = $(this);
    
    settings.onSelectRow = function () {
        const $toolbar = $(".operate");
        const selectedRowId = $(this).jqGrid("getGridParam", "selrow");
        const isChecked = selectedRowId !== undefined && selectedRowId !== null && selectedRowId.length > 0;
        
        if (isChecked) {
            $toolbar.animate({ left: 0 }, 200);
            const count = $("#gridList input[type=checkbox]:checked").length;
            $(".first").find("span").text(count);
        } else {
            $toolbar.animate({ left: '-100.1%' }, 200);
        }
        
        $toolbar.find('.close').on('click', function () {
            $toolbar.animate({ left: '-100.1%' }, 200);
        });
    };
    
    settings.onSelectAll = function () {
        const $toolbar = $(".operate");
        const selectedRows = $(this).jqGrid("getGridParam", "selrow");
        const hasSelection = selectedRows !== undefined && selectedRows !== null && selectedRows.length > 0;
        
        if (hasSelection) {
            $toolbar.animate({ left: 0 }, 200);
            const count = $("#gridList input[type=checkbox]:checked").length;
            $(".first").find("span").text(count);
        } else {
            $toolbar.animate({ left: '-100.1%' }, 200);
        }
        
        $toolbar.find('.close').on('click', function () {
            $toolbar.animate({ left: '-100.1%' }, 200);
        });
    };
    
    $container.jqGrid(settings);
};

Initialize the grid with multi-selection enabled:

const $gridList = $("#gridList");
$gridList.dataGridMul({
    url: "/ProjectBudget/GoodsInf/GetGridJson",
    height: $(window).height() - 128,
    colModel: [
        { label: "Primary Key", name: "F_Id", hidden: true, key: true },
        { label: 'Material Name', name: 'GoodsName', width: 110, align: 'left' },
        { label: 'Category', name: 'GoodsStyleName', width: 110, align: 'left' },
        { label: 'Guanjia Po Number', name: 'GjpNO', width: 109, align: 'left' },
        { label: 'Specifications', name: 'GoodsSpec', width: 120, align: 'left' },
        { label: 'Unit', name: 'UnitName', width: 80, align: 'center' },
        { label: 'Brand', name: 'GoodsBand', width: 120, align: 'left' },
        { label: 'Supplier', name: 'Supplier', width: 120, align: 'left' },
        { label: 'Stock', name: 'StockReal', width: 80, align: 'center' },
        {
            label: "Active", name: "F_EnabledMark", width: 60, align: "center",
            formatter: function (value) {
                return value === 1 ? '<i class="fa fa-toggle-on"></i>' : '<i class="fa fa-toggle-off"></i>';
            }
        },
        { label: 'Sort Order', name: 'F_SortCode', width: 60, align: 'center' }
    ],
    pager: "#gridPager",
    multiselect: true,
    sortname: 'GoodsStyleID asc,F_SortCode asc',
    viewrecords: true
});

By default, edit and view actions restrict selection to a single item—any attempt to select multiple items triggers a warning. The full selection feature updates the count dynamical. Back end logic should support comma-separated IDs for batch operations. Always evaluate whether multi-selection is appropriate per page—use it only when necessary.

Tags: jqGrid NFine multi-selection javascript web-development

Posted on Thu, 20 Aug 2026 16:09:04 +0000 by tylerdurden