Posted in Entity List, JavaScript/jQuery Plugins, Power Apps Portals

Power Pages – Adding Wildcard on List Search

Power Pages gives us the ability of adding a search option to Lists out of the box, which is a great functionality if you consider the effort if we had to implement this manually.

By default, the search looks for the exact match on all columns available in the List, and if you need to use a LIKE operator, you need to add a wildcard (*) to the search term.

If you are coming from a Dynamics 365 background, you are probably very familiar with this behaviour, but when thinking of external users, this might not be the case, and the experience may not be natural to users.

In this quick article I want to share a JavaScript code that will automatically inject an * to the search term whenever the user performs a search. Initially when trying this out I thought it would be a lot easier than it actually was, so here I will explain all the functions and the logic behind the code:

  • AddSearchWildcard: This function will get the text input element for the search, and add a * in the start of the search term
  • ClearWildcardSearch: This function will remove the * from the text input so it all looks transparent to the user
  • ClearSearchTooltip: By default, we have a tooltip explaining to the user to add a *, however this is not necessary, so this function complete removes the tooltip.
  • BindFirst: This function is used to enforce that our AddSearchWildcard function will be executed prior to the search itself
  • document.ready: Finally on document ready the we are registering the BindFirst Function and adding the necessary functions to the appropriate event listeners.

And the full code is:

$(document).ready(function () {
    $.fn.bindFirst = BindFirst;
    var list = $(".entitylist.entity-grid").eq(0);
    list.on("loaded", function () {

        var searchButton = $('.entitylist-search > div.input-group-btn > button');
        searchButton.bindFirst("click", AddSearchWildcard);
        searchButton.on("click", ClearWildcardSearch);
        ClearSearchTooltip();
    });
});

function AddSearchWildcard() {
    var queryInput = $("input.query");
    var queryInputValue = queryInput.val();
    if (!!queryInputValue && !queryInputValue.startsWith("*")) {
        $(queryInput).val("*" + queryInputValue);
    }
};

function ClearWildcardSearch() {
    var queryInput = $("input.query");
    var queryInputValue = queryInput.val();

    if (!!queryInputValue && queryInputValue.startsWith("*")) {
        queryInput.val(queryInputValue.substring(1, queryInputValue.length));
    }
}

function ClearSearchTooltip() {
    var queryInput = $("input.query");
    queryInput.attr("aria-label", "");
    queryInput.attr("data-original-title", "");
};

function BindFirst(eventName, eventHandler) {
    var elem, handlers, i, _len;
    this.bind(eventName, eventHandler);
    for (i = 0, _len = this.length; i < _len; i++) {
        elem = this[i];
        if (!!elem.tagName && elem.tagName == "BUTTON") {
            handlers = $._data(elem).events[eventName.split('.')[0]];
            handlers.unshift(handlers.pop());
        }
    }
}

Conclusion

This solution is great to enhance the user experience when searching within Lists, however keep in mind that this performs a server-side search on every column available in the list, so if you have too many columns and records on your list this might decrease your Portal performance. Before publishing this make sure that your list is already optimized with only necessary columns.

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Pages – Autocomplete Lookup / Dropdown

Basic Forms and Advanced Forms in Power Pages are an amazing way to quickly expose Dataverse data to external users for data manipulation.

The way Power Pages works is by reading the Dataverse form metadata to render the controls on the page. Using Basic Form/Advanced Form Metadata, Power Pages also allows us to control styling and behaviour to controls.

One common question I receive is how to convert a dropdown into a autocomplete control, and that’s what we’ll see in this post. Here is an example of what we are about to achieve:

It’s probably obvious, but worth mentioning that this logic is only applicable for the following type of controls:

  • Choices (OptionSet)
  • Lookups (rendered as dropdown via metadata)

I will describe here what my code below is doing, and the things I have considered when putting this together:

  • The function ConvertSelectToAutocomplete takes two arguments:
    • selectName: the schema name of your column (i.e. primarycontactid)
    • selectPlaceholder: optional parameter if you want a placeholder text when it’s blank (i.e. Please start typing…)
  • The function will copy a few elements from the original select element, for example Classes and Readonly attributes
  • Then I am creating a new <input> element pointing to an empty <datalist> and adding this element to the page
  • Now what I do is loop through all <option> within the original <select> element and add the options to the new datalist
  • The original <select> element is no longer necessary, so we can hide it from the page with a .hide()

All actions above are sufficient to convert your dropdown to a HTML5 autocomplete dropdown, but there are still a few more things to get the full solution working properly:

  • If the record being opened already contains value in the Choices/Lookup column, we need to make sure to populate with the original value. For this we will check the current value and try to find the correspondent item within the dataset
  • The last action here is to attach a logic to the OnChange event, the reason for this is that we need to make sure to keep the original column element in the page up to date with the newly selected value as in the form submit is the original element that will be sent to the server

Before we get to the full code, it’s important to note a few things:

  1. Dataset value
    • The control uses a dataset for holding the options available in the dropdown. The dataset value attribute is the text displayed within the dropdown, so I am creating a separate data-value to hold the actual id/value of the item
    • Because of that, every time we need to get the data-value, it has to be done by finding the element within the dataset list (by text)
    • In other words, for this to work properly, your dataset needs to have unique texts, otherwise it might find the wrong item
  2. Invalid entry
    • By default, this control allows you to add an entry that is not listed within the dataset, so we need to prevent this from happening
    • In my code, what I am doing is simply ignoring and removing the selected text, however you can add an error message in the code if it you prefer
$(document).ready(function () {
    ConvertSelectToAutocomplete("<your optionset/lookup dropdown here>");
});

function ConvertSelectToAutocomplete(selectName, selectPlaceholder) {
    selectPlaceholder = selectPlaceholder ?? "";
    var selectElement = $("#" + selectName);
    var selectElementClass = selectElement.attr("class");
    var readonly = $(selectElement).attr("readonly") ?? "";
    var autoCompleteElementId = selectName + "-autocomplete";
    var autoCompleteDatasetId = selectName + "-data";
    var autoCompleteElement = '<input name="' + autoCompleteElementId + '" id="' + autoCompleteElementId + '" class="' + selectElementClass + '" list="' + autoCompleteDatasetId + '" placeholder="' + selectPlaceholder + '" ' + readonly + '><datalist id="' + selectName + '-data"></datalist>';
    var options = "";

    $(selectElement).parent().append(autoCompleteElement);
    $("#" + selectName + " option").each(function (index, o) {
        options += '<option data-value="' + o.value + '" value="' + o.text + '"/>';
    });
    $("#" + autoCompleteDatasetId).html(options);

    $(selectElement).hide();

    var currentSelectedValue = $(selectElement).val();
    if (!!currentSelectedValue) {
        $("#" + autoCompleteElementId).val($(selectElement).find("option:selected").text());
    }

    $("#" + autoCompleteElementId).on("change", function () {
        var selectedValue = $("#" + autoCompleteDatasetId + " option[value='" + $("#" + autoCompleteElementId).val() + "']").attr("data-value");
        selectElement.val(selectedValue);
        if (typeof selectedValue === "undefined") {
            $("#" + autoCompleteElementId).val("");
            // optionally you can add an error message here
        };
    });
};

Conclusion

This is a nice client-side script logic to enhance the user experience in your Portals. I probably wouldn’t use this in every scenario, if you have just a few options like Yes/No/Blank, I would probably not use this; but if your list goes beyond that with 10+ options, this might improve the way users are filling in forms.

Posted in Uncategorized

Power Pages – Power Apps Portals is now Power Pages

Microsoft Build 2022 is coming the announcements of many things throughout the Microsoft stack. I highly recommend catching-up on the sessions for your favourite topics here: https://mybuild.microsoft.com/en-US/home

Power Pages

Power Apps Portals is now Power Pages – and of course this is what I am here to talk about: Announcing Microsoft Power Pages: Build secure, low-code websites | Microsoft Power Pages

This is definitely the most exciting announcement for me personally. Microsoft has put a lot of investment on Power Apps Portals over the years and while it has been part of the Power Apps products for a few years, it is now becoming a product of its own within the Power Platform under the new name of Power Pages.

You may think this is just a product re-branding (as we all know Microsoft loves to do that), but it is a bit more than that, community forum will now have its own dedicated forum now, similar to Power BI or Power Automate, new Site Templates will be available to accelerate your implementation, a complete new Design Studio with many new capabilities and many more. I see all of this is a reflection of the growth of the platform and as a community member very much involved in this product, I am really happy to see this evolution.

What does this mean to your current Portals?

The base of the Portals is still the same, so there is no compatibility issue between them. Microsoft haven’t released yet details on migration, the only potential impact I can think of would be on the domain of your portals (from .powerappsportals.com to .powerpages.com). This should be more clear over the upcoming weeks.

Posted in Entity List, JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – Remove default value on Lookup Modal

Following up on a comment from another post: Custom Lookup filter, I feel like this deserves its own dedicated article.

A Lookup column in a Basic Form/Advanced Form in Power Apps Portals is represented in a Web Page by a web control that contains a modal associated to it. This modal gives us the ability to select and filter records for that entity.

If your Lookup column is empty, when clicking on the magnifying glass, the Portals will automatically select the first record:

This behaviour might not be what you are looking for, so in this article I wanted to share a simple JavaScript code that will untick the default value, unless the lookup column already contains data:

$(document).ready(function () {

    var modalList = $("#cr285_customerlookup_lookupmodal").find(".entity-lookup").find(".entity-grid").eq(0);
    modalList.on("loaded", function () {
        var lookupValue = $("#cr285_customerlookup").val();
        if (!!!lookupValue) {
            var selected = modalList.find("table tbody > tr.selected");
            $(selected).find("td[data-th=Select] > span[role='checkbox']").click()
        }

    });
});

Also worth mentioning that this is not removing the record/row from the table within the modal. It is simply deselecting the record.

I hope this helps with your Power Apps Portals implementation.

Update December 2022

I just heard from Microsoft that this has now been fixed and shouldn’t be an issue. This articles is still relevant for injecting code to the lookup window, so I will keep the article here.