Lists in Power Pages don’t have multiselect capabilities out of the box. So if we want users to be able to select multiple rows and make a bulk edit, we need users to be able to select multiple rows. In this post, we’ll look at how we can accomplish that using the classic (non-modern) lists.
My goal for this post is to keep it simple and to keep the code to a minimum. I’m not going to use React or TypeScript – instead, I’m going old school, by using jQuery, along with Bootstrap v5 components.
The Plan
Here is what we intend to do:
- We are going to add a column to the front of every row that contains a checkbox – this will be the primary way to select a row.
- We are going to add an area above the list that shows all of the currently selected items. Users should be able to remove items from being selected in this area. This area should be hidden if no rows are selected.
- We will have an object called selectedRows that contains the ID of the selected rows, which we’ll use in future posts to enable bulk edit.
The primary challenge: classic lists have paging. So we need to be careful to handle what happens as the user switches between pages, like making sure that our list of selected items remains accurate, and that the checkbox for each row accurately reflects whether that row is currently selected.
The Code
Based on those considerations, I’ve created the following code:
$(document).ready(function () {
// create an area to show selected rows
const selectedCard = $('<div class="card"><div class="card-body"></div></div>').hide();
// a variable to keep track of all currently selected rows
const selectedRows = {};
$(".entitylist.entity-grid").on("loaded", function () {
const list = $(this);
// handle adding a blank cell to the header for the checkbox column
$(this).children(".view-grid").find("thead tr").each(function () {
// this event fires for each page of data, but the header is reused, so only add the cell once
if ($(this).find('.multiselectHeaderColumn').length == 0) {
$('<td class="multiselectHeaderColumn"></td>').prependTo(this);
}
});
$(this).children(".view-grid").find("tbody tr").each(function () {
// grab the name and id of the current row
const name = $(this).data('name');
const id = $(this).data('id');
// create a checkbox used to make the multiselect interface
var checkbox = $('<input class="multiselectCheckbox" type="checkbox" />')
// if the id exists in the object, ensure the checkbox begins checked
.prop('checked', selectedRows[id])
.on('click', function() {
if ($(this).is(':checked')) {
// create a badge to display the currently selected rows
const badge = $('<span class="badge">' + name + '</span>');
// add a button to the badge that, when clicked, unselects the row
const removeButton = $('<button class="btn btn-primary btn-sm">X</button>').on('click', function() {
selectedRows[id].remove();
delete selectedRows[id];
list.find('tr[data-id="' + id + '"] input.multiselectCheckbox').prop('checked', false);
});
badge.append(removeButton);
selectedRows[id] = badge.appendTo(selectedCard.find('.card-body'));
selectedCard.show();
} else {
selectedRows[id].remove();
delete selectedRows[id];
// if there are no selected item, hide the area
if (!Object.keys(selectedRows).length) selectedCard.hide();
}
});
$('<td />').append(checkbox).prependTo(this);
});
}).prepend(selectedCard);
});
With that code in place, we get a rudimentary multiselect interface that works even when users go between different pages in the list:
