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!  

No comments:

Post a Comment

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