Improving PowerApps by Centralized Processing

Improving PowerApps by Centralized Processing

PowerApps are promoted as Low-Code / No-Code platform to write Apps and this is right! Power users can easily adopt the “programming” language which is similar to the Microsoft Excel Formula language.

On the other hand, when developing PowerApps, users tend to duplicate command snippets and code fragments. I did this until I found a way to reduce this redundancy of my code copies in my apps.

Let me show you this on a very simple example:
Calling a Power Automate Flow from PowerApps with different parameters.

Prerequisites – Power Automate flow

My example uses a Power Automate flow similar to the flow in “Avoiding Parameter Chaos in PowerApps“. Therefore, my input parameter is still “delaySeconds”.

Example Power Automate flow
Example Power Automate flow

As you see, the difference to my former example is my added action “Respond to PowerApp or flow”. This action return parameters from a Power Automate flow to the calling PowerApp.

In other words, my example flow returns an information text as “Result” parameter to my PowerApp.

This change is necessary because I want to wait until my flow is finished. Therefore my flow must return at least one result parameter. Otherwise, the flow would run in the background without waiting.

Calling a Flow from PowerApp

As you also know, you can select and add a Power Automate flow as data source or action to your PowerApp:

Adding a Power Automate flow to a PowerApp

The added flow is now ready to use. I can invoke my flow by using a Control-Trigger. Within this function body, I need to call the “Run” command with the specified parameters:

Calling a Power Automate flow from PowerApp Trigger with parameters

Finally, my flow is executed when the trigger event raises.

So far, so good, let’s increase the complexity! …

Start Processing with different Parameters

My Power Automate flow example is executed from 3 buttons. Each of these button “OnSelect” triggers will pass a different value as “delaySeconds” parameter to my flow.

Additionally a label control is showing some information during the execution. This “Message” will provide information about the current operation to our user:

My first button “OnSelect” trigger uses 10 seconds as parameter “delaySeconds”. Therefore the code is:

UpdateContext({lValue: 10});
UpdateContext({lMessage: "Starting the flow… with " & lValue & " sec. delay…"});
'01-Demo-Wait'.Run(lValue);
UpdateContext({lMessage: "Flow done."});


The 2nd button passes 15 seconds as parameter to the flow:

UpdateContext({lValue: 15});
UpdateContext({lMessage: "Starting the flow… with " & lValue & " sec. delay…"});
'01-Demo-Wait'.Run(lValue);
UpdateContext({lMessage: "Flow done."});


And finally, the 3rd button and “OnSelect” trigger invoke the flow with 20 seconds. You see, the code is already similar:

UpdateContext({lValue: 20});
UpdateContext({lMessage: "Starting the flow… with " & lValue & " sec. delay…"});
'01-Demo-Wait'.Run(lValue);
UpdateContext({lMessage: "Flow done."});


As result of my programming I got 3 button triggers with obviously the same code and behavior. The only difference is the value stored as local context variable “lValue”.

In other words, a lot of “copy-paste” from my code.

Processing Workflow

Well, my example is quite simple and copy-paste works very well here.

But what if there are more complex parameters, the processing is error-prone or time-consuming?

… Then I should do a little bit more. Here are some examples:

  1. Pre-processing to prepare or transform my data for the flow
  2. Post-processing to transform the received results for presentation
  3. Error handling if my result is unexpected 😮
  4. And finally, during such a long period of operation, I should give my user feedback on what is happening

Consequently, the amount of code will be increased. So, code cloning is not an option!

Additionally, from a maintainability and development point of view, it makes sense to centralize my code. It is easier to change one trigger instead of 3 triggers.

In other programming languages I would use a module or function to bundle functionality. Unfortunately, PowerApps Language works differently. You can’t create a module or function.

Moreover, I can’t call anything in PowerApps except a flow. As a result, I have to react to triggers where I can place my code.

So, my million dollar question is: “Is there a way to reduce code based on triggers?” … and my answer is: “Yes, I have found one!

Centralized Processing

I use a Toggle Control to centralize my execution code. This control can react to variable changes and has its own trigger to handle changes.

This means, I set up the “Default Value” property of my Toggle Control to my context variable “lExecuteFlow”:

lExecuteFlow


Furthermore, changes to “lExecuteFlow” will raise the “OnChange” trigger of my Toggle Control.

So, my code is quite simple. I change the variable “lExecuteFlow” to its opposite value by using the logical functionNot(…)” … nothing more.

UpdateContext({lExecuteFlow: Not(lExecuteFlow)});

Reducing the Trigger Code

Each of my buttons passes a parameter to my Power Automate flow. The update of the parameter “lValue” is added to the UpdateContext call of each button trigger as follows:

UpdateContext({lValue: 10, lExecuteFlow: Not(lExecuteFlow)});


As result, I have now 3 triggers with 1 line of code instead of 3 triggers with the whole execution code. This is in my opinion much better!

Improved Flow Invocation

My flow is now centralized executed centrally by the “OnChanged” trigger of my Toggle Control:

Additionally, I added some comments to structure my code. I hope 🙂 this improve the readability and understanding of my code for future maintenance a little bit.

Now my “OnChanged” trigger code contains the whole logic. Therefore my trigger-body contains, prepare input parameters, invoke my flow, do some post processing, give user feedback, and error handling, as follows:

// Pre-Processing
UpdateContext({lMessage: "Prepare parameters Flow"});
UpdateContext({lDelayInSeconds: lValue});

// Processing
UpdateContext({lMessage: "Starting the flow with " & lDelayInSeconds & " sec. delay..."});
UpdateContext({lResult: '01-Demo-Wait'.Run(lDelayInSeconds).result });

// Error Handling
UpdateContext({lSuccess: IsBlankOrError(lResult)});
If(Not(lSuccess), Notify("Flow Execution Error", NotificationType.Error));

// Post-Processing (only on success)
If(lSuccess, 
   UpdateContext({lMessage: "Flow done: " & lResult}),
   UpdateContext({lMessage: "Flow failed."}) );

This means, my execution code is now well structured and in one place. Furthermore I can maintain changes of the behavior easily.

Some Cleanup…

Well, I have added a Toggle Control and centralized my code. This is perfect for development.

PowerApp with my "Trigger Control"

On the other hand the added control is totally useless for my users of my PowerApp. In short, I need to hide my Toggle Control.

For debugging I prefer to switch the visibility of such “Centralized Trigger Controls” on and off by a global variable like “mShowTrigger”:

Change the Visibility of Trigger Control

My PowerApp App-Trigger “OnStart” sets the initial value of my global variable “mShowTrigger”:

Set(mShowTrigger, true);


Every time I manually run the “OnStart” App-Trigger from popup menu, I reassign the value as defined:

Execute App "OnStart" trigger manually

As a result, I can centrally switch the visibility of my “Trigger Controls” on and off.

Conclusion

I have showed you my example how I structure and centralize my execution code. This means: Avoiding unnecessary code-copies in PowerApps is possible!

There are good reasons why this make sense. First of all, I know where my code is. Second, I “hopefully” only have to change 1 line of code instead of many if something is wrong. Finally, using such a pattern in complex PowerApps greatly improves the readability and maintainability of my code.

In my opinion, such patterns greatly improve the quality of your PowerApps.

What do you think? … Do you use such patterns, tricks, or do you know a better way? Add your comments and/or share your examples!

Share

Leave a Reply

Your email address will not be published. Required fields are marked *