Skip to main content

Notification control using PowerApps component framework and Azure SignalR

Headshot of article author Rui Santos

There has been great momentum around PowerApps component framework and numerous amazing controls are being created and shared by the community. Today I am going  cover a different kind of PowerApps component, one which is not UI focused but utilizes Microsoft Azure signalR to enable real time communication between different PowerApps instances, D365 modules, or any other system.

Please see my LinkedIn post for the demo here.

So here is how I did it.

Part 1: Create a framework component

To create the PowerApps pro-dev component please follow the documentation here.

Steps:

  • Open the Developer Command prompt of Visual Studio, in my case it is the 2019 version
  • Create a folder named NotificationComponent C:\>mkdir NotificationComponent
  • Inside the folder, NotificationComponent run “pac pcf init –namespace my.NotificationNamespace –name NotificationComponent –template field”
C:\NotificationComponent>pac pcf init --namespace my.NotificationNamespace --name NotificationControl --template field
The PowerApps component framework project was successfully created in 'C:\NotificationComponent'.
Be sure to run 'npm install' in this directory to install project dependencies.
  • We would need to install SignalR for this control, please refer to documentation  here 

Steps:

  • Run the command “npm install @aspnet/signalr” to installs the SignalR TypeScript client, which allows the component to send messages to the server

C:\NotificationComponent>npm install @aspnet/signalr

C:\NotificationComponent>npm install @aspnet/signalr
npm WARN pcf-project@1.0.0 No repository field.
npm WARN pcf-project@1.0.0 No license field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
+ @aspnet/signalr@1.1.4
added 45 packages from 51 contributors and audited 10412 packages in 14.738s


Steps:

Next step is to update the code of our component. To do so you can use your preferred IDE, I am going to use Visual Studio code.

  • Run the command: “code .” to open VS Code from the current component folder.

  • Under the folder, NotificationComponent open the component manifest file “ControlManifest.Input.xml”
  • Next step is to add desired properties to the component via this manifest file
    1. SignalRHubConnectionUrl : Input property that will define the URL for the SignalRHub
    2. MessageToSend: Input property that will contain the message we want to send
    3. outputs messages:
      • MessageReceivedType
      • MessageReceivedText
      • MessageReceivedSender
  • The final manifest file looks as follows :

  • Next step is to build the component project using the command

npm run build”.

C:\NotificationComponent>npm run build

> pcf-project@1.0.0 build C:\NotificationComponent
> pcf-scripts build
[20:46:4] [build]  Initializing...
[20:46:4] [build]  Validating manifest...
[20:46:4] [build]  Validating control...
[20:46:4] [build]  Generating manifest types...
[20:46:4] [build]  Compiling and bundling control...
.....
[20:46:6] [build]  Succeeded
  • To be able to package this control we need to create and build a solution
    1. Enter in the folder NotificationControl and create a folder called “Solutions” with “mkdir Solutions
    2. Enter into “Solutions” folder
  • Run the command

pac solution init –publisher-name developer –publisher-prefix dev

C:\NotificationComponent\NotificationControl\Solutions>pac solution init --publisher-name developer --publisher-prefix dev
CDS solution project with name 'NotificationControl' created successfully in current directory.
CDS solution files were successfully created for this project in the sub-directory Other, using solution name NotificationControl, publisher name developer, and customization prefix dev.
Please verify the publisher information and solution name found in the Solution.xml file.
  • Once the new solution project is created, you need to refer to the location where the newly created component is located. You can add the reference by using the command

pac solution add-reference –path C:\NotificationComponent

  • Run the command “msbuild /t:restore” followed by the command “msbuild

Now we are going to implement the logic in our component to do so, open index.ts location in the “NotificationComponent” in the code editor

  • Add the following code to import SignalR

  • Declare variables
private _receivedMessage: ReceivedModel;
private _notifyOutputChanged: () => void;
private _context: ComponentFramework.Context<IInputs>;
private connection: signalR.HubConnection;
private _signalRApi: string;
  • Declare the model of the messages we will receive
class ReceivedModel
{
  sender: string;
  text: string;
  type:string;
}
  • Change Init() function:
this._context = context;
this._notifyOutputChanged = notifyOutputChanged;
this._signalRApi=context.parameters.SignalRHubConnectionUrl.raw?
context.parameters.SignalRHubConnectionUrl.raw:"";

//Create the connection to SignalR Hub
this.connection = new signalR.HubConnectionBuilder()
.withUrl(this._signalRApi)
.configureLogging(signalR.LogLevel.Information) // for debug
.build();

//configure the event when a new message arrives
this.connection.on("newMessage", (message:ReceivedModel) => {
this._receivedMessage=message;
this._notifyOutputChanged();
});

//connect
this.connection.start()
.catch(err => console.log(err));
  • Create a function to send the message to SignalR called httpCall
public httpCall(data:any, callback:(result:any)=>any): void {
 var xhr = new XMLHttpRequest();
 xhr.open("post", this._signalRApi+"/messages", true);
 if (data != null) {
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(JSON.stringify(data));
 }
 else xhr.send();
}
  • Edit the method updateView. This method is going to be called when we change the “MessageToSend” property of the component
public updateView(context: ComponentFramework.Context<IInputs>): void
{
  //When the MessageToSend is updated this code will run and we send the message to signalR
  this._context = context;
  let messageToSend= JSON.parse(this._context.parameters.MessageToSend.raw!= null?
  this._context.parameters.MessageToSend.raw:"");
  this.httpCall(messageToSend, (res)=>{ console.log(res)});
}
  • Edit the function getOutputs. Here we specify what are the properties we want to output.
public getOutputs(): IOutputs
{
  //This code will run when we call notifyOutputChanged when we receive a new message
  //here is where the message gets exposed to outside
  let result: IOutputs = {
  MessageReceivedText: this._receivedMessage.text,
  MessageReceivedType: this._receivedMessage.type,
  MessageReceivedSender: this._receivedMessage.sender
  };
  return result;
}
  • We should stop the SignalR connection when the component is not active, we could also send a notification before we leave.
public destroy(): void
{
  // Add code to cleanup control if necessary
  this.connection.stop();
}
  • Now we have finished the code in our control, to package the solution and install it in CDS we run the command “msbuild /t:restore” followed by “msbuild”. (to update to CDS we need to have an auth profile, you should only run this once)

pac auth create –url https://your_org_name.crm4.dynamics.com/

  • Go to the folder NotificationComponent by calling “cd ..” twice
  • Run the command: “pac pcf push –publisher-prefix dev

You should see something like this:

C:\NotificationComponent>pac pcf push --publisher-prefix dev

Connected to...############
Creating a temporary solution wrapper to push the component...
Creating temporary solution wrapper: done.
File at C:\NotificationComponent\obj\PowerAppsTools_dev.
Building the temporary solution wrapper...
Building temporary solution wrapper: done.
Dropping temporary solution wrapper zip file: done.
File at C:\NotificationComponent\obj\PowerAppsTools_dev\bin\Debug\PowerAppsTools_dev.zip.
Importing the temporary solution wrapper into the current org...
Starting Solution Import: 1/7, 14%.
Import of solution file completed: 2/7, 28%.
Waiting on completion of import job '99fcb884-d431-4eb1-b7f9-b7e1733c1c53': 3/7, 42%.
Processing of import job ended at 9/18/2019 9:13 PM after 100% completion: 4/7, 57%.
Import job reported a status of 'SUCCESS': 5/7, 71%.
Unmanaged solution import has completed, starting to publish customizations: 6/7, 85%.
Customizations have been published: 7/7, 100%.
Importing the temporary solution wrapper into the current org: done.
  • Now our component is installed in CDS and we can configure the SignalR

Part 2: Configuring SignalR to broadcast the messages, steps:

  •  First, we create the azure Service, logging in into https://portal.azure.com
  • Search by “SignalR Service” and create it

  • Configure the service like the image with a different name in Resource Name

  • Copy ConnectionString  going to Keys from SignalR, you will need it later

Create an Azure Function to control the Negotiation and Messages with SignalR, using the information: https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-javascript?WT.mc_id=signalrquickstart-github-antchu

  • Create a new folder for this code and enter into it
  • C:\>mkdir NotificationCompAzureFunc
  • C:\>cd NotificationCompAzureFunc
  • git clone https://github.com/Azure-Samples/signalr-service-quickstart-serverless-chat.git
  • In your code editor, open the src/chat/javascriptfolder in the cloned repository.
  • Rename settings.sample.jsonto local.settings.json.
  • In settings.json, paste the connection string into the value of the AzureSignalRConnectionStringsetting. Save the file.

  • Run command “C:\NotificationCompAzureFunc>func extensions install”

To deploy this function to Azure we follow the steps described here.

  • Go to C:\NotificationCompAzureFunc\signalr-service-quickstart-serverless-chat\src\chat\javascript> and open vs code from there “code .”
  • Deploy the code to Azure

Create a new Function App

  • Select a Data Center location
  • After deploy go to Azure and check your newly App

  • Configure the connection string

  • New Application Setting:

  • Save all the settings
  • Configure CORS to allow PowerApps domains

  • Add these domains and enable Access-Control….
https://azure-samples.github.io -> If you want to use the chat example
https://127.0.0.1:8181 -> if you want dev environment
https://eu.create.powerapps.com
https://content.powerapps.com
https://your_org_name.crm4.dynamics.com

Now everything should be configured correctly we can go back to PowerApps and try it out.

Part 3: Create a canvas PowerApps to use this component

  • Create a new canvas PowerApp

  • Enable the Preview component and experimental PowerApps component framework features – for details see here.

  • Import the component

 

  • Select and click Import

 

  • Insert the component into the screen

  • Get the url from Azure function, in our case https://pcfs#######.azurewebsites.net

  • Configure the component

  • Add a button to create the message to send. Notice the “MessageToSend” variable is bound in our component in the previous step

  • Save the app and publish. Try to run the App and check if you get it connected to WebSockets

  • Add more controls to the App to be able to send a message and receive the results

In the button “OnSelect” event edit the “text” to receive the Text from an TextInput:

Set(MessageToSend,
JSON({
 sender:"image",
 text:TextInput2.Text,
 type:"test"
}))
  • Rename the component to “NotificationControl”. Create 3 Input that bound to the output variables of “NotificationControl”

Now after publishing open the PowerApp in 2 different browser tab’s. In one of those, specify a message in the “send text” and click on the button, go to other app and check the message, you should be able to see real-time communication between the two PowerApps.

You can also include the component into Microsoft D365 or any model-driven PowerApps and create real-time communication experience .

We will cover how to secure the communication with Azure Function in a different post, we hope this tutorial gives you enough push to build your own components.

Feedback and resources

The goal of this component is to give you inspiration for real-time communication between PowerApps, Microsoft D365 or any other system using Azure services; Please try out this exciting possibility and use the PowerApps Community forum to provide feedback and report bugs.

To everyone who has been using the PowerApps component framework and sharing feedback, big thanks for being a part of this journey with us.

Cheers,

Rui Santos