Engineered Code is proud to announce the availability of ecLearn - the Learning Management System built on top of Microsoft Dataverse

ENGINEERED CODE BLOG

Power Pages: Multiselect Load Event

A few months back, I wrote an article about how to handle the change event for multiselect (choices) columns on Power Pages forms. Another common requirement is handling an event when the page first loads – this can be useful in situations where you want to modify the available options based on other values on the page. In this post, I’ll look at how you might be able to do that.

In an ideal world, a post like this wouldn’t be necessary. Handling change and load events would be straight-forward, and even documented by Microsoft. Unfortunately, that is not the case.

What makes this situation even more challenging is that the multiselect control is implemented in a few layers. First, it is a PCF Control, which means not only is there a layer for the PCF Control itself, but also the code used by Microsoft to add PCF Controls to the page. Then, the PCF Control leverages a jQuery plugin called Searchable Option List.

I alluded to this a bit is that previous post, but PCF Controls are not loaded at the time the document ready event fires, which is normally where you’d put the code to handle any logic you need when the page is loaded for the first time. When the document ready event fires, all you’ll see is some simple markup that will eventually be transformed later when the PCF Control is actually initialized.

Not only is the PCF Control not loaded, but the libraries that it uses, like the Searchable Option List, aren’t included on the page yet either. So we can’t even hook any static methods that may be available from the plugin, such as setting default event handlers.

So it seems like we might be out of luck. But of course, if we were out of luck, I probably wouldn’t be writing this post. And once you get to the end of this post, you might think I shouldn’t have written this post. Because what’s about to come is pretty ugly. However, I was too stubborn to give up. So I’m not saying what I’m about to share is great. But I think it was an interesting exercise, and by sharing I’m hoping that others might have some ideas on how to improve it.

The “solution” I came up with was figured out by going through the JavaScript related to the multiselect control in my browser’s developer tools. What I was hoping to find was some straight-forward way to hook into the initialization and change events that I knew existed in the Searchable Option List control. But, as I mentioned, this wasn’t possible because that library hasn’t even been loaded at this point.

So I thought, what if I add the library to the page myself? Then perhaps I could hook into some of the functionality.

My first try was to simply add the script to the page:

<script src="https://content.powerapps.com/resource/powerappsportal/controlsAssets/v_3.0.30_solCustom.8B054B1.js" />

This worked in that the library was now available to me. However, when the PCF Control functionality fires, the library gets added to the page again, so anything I’ve done up to this point to hook up custom events is erased.

So next I looked into how to not have the PCF Control add that library. After poking around in the markup, I noticed that in the initial source of the page, the configuration for the PCF Control is stored on an element in a data-pcf-control attribute. So my idea was to edit that config to remove the library. So my code to do that, and then add the library myself, looks like:

var multiselectColumnName = 'new_multiselect';
 
// get the JSON config, stored in the data-pcf-control attribute
var pcfControlConfigSpan = $('#' + multiselectColumnName).closest('.control').find('[data-pcf-control]');
var pcfControlConfig = pcfControlConfigSpan.data('pcf-control');
 
// find the resource where the name contains "solCustom", and add it to the page
const s = document.createElement("script");
s.src = $.grep(pcfControlConfig.Resources, function (e) {
    return e.Path && e.Path.indexOf('solCustom') >= 0;
})[0].Path;
document.body.appendChild(s);
 
// remove the resource where the name contains "solCustom", since we just did it ourself
pcfControlConfig.Resources = $.grep(pcfControlConfig.Resources, function (e) {
    return !e.Path || e.Path.indexOf('solCustom') < 0;
});
 
// update the attribute with our changes
pcfControlConfigSpan.attr('data-pcf-control', JSON.stringify(pcfControlConfig));

That code should be put on the Custom JavaScript of the web page where your form appears. It can’t be put in the Custom JavaScript of the form, because that appears above the form definition, so the element with the data-pcf-control won’t exist yet. It should not be put into a document ready event – we need script added to the page as soon as possible.

Now that we have our library loaded (and only once), we can hack into the initialization routine of Searchable Option List jQuery plugin so that we can provide a custom onInitialized event.

The Searchable Option List jQuery plugin extends the jQuery prototype ($.fn) by adding a function called multiSelectOptionSet. The PCF Control calls this function when it is setting everything up. We’re going to hack this by overriding this method with our own version, which will give us the ability to add our own events:

// get a reference to the existing version so we can call it
var old = $.fn.multiSelectOptionSet;
$.fn.multiSelectOptionSet = function (options) {
    // check which multiselect field is being initialized
    if (options.key == multiselectColumnName) {
        // get a reference to the existing init handler, if any
        var oldInit = options.events.onInitialized;
        // add our own init handler
        options.events.onInitialized = function (sol, items) {
            console.log('init!');
            // call the original init handler
            oldInit && oldInit.call(this, sol, items);
        };
        // get a reference to the existing change handler, if any
        var oldChange = options.events.onChange;
        // add our own change handler
        options.events.onChange = function (sol, changedElements) {
            console.log('changed!');
            // call the original change handler
            oldChange && oldChange.call(this, sol, changedElements);
        };
    }
    return old.call(this, options);
}; 

We’ll add this code to the load event of the Searchable Option List JavaScript. Therefore, our final code looks like:

var multiselectColumnName = 'new_multiselect';
 
// get the JSON config, stored in the data-pcf-control attribute
var pcfControlConfigSpan = $('#' + multiselectColumnName).closest('.control').find('[data-pcf-control]');
var pcfControlConfig = pcfControlConfigSpan.data('pcf-control');
 
// find the resource where the name contains "solCustom", and add it to the page
const s = document.createElement("script");
s.src = $.grep(pcfControlConfig.Resources, function (e) {
    return e.Path && e.Path.indexOf('solCustom') >= 0;
})[0].Path;
s.addEventListener('load', function () {
    // get a reference to the existing version so we can call it
    var old = $.fn.multiSelectOptionSet;
    $.fn.multiSelectOptionSet = function (options) {
        // check which multiselect field is being initialized
        if (options.key == multiselectColumnName) {
            // get a reference to the existing init handler, if any
            var oldInit = options.events.onInitialized;
            // add our own init handler
            options.events.onInitialized = function (sol, items) {
                console.log('init!');
                // call the original init handler
                oldInit && oldInit.call(this, sol, items);
            };
            // get a reference to the existing change handler, if any
            var oldChange = options.events.onChange;
            // add our own change handler
            options.events.onChange = function (sol, changedElements) {
                console.log('changed!');
                // call the original change handler
                oldChange && oldChange.call(this, sol, changedElements);
            };
        }
        return old.call(this, options);
    }; 
});
document.body.appendChild(s);
 
// remove the resource where the name contains "solCustom", since we just did it ourself
pcfControlConfig.Resources = $.grep(pcfControlConfig.Resources, function (e) {
    return !e.Path || e.Path.indexOf('solCustom') < 0;
});
 
// update the attribute with our changes
pcfControlConfigSpan.attr('data-pcf-control', JSON.stringify(pcfControlConfig));

As I said earlier, this is not a technique that I’m overly proud of – it definitely falls in the category of a hack. But, it seems to do the job. Let’s hope Microsoft gives us a better way.

 

2 responses to “Power Pages: Multiselect Load Event”

  1. Muhammad Iqbal says:

    I have return a method to remove the items from the list so i can show filtered list.
    But struggling to reset the control to load all the options back?

Leave a Reply

Your email address will not be published. Required fields are marked *

Contact

Engineered Code is a web application development firm and Microsoft Partner specializing in web portals backed by Dynamics 365 & Power Platform. Led by a professional engineer, our team of technology experts are based in Regina, Saskatchewan, Canada.