Code Customizations
The behavior of Power Pages websites can be manipulated in multiple ways by using custom code, namely Javascript files.
In the article below, we will walk through some of the options, how you can override or extend the behavior of your sites, pages and components.
Integrating 3rd party libraries
Power Pages allows you to upload your own CSS files to alter the look and feel of your portal. The stylesheets are applied on all pages, but it is possible to apply custom stylings for a specific page only as well. The behavior is different with Javascript files. You can upload your own Javascript files as Web Files to a portal, however they will not be referenced on any of the pages, unless you do so.
Javascript files
To include a Javascript file on all pages of your portal - whether it is your own library or a 3rd party library - you can do it as follows:
- (Optional for own libraries) Create a new Web File and upload your Javascript file. The Parent Page can be the Home of your portal, depending on your selection, the URL of the file will change.
- Open Web Templates and select the Footer template for your portal.
- At the very bottom of the source code, include a
<script>tag referring the Javascript file you would like to load.
// Example for an own library
<script src="https://myportal.powerappsportals.com/my-site.js"></script>
// Example for the select2 3rd party library
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js" integrity="sha512-2ImtlRlf2VVmiGZsjm9bEyhjGW4dU7B6TNwh/hx/iSByxNENtj3WVE6o/9Lj4TJeVXPi4bnOIMXFIJJAeufa0A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
CSS files
According to the W3C standards, a <style> element must always be included inside the <head> of the document. The same stand for the <link> elements.
At the time of the writing, changing the <head> of the template is not supported (shouldn't be mistaken with the Header Web Template, which is part of the <body>). A workaround might be to manipulate the template with a custom Javascript, adding the stylesheet programmatically, however this should be considered as a hackfix only!
Instead, we will use the @import rule in an uploaded CSS file to import external stylesheets from other sources:
- Create a custom CSS file for your template.
- Add an
@importrule to your stylesheet file contents:
/* Example for the select2 3rd party library */
@import url("https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css")
Page Components
Custom Validators
Power Pages by default adds validators to required fields on your forms to prevent saving of a record when these fields are empty. Custom validators can also be added to a page to add more complex validation logic to your fields.
Note that this behavior is not supported officially by Microsoft and may change in the future.
Use the following function to add custom validators to your page:
/**
* Add a custom validation logic to your page.
* @param fieldName Name of the field (attribute) to associte the validator with.
* @param validatorName A unique name for the validator (e.g.: ContainsDigitsValidator).
* @param errorMessage Message to be returned in case the validationFunction returns false.
* @param validationFunction Validator function, returning true on correct and false on incorrect input.
* @returns
*/
function addCustomValidator (fieldName, validatorName, errorMessage, validationFunction) {
if (typeof (Page_Validators) == 'undefined') return;
let labelName = "#" + fieldName + "_label";
let newValidator = document.createElement('span');
newValidator.style.display = "none";
newValidator.id = fieldName + validatorName;
newValidator.controltovalidate = fieldName;
newValidator.errormessage = "<a href='" + labelName + "'>" + errorMessage + "</a>";
newValidator.validationGroup = "";
newValidator.initialvalue = "";
newValidator.evaluationfunction = validationFunction;
Page_Validators.push(newValidator);
$("a[href='" + labelName + "']").on("click", function () { scrollToAndFocus(labelName, fieldName); });
}
Entity Grid
Custom Event Handlers
Custom JavaScript logic can be applied to your entity grids, using the exposed loaded event which is fired on load, filtering, paging - all times when the view is refreshed.
Use the code as follows to subscribe to the event:
$(".entitylist.entity-grid").on("loaded", function () {
// Your code here
});
The event handler can be used to achieve outcomes, such as making the rows of the table clickable:
$(".entitylist.entity-grid").on("loaded", function () {
let $tableRows = $(this).find('tbody tr');
$tableRows.on("click", function () {
window.location = relativePath + $(this).data('id');
});
$tableRows.css('cursor', 'pointer');
});
Form Components
Date Picker
Power Pages uses a slightly customized version of the bootstrap-datepicker library, which supports a wide array of customization options.
We have to be careful of course when manipulating the control to prevent breaking any existing custom functionality Microsoft has implemented!
The datepicker control is not directly associated with the input field of the attribute, the DOM structure looks something as follows:
<div class="control">
<!-- The actual attribute's hidden field -->
<input type="text" id="cra64_datefield" class="datetime form-control " ... style="display: none;">
<div class="input-append input-group datetimepicker" role="none">
<!-- All datetimepicker control related fields -->
</div>
</div>
When attempting to manipulate the datetime picker, we should use the .datetimepicker DOM element associated with the attribute. Use the code as follows to retrieve the appropriate div element:
let fieldName = "cra64_datefield";
let $dateTimePicker = $('#' + fieldName).parent().children('.datetimepicker');
You can use the option methods of bootstrap-datepicker to manipulate the behavior of the component, even after it has been initialized. Use the .data("DateTimePicker") attribute to access the option methods.
Some examples of possible manipulations:
let minDate = new Date(Date.parse("2023-06-01"));
let maxDate = new Date(Date.parse("2023-07-01"));
let fieldName = "cra64_datefield";
let $dateTimePicker = $('#' + fieldName).parent().children('.datetimepicker');
// Earliest date allowed by the datepicker
$dateTimePicker.data('DateTimePicker').minDate(minDate);
// Latest date allowed by the datepicker
$dateTimePicker.data('DateTimePicker').maxDate(maxDate);
// Update currently selected value
$dateTimePicker.data('DateTimePicker').date(minDate);