Microsoft Power Pages - Importing JS Libraries

 INTRODUCTION

Microsoft Power Pages is a platform for developing web sites - from very simple... to surprisingly complex.  However, with additional complexity comes the need to leverage external JavaScript libraries.  We'll be taking a look at that in this brief article.

ASSUMPTIONS

This article will be most useful to you if you already have some working knowledge of

  • Power Pages platform
    • Creating a Site and Adding Pages/Components
  • HTML
    • Specifically, the Script tag
  • Portal Management
    • All of the information surrounding a Power Pages site is stored in a D365 environment and that data is accessible through the D365 Portals application
SCENARIO

We have a web page on our site that needs to include an HTML editor.  There are are many (paid and free) great editors out there and we'll be using the Jodit editor in this example.

HOW TO

To implement a Jodit editor on a web page, we need to import the Jodit CSS and JavaScript files.  Once we have those libraries, we can then properly surface a nice editor.

SOME CUSTOM HTML

The Jodit editor requires an HTML textarea control that it transforms into a nice HTML editor.  We started by adding a 1 column section to the Page as a place to put the textarea and then inserted the markup there.  Here's the HTML for the textarea, including the placeholder section added by the platform:

 <div class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;">  
  <div class="container" style="padding: 0px; display: flex; flex-wrap: wrap;">  
   <div class="col-md-12 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 310px; word-break: break-word;">  
    <div class="mb-3" id="body">  
     <label for="editor">Dashboard Message</label>  
     <textarea id="welcomeContent" style="width:100%;"></textarea>  
   </div>  
   </div>  
  </div>  
 </div>  

METHOD 1 - ADD FILES FROM PORTAL MANAGEMENT

In this approach, we'll link to the Portal Management application by clicking the ellipses on the Power Pages left nav menu.  This will open a new browser Tab.

There is a great write up on this approach, so I'm not going to recreate the wheel


Follow this process for the files needed (CSS/JS) for the library you need.

METHOD 2 - ADD FILES DYNAMICALLY FROM PAGE JAVASCRIPT

This approach is equally effective but has the advantage of being able to consume files directly from CDN or other global providers without having a local copy of the files.  If the library you need is not available from a CDN, the first method will need to be used.

For the Page in question, 
  • click the Edit code button to open Visual Studio Code for Web.
  • click the JavaScript file on the left explorer panel
    • this loads the file in the editor
  • The following code will dynamically load the Jodit JS/CSS from the CDN
  • This code will initialize the editor

   let welcomeEditor = null;  
   
   var joditCSS = document.createElement("link");  
   joditCSS.rel = "stylesheet";  
   joditCSS.type = "text/css";  
   joditCSS.href = "//cdnjs.cloudflare.com/ajax/libs/jodit/3.19.3/jodit.min.css";  
   document.getElementsByTagName("head")[0].appendChild(joditCSS)  
   
   let joditScript = document.createElement("script");  
   joditScript.type = "text/javascript";  
   joditScript.src = "//cdnjs.cloudflare.com/ajax/libs/jodit/3.19.3/jodit.min.js";  
   document.head.appendChild(joditScript);    
   
   document.addEventListener("DOMContentLoaded", function(){  
     console.log("initRegistrations started");  
     document.getElementById('email').addEventListener(  
       'change',   
       email_Change  
       );   
      
     document.querySelectorAll('.fldRequired').forEach(c => {  
       c.addEventListener('change', event => {  
         validateRequired(c);  
       });  
     });  
     
     setTimeout(() => {  
       document.querySelectorAll('.fldRequired').forEach(c => {  
         validateRequired(c);  
       });  
     }, 1000);  
   
       welcomeEditor = Jodit.make("#welcomeContent");  
   
   });  

THE RESULT

With all of this in place, you should get a page that renders the Jodit editor as expected





SUMMARY

Both methods effectively import the libraries you specify and the method you choose will likely be based on the answer to some basic questions
  • Is this library used by other pages?
    • Get a local copy and use Method 1
  • Is this library available from a CDN
    • Consider Method 2
  • Are the timing issues with the load of the files?
    • Use Method 1

Microsoft Power Pages - Roll Your Own UI

INTRODUCTION

As part of getting my feet wet with Power Pages, I'm creating a simple site that does most of things I'd expect a site might need to do.  The more I play with the platform, the more potential I see for creating sites that range in complexity from simple to sophisticated.  This is a GREAT feature - make simple things simple to do and make difficult things possible.  

In this post, I'm going a little deeper with a discussion of bypassing the platform's built-in integration with Dataverse to performing customizations that let you roll your own UI and talk directly with SQL Server...  no Dataverse required!

ASSUMPTIONS

This article will be most helpful if you have some familiarity with the following concepts

  • HTML
    • used to create our custom UI
  • CSS (just a little in-line styling)
    • used  to style the UI
  • JavaScript
    • used to manipulate the HTML DOM
    • and call our Power Automate Flow Endpoints
  • Power Automate Flows
    • to get data from the UI and send it to SQL Server
      • Email validation
      • New user registration creation
    • but you could use any API you like
  • Power Pages
    • we won't be explaining the basics of creating a site and adding Pages/Components
  • Bootstrap (ugh... 3.x)
    • used to style the pages
    • used to implement a Modal message box
SCENARIO

As shown in a previous post, we have a simple site that promotes a software product.  On the Home page, there is an immediate call to action: Register.  The Registration page 

  • Prompts for basic information
  • Validates a non-duplicated email address
    • using a Power Automate Api call
  • Creates the new User in the database
    • using a Power Automate Api call
      • Adds the user
      • Assigns appropriate roles
      • Sends a confirmation email with Quick Start attachment
  • Uses a Bootstrap Modal 
    • for Validation/Api messages
LET'S DO IT - QUICK POWER AUTOMATE FLOW OVERVIEW

The Email Validation Flow

This flow is simple enough, it receives a JSON payload with an "email" property for the value to be checked for duplicates.  It returns an empty array (if no duplicates are found) or an array of the duplicates.


The Create Registration Flow

This flow receives a JSON payload with the registration data and creates the new user record as well as appropriate roles for the user.  It then sends a confirmation email with a QuickStart.pdf attachment.


In the remainder of this article, we'll refer to these two flows as the API endpoints being called when we need to do some database interactions.

A PEEK AT A POWER PAGE

Every page that is added to a Power Pages site has 3 important files that define how the page looks and works:
  • HTML file
    • defines the layout and elements on the page
    • populated with HTML markup as you add elements (like text, images, etc) to the page
  • CSS file
    • defines the styling of the page and elements
    • empty by default
  • JavaScript file
    • defines behaviors
    • empty by default
You access these 3 files by clicking the Edit code button on the page Designer to open Visual Studio Code for Web in a new browser tab


It will look something like this.  When you select a file, as shown below, that file will be loaded into the VS Code editor for you to make any changes you like


If you've done any web development, your eyes should be getting wide at this point.  With access to these files, you can customize a site page to do lots of things that you couldn't otherwise accomplish.... like creating a custom Registration form that interacts with SQL Server through an API....  And that's exactly what we'll be showing next!

ADD A SECTION FOR THE FORM

The Registration page we started with a simple page that we laid out using the out-of-box tools - it's just a couple of text controls and an image.  We then used the Add Section feature to create a new section under the page header.

When you add a new section, you are prompted to select an element to put in the section - we didn't do this!  The result was a placeholder in the HTML markup that we used to contain our custom form.  In the HTML file, the section started with these tags:


This gives us a perfect place to put our HTML for the Registration fields and submit button.  After the custom HTML changes (shown below), it will look like this



THE CSS FILE

For this exercise we did mostly in-line styling; however, we did add the following to the empty CSS file:

 label {  
   text-transform: uppercase;  
   font-weight: 600;  
   font-size: 80%;  
 }  
   
 .fldRequired {  
   border-left: 5px solid red;  
 }  
   
 .fldRecommended {  
   border-left: 5px solid yellow;  
 }  
   
 .fldValidated {  
   border-left: 5px solid green;  
 }  
The label simply standardizes how the field labels look.  The 3 classes help with making required field validations visually effective.

THE HTML FILE

In the placeholder section mentioned above, we added the markup for the fields used to gather Registration information from the visitor and a DIV at the bottom for the Bootstrap modal.  The full HTML markup is shown here and the customized sections are commented:

 <div data-component-theme="portalThemeColor3" class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;">  
  <div class="container" style="display: flex; flex-wrap: wrap;">  
   <div class="col-md-6 columnBlockLayout" style="flex-grow: 1; min-width: 310px; word-break: break-word; display: flex; flex-direction: column; margin: 25px 0px; padding: 16px;">  
    <div class="row sectionBlockLayout" style="display: flex; flex-wrap: wrap; padding: 8px; margin: 0px; min-height: 15px;"></div>  
    <div class="row sectionBlockLayout" style="display: flex; flex-wrap: wrap; padding: 8px; margin: 0px; min-height: 15px;"></div>  
    <h3 style="text-align: left;">Registration</h3>  
    <p style="text-align: left;">Want to see what iQueue can do for you?&nbsp; Take a few seconds to register and you'll get an email with login information and a Quick Start guide so that you can kick the tires.&nbsp; The "demo" site is fully functional and waiting for you - elevate your Process Management today!</p>  
   </div>  
   <div class="col-md-6 columnBlockLayout" style="flex-grow: 1; min-width: 310px; word-break: break-word; display: flex; flex-direction: column; margin: 25px 0px; padding: 16px;"><img src="/Registration/register.jpg" alt="" name="register.jpg" style="width: 100%; height: 295px; max-width: 100%; box-shadow: 0px 0px 6px rgb(0, 0, 0); border-radius: 5px;" /></div>  
  </div>  
 </div>  
 <div class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px; background-image: linear-gradient(0deg, rgba(161, 159, 157, 0), rgba(161, 159, 157, 0)); border: 0px solid rgb(161, 159, 157); border-radius: 0px;">  
  <div class="container" style="padding: 0px; display: flex; flex-wrap: wrap;">  
   <!--the customized form section-->  
   <div class="col-md-12 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 310px; word-break: break-word; margin: 20px 0px; padding: 16px;">  
    <div class="mb-3" style="margin-bottom: 15px;"><label for="firstName">First Name</label><input type="text" id="firstName" class="form-control fldRequired" /></div>  
    <div class="mb-3" style="margin-bottom: 15px;"><label for="lastName">Last Name</label><input type="text" id="lastName" class="form-control fldRequired" /></div>  
    <div class="mb-3" style="margin-bottom: 15px;"><label for="email">Email</label><input type="text" id="email" class="form-control fldRequired" /></div>  
    <div class="mb-3" style="margin-bottom: 15px;"><label for="company">Company</label><input type="text" id="company" class="form-control" /></div>  
    <div class="mb-3"><button onclick="submitRegistration()" class="btn btn-primary">Submit</button></div>  
   </div>  
   <!--end customized form section-->  
  </div>  
 </div>  
 <!--error modal-->  
 <div class="modal fade" id="errorModal" tabindex="-1" role="dialog">  
  <div class="modal-dialog" role="document">  
   <div class="modal-content">  
    <div class="modal-header">  
     <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>  
     <h4 class="modal-title" id="errorModalLabel">...</h4>  
    </div>  
    <div class="modal-body" id="errorModalBody">  
     ...  
    </div>  
    <div class="modal-footer">  
     <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>  
    </div>  
   </div><!-- /.modal-content -->  
  </div><!-- /.modal-dialog -->  
 </div><!-- /.modal -->  

When you break this down, you see that there are 2 divs
  • a Data Entry div
    • we use this to gather the data from the visitor and then pass it along to the API endpoints
  • a Bootstrap Modal div
    • for messaging
It looks like this when the custom HTML is added




THE JAVASCRIPT FILE

The following JavaScript code provides the functionality we're looking for.  There is nothing too fancy here but it does include some required field validation and the accompanying visual styling manipulations.  
   console.log("initRegistrations started");  
   document.getElementById('email').addEventListener(  
     'change',   
     email_Change  
     );   
    
   document.querySelectorAll('.fldRequired').forEach(c => {  
     c.addEventListener('change', event => {  
       validateRequired(c);  
     });  
   });  
   
   setTimeout(() => {  
     document.querySelectorAll('.fldRequired').forEach(c => {  
       validateRequired(c);  
     });  
   }, 1000);  
     
     
     
   function email_Change(){  
     //some code here to see if that email is in play already  
     console.log("email_Change executed");  
     let payload = {};  
     payload.email = document.getElementById('email').value;  
   
     if (payload.email.length === 0){return;}  
   
     let url = "https://prod-84.westus.logic.azure.com:443/workflows/abc/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=123";  
     executeApiRequest(payload, url,   
       function(retVal){  
         if (retVal.length > 0){  
           document.getElementById('email').value = "";  
           validateRequired(document.getElementById("email"));  
           popModal("Duplicate Email", "The email you entered is already in use - please enter a different email address.");  
         }  
       },   
       function(error){},   
       true);  
   
   }  
   
   function executeApiRequest (parameters, url, successCallback, errorCallback, async) {  
     let uri = url;  
     let req = new XMLHttpRequest();  
     req.open("POST", uri, async);  
     req.setRequestHeader("Accept", "application/json");  
     req.setRequestHeader("Content-Type", "application/json; charset=utf-8");  
     req.setRequestHeader("Cache-Control", "no-cache'");  
   
     if (async) {  
       req.onreadystatechange = function () {  
         if (this.readyState === 4 /* complete */) {  
           req.onreadystatechange = null;  
           if (this.status === 200) {  
             let data = JSON.parse(this.response, dateReviver);  
             successCallback(data);  
           }  
           else {  
             errorCallback(this.responseText);  
           }  
         }  
       };  
       parameters ? req.send(JSON.stringify(parameters)) : req.send();  
     }  
     else {  
       parameters ? req.send(JSON.stringify(parameters)) : req.send();  
       if (req.status === 200 || req.status === 204) {  
         if (req.status === 200) {  
           let data = JSON.parse(req.response, dateReviver);  
           successCallback(data);  
         }  
       }  
       else {  
         errorCallback(this.responseText);  
       }  
     }  
   }  
   
   function dateReviver (key, value) {  
     if (typeof value === 'string') {  
       let a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);  
       if (a) {  
         return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));  
       }  
     }  
     return value;  
   }  
   
   function validateRequired(c) {  
   
     if (c.value.length > 0) {  
       c.style.borderLeft = "5px solid green";  
     }  
     else {  
       c.style.borderLeft = "5px solid red";  
     }      
   }  
   
   function submitRegistration() {  
     console.log("submit button clicked");  
       
     let reg = {};  
     reg.FirstName = document.getElementById("firstName").value;  
     reg.LastName = document.getElementById("lastName").value;  
     reg.Email = document.getElementById("email").value;  
     reg.Company = document.getElementById("company").value;  
   
     let updateOk = true;  
   
     document.querySelectorAll(".fldRequired").forEach(c => {  
       if (window.getComputedStyle(c).borderLeft.indexOf("red") >= 0 ||  
         window.getComputedStyle(c).borderLeft.indexOf("255") >= 0) {  
         updateOk = false;  
       }  
     });  
   
     if (!updateOk){  
         
       popModal("Validation", "Fields marked in RED are required")  
       return;  
     }  
    
     let url = "https://prod-176.westus.logic.azure.com:443/workflows/abc/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=123";  
     //validations passed... create the registration record  
     executeApiRequest(reg, url,   
       function(retVal){  
         popModal("Success", "Registration Succeeded!<br/><br/>Be sure to check for the Confimration email.<br/>Stand by for automatic navigation to Home page.");  
         setTimeout(function(){  
           window.location.href = "https://iqueue.powerappsportals.com/";  
         }, 10000);  
       },   
       function(error){},   
       true);  
   
   }  
   
   function popModal(title, body){  
   
     document.getElementById("errorModalLabel").innerHTML = title;  
     document.getElementById("errorModalBody").innerHTML = body;  
     $('#errorModal').modal('show');  
   
   }  

  • When the Registration page loads
    • add an Event Listener to the email field so that we can fire the function to check for duplicates
    • add an Event Listener to all required fields so that we can provide the visual cues
    • do a validation to set the required field visual (the red left border)
  • email_Change
    • sends the email value entered by the user to the Email Validation Flow and processes the result
  • executeApiRequest
    • a reusable XmlHttpRequest function to call the API Flows
  • dateReviver
    • a help for executeApiRequest to generate friendly date formats
  • validateRequired
    • sets the left border for required fields
  • submitRegistration
    • gathers the Registration information and sends it to the Create Registraion Flow
  • popModal
    • populates and displays the notifications div

OF INTEREST - BOOTSTAP INTEGRATION

Microsoft includes BOTH Bootstrap CSS and Bootstrap Bundle JS.... so you can do a lot of cool things in the UI natively.  However, this is a 3.x version of the Bootstrap stack and it definitely works a bit differently than 4.x and 5.x versions.

An example of a Modal Bootstrap message



WRAP-UP

Power Pages gives you the flexibility to keep things simple when they are simple.... or to go hog wild when you need elevated levels of complexity.  

Microsoft Power Pages - Calling a RESTful API

 INTRODUCTION

Microsoft has put a nice, new front end on their "Portals" technology... and they now call it "Power Pages".  This is really a great step forward because it abstracts much of the complexity that underlays the Portals platform.  The new UI gives you a "point-and-click" designer with (limited) access to properties of the elements you add to a page.  If you are a pro web designer, you'll find it limiting...  otherwise, you'll be amazed that you can create a nice web site without having to know much at all about web development (assuming your site doesn't have do much other than support site navigation).

WHAT WE WON'T COVER

It's not the purpose of this article to cover in detail the creation of a RESTful Api endpoint.  For the purposes of this article, we created a simple Power Automate triggered by an HTTP Request.  This process generates an Endpoint Url which we then used in our XmlHttpRequest.

WHAT WE ASSUME

We won't be talking about the basics of getting into Power Pages and creating a site and adding Pages or Components...  This article isn't for you if you haven't yet covered those basics.  Although we do discuss the Dataverse in this article, it would be helpful if you are already at least a little familiar with what it is and how to work with tables, fields, views and forms.

SOME OF THE NICE STUFF in POWER PAGES

  • Templates
    •  A variety...  just to get you up and running
  • Page Layout
    • Based on Bootstrap and very easy to work with
  • Elements
    • Every kind of thing you'd want to add to a page... including IFrames, Images, Videos, etc
  • Authentication
    • If you want restrict access to your site, Power Pages lets you easily implement one of any number of Authentication methods including:
      • Azure AD
      • Google
      • Facebook
      • Twitter
      • Ad Hoc (Forms based)
      • Microsoft Account
      • Etc
    • Or... no Authentication required
      • Which makes your site publicly available
  • Customization
    • Per Page, you can
      • add your own JavaScript
      • add your own CSS
      • customize the HTML
    • Per Site, you can
      • Associate your site with a custom Domain :)
  • Dataverse Integration
    • When you have to work with data, seamless integration with the Dataverse makes it pretty simple
These are just some of the highlights....  you can dig in yourself by starting a free trial of the platform: https://powerpages.microsoft.com

OUR SCENARIO

We have a page that asks a visitor to Register.  We want to make sure that they are entering a unique Email address based on Email addresses in the existing Registration table.  

OUR SIMPLE SITE 

We start our site with a simple Home page 


The Home page includes a "call to action" to register.  When a visitor clicks that button, the Registration page is opened


We're not asking for much here...  but we do want to make sure that the email address provided is unique within our Registration table in the Dataverse.  In this case, we'll check the email BEFORE the form data is submitted.

HOW-TO OVERVIEW

To accomplish our goal, we're going to need to add some custom JavaScript to our Registration page.  In the Designer, every page includes a link to "Edit code" near the top-right of the designer canvas


When you click the Edit code button, a new browser tab opens with Visual Studio Code for Web and any resources associated with the current page available to edit (html, js, css).



The html file will be populated with the markup for your page - which you can edit!  The js and css files will be empty - just waiting for your brilliant customizations.  The changes you make here will only apply to the specific Power Page loaded in the Designer when you clicked the Edit code button.

Now that we know how to add some custom JavaScript....  exactly what JavaScript to we need to add?

  • We need to be able to run a validation process when the value in the Email field changes
    • So we need to add an event listener to the Email field
    • Fortunately, a Power Page form based on a Dataverse table gives us easy accessibility to each field on the form 
  • That event listener, when triggered, will pass the Email value to a REST Api endpoint
    • This will be done with XmlHttpRequest in JavaScript
    • the Endpoint gets a list of any records in our Registration table and passes that back to the Power Page
  • The Power Page checks the value returned by the REST Api and responds accordingly

A WORD OR TWO ABOUT DATA

When we created the Registration form (see above), we designated a Dataverse table for the form to be based upon.  In fact, you can ONLY create a form based on a Dataverse table.  The Dataverse table and it's related Forms and Views come into play here.  When you create the form in the Designer, you have to specify the table and form from the Dataverse.  This way, Power Pages has somewhere to put the data AND a reference for what the form should look like.  All of this can be done from the Designer.  Just be aware... you don't get to lay out your form in the Designer.  You create the form layout as part of the Dataverse table definition... and Power Pages renders the form you designate.


The image above shows the Designer in Data mode with our Registration table/form selected.  As shown, this form is what is eventually rendered by the Registration Power Page.

THE CODE

With our Registration page selected in the Designer, we click the Edit code button to open Visual Studio Code for Web.  Click on the JS file to open the empty editor.

The first snippet we need is the Event Listener for the Email field.  In our case, the ID for the Email field is cr2d2_email (which is the logical name for this field in the Dataverse).  

   document.getElementById('cr2d2_email').addEventListener(  
     'change',   
     email_Change  
     );   
This is reasonably straight-forward.  We're hooking a call to a function called "email_Change" when the Email field value changes.

Now we need the "email_Change" function.  This function will get the value for Email entered by the visitor and then pass that to a function to make the REST Api call.

   function email_Change(){  
     let payload = {};  
     payload.email = document.getElementById('cr2d2_email').value;  
   
     if (payload.email.length === 0){return;}  
   
     executeApiRequest(payload,   
       function(retVal){  
         if (retVal.length > 0){  
           document.getElementById('cr2d2_email').value = "";  
           alert("That email is alreday in use.... please select another.");  
         }  
       },   
       function(error){},   
       true);  
   
   }  
As shown, we create a payload object and populate a property called "email" with the value from the form.  This is specific to the structure of our Api - the Api expects to receive this exact payload.  If Email is blank, we exit.

All good so far... but now we need a couple of functions to do the XmlHttpRequest part of the process.  As shown above, the function we need to create next is called "executeApiRequest".  That function will in turn call a helper function called "dateReviver" which helps to format dates in a friendly way.

   function executeApiRequest (parameters, successCallback, errorCallback, async) {  
     let uri = "https://prod-84.westus.logic.azure.com:443/workflows/xxxxxxxxxxxxxxxxxxxxx11111111abc999/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=uLAnPelNPd2RvVwCbri7j9ctQnwPO7ev54l6yU9UJ2M"; //path to api goes here  
     let req = new XMLHttpRequest();  
     req.open("POST", uri, async);  
     req.setRequestHeader("Accept", "application/json");  
     req.setRequestHeader("Content-Type", "application/json; charset=utf-8");  
     req.setRequestHeader("Cache-Control", "no-cache'");  
   
     if (async) {  
       req.onreadystatechange = function () {  
         if (this.readyState === 4 /* complete */) {  
           req.onreadystatechange = null;  
           if (this.status === 200) {  
             let data = JSON.parse(this.response, dateReviver);  
             successCallback(data);  
           }  
           else {  
             errorCallback(this.responseText);  
           }  
         }  
       };  
       parameters ? req.send(JSON.stringify(parameters)) : req.send();  
     }  
     else {  
       parameters ? req.send(JSON.stringify(parameters)) : req.send();  
       if (req.status === 200 || req.status === 204) {  
         if (req.status === 200) {  
           let data = JSON.parse(req.response, dateReviver);  
           successCallback(data);  
         }  
       }  
       else {  
         errorCallback(this.responseText);  
       }  
     }  
   }  
   
   function dateReviver (key, value) {  
     if (typeof value === 'string') {  
       let a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);  
       if (a) {  
         return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));  
       }  
     }  
     return value;  
   }  
We created a simple Power Automate triggered by HTTP Request.  The Uri that you see is generated by Power Automate when saved.  This "flow" accepts the payload (email address) and queries the Registration table for any rows with the same email...  then returns the results.  The Power Page evaluates the return of the Async call and then displays an Alert if any matching rows are returned.  When the Alert is dismissed, the Email field is cleared.

WRAP UP

When the code snippets have been added, save the changes by clicking the Visual Studio Code menu button (far upper left) and selecting Save.  When you close the Visual Studio Code tab, you are prompted by the Power Page Designer to Sync... do so.  You can now test your page!  

Microsoft Power Pages - Importing JS Libraries

  INTRODUCTION Microsoft Power Pages is a platform for developing web sites - from very simple... to surprisingly complex.  However, with ad...