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 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.

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

Power Apps Portals – Entity List: Making the entire row clickable

When creating a page containing an Entity List (or just List) in Power Apps Portals, it is very common to add a details page so users can drill-down to the records details.

The list is represented as below example:

By default, the first column contains the link to the details Web Page as well as the action menu on the right dropdown.

In this post, I will share two options for JavaScript/jQuery code to make the entire row clickable.

Solution 1 – Making each cell clickable

In this example, we will perform the following:

  • Loop through the list, retrieving the URL for record details each row
  • Then we will loop through each individual cell, and attach an <a> element to represent the link
  • The code also needs to ignore the TD containing the dropdown (optionally this can be removed)
SetLinkAllCells = function () {

    var entityList = $(".entitylist.entity-grid").eq(0);

    entityList.on("loaded", function () {
        entityList.find("table tbody > tr").each(function (index, tr) {
            
            var primaryColumn = $(tr).find('td')[0];

            /// or retrieve column by name
            /// var primaryColumn = tr.find('td[data-attribute="name"]');

            var url = $(primaryColumn).find("a")[0].href;
            console.log("URL: " + url);
            if (!!url) {
                $(tr).find('td').each(function (index, td) {
                    /// ignore action menu / dropdown
                    if ($(td).attr("aria-label") == "action menu")
                        return;

                    var cellValue = $(td).text();
                    /// clear td value 
                    $(td).text("");
                    var newElement = '<a href="' + url + '" class="details-link has-tooltip" data-toggle="tooltip">' + cellValue + '</a>';
                    $(td).append(newElement);
                });
            }
        });
    });
};

Solution 2 – Making the table row clickable

In this example, we will perform the following:

  • Loop through the list, retrieving the URL for record details each row
  • Attach a new function to the click event of the row, redirecting the user to the URL
  • Remove the action menu, as this will also be triggered on the row click event
  • Set the cursor to pointer for better UX
SetLinkAllRow = function(){
    var entityList = $(".entitylist.entity-grid").eq(0);

    entityList.on("loaded", function () {
        entityList.find("table tbody > tr").each(function (index, tr) {
            
            var primaryColumn = $(tr).find('td')[0];

            /// or retrieve column by name
            /// var primaryColumn = tr.find('td[data-attribute="name"]');

            var url = $(primaryColumn).find("a")[0].href;
            console.log("URL: " + url);
            if (!!url) {
                $(tr).css("cursor","pointer")
                // remove menu dropdown
                $(tr).find('td[aria-label="action menu"]').remove();

                $(tr).click(function(){
                    window.location.href = url;
                });
            }
        });
    });
};

Now all we need is to use the preferred method:

$(document).ready(function () {
    //SetLinkAllRow();
    // or
    //SetLinkAllCells();
});

Conclusion

Each method might suit different needs, you might also consider ignoring cells that already contain a clickable value, for example e-mail address or phone number columns.

Also if your action menu contains several actions, such as delete / start a workflow, etc, the solution might need a bit of a tweak.

In general, this can be a quick win to improve UX in your Power Apps Portals implementation.

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – Custom Lookup filter

A common requirement for Power Apps Portals is to add dynamic filters when we open a lookup modal. Unfortunately there is no OOB way to achieve this. It is important to state here that the Portal relies on the Lookup View from your D365/Dataverse, so if you need a static filter you can always change that view:

In this post I will show a customization using client-side code to filter the data presented on the lookup modal. But before that, I’d like to share an article by a community champion that uses a different method: https://www.dancingwithcrm.com/custom-lookup-filtering-powerapps-portal/ by Oleksandr Olashy.

The idea of the article above is to render the lookup as a dropdown, and then use JavaScript/Web Templates to perform a query and finally re-populate the options in the dropdown element.

That approach will probably be enough for most scenarios, but what if you still need to show the Lookup modal as below?:

Here are a few reasons you might want to display the Lookup modal:

  • If your list is a very long list, a dropdown might not be the best experience for the user;
  • You might want to use the search;
  • You might want to display multiple columns;
  • If you have a subgrid and have an action for Associate record, this will always open the lookup-modal.

In my example, I have an Account lookup on the Contact profile form, and the filter I want to do here is hiding certain records if the current user is not an Administrator. I will also add a flag on the Account entity to mark records that should be hidden.

Here is my Account data with no filters via Advanced Find search:

Now I will add the following JavaScript code to the Profile Web Page:

$(document).ready(function () {

    var list = $("#parentcustomerid_lookupmodal").find(".entity-lookup").find(".entity-grid").eq(0);
    list.on("loaded", function () {
        
        // hide "Admin Only" column
        list.find("table thead th").eq(2).hide();
        list.find("table tbody tr").each(function () { $(this).find("td").eq(2).hide(); });

        var isAdmin = "{{ user | has_role: 'Administrators' }}";
        console.log("is admin: " + isAdmin);
        if (isAdmin == "false") {
            list.find("table tbody > tr").each(function () {
                var tr = $(this);
                var adminOnly = $(tr).find('td[data-attribute="cr42c_adminonly"]').attr("data-value");
                if (adminOnly == "true") {
                    tr.remove();
                }
            });
        }
    });
});

If I open the lookup again via Portals, these are the options that I am presented with:

Let’s explore the JavaScript code:

  • I am finding the modal associated with the id parentcustomerid_lookupmodal and injecting a function to the OnLoad event;
  • Optionally, I am hiding the Admin Only column, as I want that column to be for technical purposes only;
  • Using Liquid, I am checking if the user contains the Administrators role;
  • Finally I am looping through the list and checking the value for my Admin Only column, and completely removing the <tr> element in case it’s not supposed to be shown.

Here are a few other business scenarios for which this approach might be applicable:

  • Dynamic filter based on a parent record (performing a query via oData or FetchXML to retrieve the related records);
  • Filter records on an Associate modal – for this we just need to change how to assign the list variable:

From:

var list = $("#parentcustomerid_lookupmodal").find(".entity-lookup").find(".entity-grid").eq(0);

To:

var list = $(".associate-lookup").find(".entity-grid").eq(0);

Conclusion

I really wish we were able to select which view to display in Portal lookups, as well as set them dynamically. Perhaps Microsoft will add this feature in the future, but in the meantime this JavaScript code is a good solution for filtering lookup controls.

I hope this tip comes in handy for your Power Apps Portals project.

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

Power Apps Portals – JavaScript Tip #03 – Load Edit modal after record creation

Hi

A very common business requirement I see is to create a record as well as related (1:N) records within the same page. By design, in Power Apps Portals (as well as Dynamics 365 / CDS / Dataverse) we need first to create the main/parent record to be able to create any related records. There are a few different ways to implement this, for example:

  • Web Forms (or Entity Forms with redirect) – having the initial step to the main record creation, and then moving to another step/page with the subgrid
  • Web API – the new Web API allows us to create multiple records in the same request, but requires a good bit of custom development

The above might be enough for most scenarios, but I want to explore here a different one that is very common. Let’s say we have an Entity List (or subgrid) with both Create and Edit actions enabled using an Entity Form modal instead of opening a Web Page – how can we fix this problem then?

We can achieve this requirement by adding a JavaScript on the Entity List (or Entity Form containing the subgrid if that’s your case). The idea of the JavaScript code is to check if there is any created record on every time the list is loaded, and force the Edit Entity Form to open, this will allow the user to enter the related records without having to click on the record. Let’s take a look at how this will look to the end-user:

Technical Implementation

I will explain here, at a high-level, my implementation:

  • I have two global variables to help in my code
    • existingRecordList – this is an array that I will store the existing list items whenever loaded
    • firstLoad – this is a simple variable to control when the page is being loaded for the first time
  • Then I inject a function (CheckNewRecord) to the onload event of my Entity List
  • These are the steps of my main function (CheckNewRecord):
    • Initiate the existingRecordsList in case this is the first load
    • Compare the size between existingRecordsList and Entity List records
    • I am creating a new temporary array to avoid conflicting with my global array
    • For each item of my Entity List:
      • Add the record ID to the temporary array
      • Ignore if this is the first time loading or if we already found which one is the new record
      • Check if the ID doesn’t exist in the global array
      • If it doesn’t exist, it means we found the new record, so we can store that in a separate variable (newRecordId) as well as marking that we already found the new record
    • Reload the global array with the temporary array; this is necessary for the next loads of the Entity List
    • If we found a new record, we need then to open the Edit modal/Entity Form
      • Via jQuery I am finding the element with the ID of the new record, and triggering the click event for the “details-link” class
    • Finally set the firstLoad control variable to false
var existingRecordsList;
var firstLoad = true;

$(document).ready(function() {

    var list = $(".entitylist.entity-grid").eq(0);
    list.on("loaded", CheckNewRecord);
});

function CheckNewRecord() {

    var list = $(".entitylist.entity-grid").eq(0);
    var newRecordFound = false;
    var newRecordId;

    // first load
    if (firstLoad) {
        existingRecordsList = [];
    }

    if (existingRecordsList.length != $(list).find('table tbody tr').length) {

        var tempRecordList = [];

        $(list).find('table tbody tr').each(function() {
            var id = $(this).attr("data-id");
            tempRecordList.push(id);

            if (!firstLoad && !newRecordFound && existingRecordsList.indexOf(id) < 0) {
                newRecordId = id;
                newRecordFound = true;
            }
        });
        // reload global variable with items in temp list
        existingRecordsList = tempRecordList;

        if (newRecordFound && !!newRecordId) {
            // open edit modal
            var newRecordElement = $(list).find("[data-id='" + newRecordId + "']");
            $(newRecordElement).find("a.details-link").eq(0).trigger("click");
        }
    }
    firstLoad = false;
}

Conclusion

The idea of this post was more to show tricks that we can do with an Entity List/Subgrid by applying JavaScript code. This is not limited to opening the Edit Entity Form after the creation, but really anything you might need to perform via JavaScript once a record is created and you don’t want to re-load the page.

This works pretty well with an Entity List/Subgrid containing a small number of records. But be mindful that in case of a large list, this might not work as the new record might not be in the current page.

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – JavaScript Tip #02 – Set Attributes Read-Only

Hello and welcome to another JavaScript tip for Power Apps Portals.

In this post I will show you how to set attributes read-only in your Entity Form / Web Form.

A generic way to set an attribute as read-only would be the following:

$("#<attribute name>").attr("readonly", true);

But depending on the datatype of your attribute, some additional changes to the HTML elements might be required. For example, a lookup field is normally rendered with a search button, or a datetime field has the datetime picker next to the input control:

Instead of re-writing the same code every time, let’s create some generic functions passing the field name as parameter, as well as a true/false flag to define if the field should be read-only. We will create a few different functions, according to the field datatype:

Set DateTime ReadOnly

SetDateTimeFieldReadOnly = function (fieldName, readOnly) {
    if (readOnly) {
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").attr("readonly", true);
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").css("cursor", "not-allowed");
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").on("mousedown", function (e) { e.preventDefault(); return false; });
    } else {
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").attr("readonly", false);
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").css("cursor", "default");
        $('#' + fieldName).siblings("div.datetimepicker").find("input, .input-group-addon").off("mousedown");
    }
};

Set Lookup ReadOnly

SetLookupFieldReadOnly = function (fieldName, readOnly) {
    if (readOnly) {
        $('#' + fieldName).siblings("div.input-group-btn").find("button").prop("disabled", true);
        $('#' + fieldName).siblings("div.input-group-btn").hide();
    } else {
        $('#' + fieldName).siblings("div.input-group-btn").find("button").prop("disabled", false);
        $('#' + fieldName).siblings("div.input-group-btn").show();
    }
};

Set Checkbox ReadOnly

SetCheckboxFieldReadOnly = function (fieldName, readOnly) {
    if (readOnly) {
        $('#' + fieldName).prop("disabled", true);
    } else {
        $('#' + fieldName).prop("disabled", false);
    }
};

Set Radio Button ReadOnly

SetRadioFieldReadOnly = function (fieldName, readOnly) {
    if (readOnly) {
        $('#' + fieldName).find("input[type='radio']").prop("disabled", true);
    } else {
        $('#' + fieldName).find("input[type='radio']").prop("disabled", false);
    }
};

Set Dropdown ReadOnly

SetDropdownFieldReadOnly = function (fieldName, readOnly) {
    if (readOnly) {
        $('#' + fieldName).attr("readonly", true);
        $('#' + fieldName).css("pointer-events", "none");
    } else {
        $('#' + fieldName).attr("readonly", false);
        $('#' + fieldName).css("pointer-events", "auto");
    }
};

We still need one more function to cater for any other datatype. This will be the main function that we will make the call and we need one additional parameter, representing the field datatype.

This function will validate the field type and call the appropriate function we created above (via switch case), finally having the generic read-only function in the default instruction:

Set Field ReadOnly Function

SetFieldReadOnly = function (fieldName, readOnly, type) {
    try {
        type = type.toLowerCase();

        switch (type) {
            case "date":
            case "time":
            case "datetime":
                SetDateTimeFieldReadOnly(fieldName, readOnly);
                break;
            case "lookup":
                SetLookupFieldReadOnly(fieldName, readOnly);
                break;
            case "checkbox":
                SetCheckboxFieldReadOnly(fieldName, readOnly);
                break;
            case "radio":
                SetRadioFieldReadOnly(fieldName, readOnly);
                break;
            case "dropdown":
                SetDropdownFieldReadOnly(fieldName, readOnly);
                break;
            default:
                if (!!readOnly) {
                    $("#" + fieldName).attr("readonly", true);
                    $("#" + fieldName).css("cursor", "not-allowed");
                    $("#" + fieldName).on("mousedown", function (e) { e.preventDefault(); return false; });
                } else {
                    $("#" + fieldName).attr("readonly", false);
                    $("#" + fieldName).css("cursor", "default");
                    $("#" + fieldName).off("mousedown");
                }
                break;
        }
    }
    catch (err) {
        console.error("Error SetFieldReadOnly: " + err.message);
        return;
    }
};

Now you can combine all JavaScript code above, saving as a .js file (and upload as a Web File), or a Web Template record, or a Content Snippet record, etc. Then refer to it in any Portal page, or even the Tracking Code Content Snippet (so it can be used in every page) – and that’s it, the functions are ready to be used in your Portals.

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – Field Label Position

Hi,

In this post I will show you how to set the field label position within an Entity Form/Web Form in Power Apps Portals.

If you are familiar with Dynamics 365, you probably are aware that we can define which position to place a field label within a form. The positions available are:

  • Side: label and input control are positioned side-by-side
  • Top: label and input control are positioned as top-bottom

Unfortunately, Power Apps Portals ignores this definition and always renders your form as top-bottom positioning:

I will show you how to change this behavior to render your field label as side-by-side position. We can achieve this via JavaScript or CSS:

Via JavaScript/jQuery

If you need to set only for a specific form or perhaps specific section in your form, you can use the following jQuery function (just relace the sectionGeneral below for your section name):

$("table[data-name|='sectionGeneral']").find(".control").css("clear", "none");
$("table[data-name|='sectionGeneral']").find(".control").css("float", "right");

Via CSS

If you want to setup this behavior for every Entity Form/Web Form within your Portals, or perhaps for an entire Web Page, you can set it via CSS (you can place this in your .css file or the Web Page custom css field):

.section tbody tr td div.control{
    clear: none !important;
    float: right;
}

Now refresh your Portals Web Page and you will have the below rendered form (don’t forget to clear the cache):

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – JavaScript Tip #01 – Hide & Show Elements

Power Apps Portals allows us to add custom JavaScript/jQuery code to manipulate behaviour within the Portals website.

I am starting a series of quick posts with snippets of JavaScript/jQuery code to help you with your Power Apps Portals implementation.

When talking about client-side customization, there is no single way of interacting with the page elements, there are several ways to achieve same results, in this post I will give an example on how to hide & show elements in your page, so here we go:

Hide & Show Fields

$("#<field name>").closest("td").find("div.control, div.info").hide(); // show();

Hide & Show Section

$("table[data-name='<section name>']").parent().hide(); // show();

Hide & Show Tab

$("div[data-name='<tab name>']").prev("h2.tab-title").hide();  // show();
$("div[data-name='<tab name>']").hide();  // show();

Hide & Show Entity List/Sub-grid column

var list = $(".entity-grid")
list.on("loaded", function () {
	list.find("th:contains('<column display name>')").hide(); // show();
	list.find("td[data-th='<column display name>']").hide(); // show();
}); 

Hide & Show Option Set value

$("#<field name> option[value='< option set value>']").hide(); // show();

Do you have any suggestions or requests for another JavaScript tip? Let me know in the comments below.

Posted in JavaScript/jQuery Plugins, Power Apps Portals

Power Apps Portals – Adding field Mask

Currently in Power Apps Portals, we can’t set a mask for a field, which usually improves a lot the UX. In this post I will show you how a simple example to achieve that using JavaScript/jQuery.

In my case, I am adding a whole number field to the Profile page/form.

Download jQuery Mask Plugin

For this example I am using the following jQuery Plugin: https://igorescobar.github.io/jQuery-Mask-Plugin/

  • Download the plugin and locate the jquery.mask.min.js file under the “dist” folder
  • Create a Web File in your CDS environment
    • Note that by default you can’t upload a .JS file, I am going to rename the file to .ES to bypass this validation (you can also change your environment settings but this is not recommended) – but keep the Partial URL as .JS
    • I am going to set the parent page as Home
    • I am going to set it to be hidden from site map and excluded from search
Mask01
Mask02
Mask03

Adding JS library reference

We need to reference the JS, open your Web Page (content page) and add the following to your HTML “copy” field:

Mask05

You can also add the above line to the “Tracking Code” Content Snippet, and this would be loaded in every page, in my example this JS would only be loaded in the Profile Web Page.

Adding the mask via JavaScript / jQuery 

Still in your content page, navigate to the Advanced tab and add the following code:

Mask04
$(document).ready(function () {
       AddWholeNumberMask();
});
AddWholeNumberMask = function()
{
         $("#ollie_wholenumber").mask("00000", { placeholder: "i.e.: 12345" });
};

Result

This is how you will see your mask in the Portal, and the mask will prevent the user from typing non-number fields or more than 5 numbers in my case.

Mask06

You can reference the below documentation for other masks (date field / money / e-mail / etc): https://igorescobar.github.io/jQuery-Mask-Plugin/docs.html

Above I am using a placeholder to indicate the user what the expected format is, but this is optional, I could have the mask just as: $(“#ollie_wholenumber”).mask(“00000”);