DEV Community

Gabriel Follone
Gabriel Follone

Posted on • Edited on

Power Apps - Canvas - Code Reusability How to Do It

Table of Contents

1. Introduction

As an application grows, it's natural to look for ways to optimize development. Code reusability has always been an applicable concept in Power Apps, although on smaller scales and with greater complexity in the past. Currently, it's more accessible and effective. This article will present several examples of how to reuse logic in your Canvas application, as well as discuss when it's appropriate to apply these techniques.

For this, let's start with a simple and very common scenario in the daily life of those who create apps with this technology.

2. Challenge

Let's say someone asked for a screen to register new people on the team and the screen needs to have a form with 3 text fields, a reset button, a save button, and a table on the side where it's possible to view all team members. Every time the user enters the screen, it needs to reload the users table and reset the input controls, the reset button whenever clicked needs to reload the table and reset the input controls, and the save button needs to update the team members table, simple right?!

So let's go to the screen without logic and we should have something like this:

The image displays a two-panel user interface. The left panel is a form with three input fields labeled

Now let's create the logic to reset the input controls and we should have something like this:

//Reset Fields
Reset(tiExample1Field_1);
Reset(tiExample1Field_2);
Reset(tiExample1Field_3);
Enter fullscreen mode Exit fullscreen mode

Creating the logic to reload the table we have something like this:

//Reload Collection
ClearCollect(colTeam, tbTeam)
Enter fullscreen mode Exit fullscreen mode

If we don't follow best practices, we'll end up adding this logic repeatedly in different controls, meaning we'll have the same logic in the screen's OnVisible and in the reset button's OnSelect:

/*==============OnVisible==============*/
//Reload Collection
ClearCollect(colTeam, tbTeam);
//Reset Fields
Reset(tiExample1Field_1);
Reset(tiExample1Field_2);
Reset(tiExample1Field_3);

/*==============OnSelect==============*/
//Reload Collection
ClearCollect(colTeam, tbTeam);
//Reset Fields
Reset(tiExample1Field_1);
Reset(tiExample1Field_2);
Reset(tiExample1Field_3);
Enter fullscreen mode Exit fullscreen mode

3. How to Solve It?

Now the discussion starts to get serious, after all, currently the best way to do this is still in preview and the others we have available are workarounds, and what are they?

Buttons

We have the option to add all of this to an invisible button on the screen and make the screen's OnVisible properties and the reset button's OnSelect be a Select() to this invisible button. This way we ensure maintenance in a single place, but what are the limitations of this approach? The Select() doesn't work with controls from other screens and doesn't have native support for parameters, meaning if we have logic that is used several times by different controls and events on the same screen and we don't need to pass dynamic values to this logic, this option becomes very interesting and doesn't require activating any preview features.

The image displays a series of screenshots from what appears to be a Power Apps development environment, highlighting various configurations and interactions. The primary focus is on the

Custom Components and Enhanced Component Properties

The most viable option currently that doesn't need to activate preview features (yes, enhanced component properties are GA), what are the pros and cons? It brings the same gains as reusability with buttons and still supports parameters (and even allows optional parameters), we can call properties of type event, action, and function from different screens. The downside is just that it's not so easy to use and especially for those who aren't used to real development might not understand very well how to use or where to apply it, but here's an example:

In this example we don't need to pass any parameters, but we need to change the state of items that are on the screen so we need to activate the component's Access app scope and with that our property needs to be of type action:

The image displays a screenshot of a Power Apps development interface, focusing on the configuration of a custom component. On the left, within the

Then copy and paste the logic from the invisible button's OnSelect from the previous step into the property we just created, add this custom component in a way that's not visible to the user on the team form screen and replace the Select(buExample1Invisible) with ccFunctionsTeam_1.fxReloadAndResetTeam() and voilà!

The image displays a series of screenshots from a Power Apps development environment, illustrating how a custom function is triggered by an event and impacts the user interface. The main focus is on the

User Defined Functions

The best option, but still not in GA are definitely the UDFs (even though they still don't support optional parameters, they now support behavior functions), and why? Simplicity! It's better than working with Enhanced Component Properties because we can declare it directly in the formula bar, meaning we don't need to create components and add them to the screen, which makes the solution much simpler, elegant, and easy to understand. And how would this look in practice?

First, copy and paste to the formula bar in the UDFs section the code snippet below (if you don't know what the UDF section is, read my other article about Power Apps - Canvas - Formula Bar Anatomy - DEV Community):

udfReloadAndResetTeam() = {
    //Reload Collection
    ClearCollect(colTeam, tbTeam);
    //Reset Fields
    Reset(tiExample1Field_1);
    Reset(tiExample1Field_2);
    Reset(tiExample1Field_3);
};
Enter fullscreen mode Exit fullscreen mode

And the solution would look like this:

The image displays a series of screenshots from a Power Apps development environment, illustrating how a user-defined function (UDF) named udfReloadAndResetTeam() is integrated into an application. Red boxes and arrows highlight that this UDF is assigned to both the OnSelect property of a component and the OnVisible property of the

Well, from here I assume I no longer need to explain the benefit of all this and the examples will now always start from using UDFs to make this article a bit more practical.

In our challenge so far we've only solved the request regarding the reset button's OnSelect and the Team form screen's OnVisible, but we still need the Save control and how can we do this following all these best practices I'm talking about?

Safely Manipulating Items

So far the expression we were reusing was very simple, it had no parameters or anything dynamic. Now let's create a UDF that accepts parameters, but not only that, let's declare types to make our application resistant to unexpected bugs. And in this example we'll create a static named formula with a default value for our team collection just to make it easy to explain.

Copy and paste in your formula bar in the table section the named formula below:

tbTeam = [
    {ItemId: 1, ItemPersonName: "John Doe", ItemPersonMail: "jdoe@contoso.com", ItemPersonRegion: "NA"},
    {ItemId: 2, ItemPersonName: "Gabriel Follone", ItemPersonMail: "gfollone@contoso.com", ItemPersonRegion: "SA"}
];
Enter fullscreen mode Exit fullscreen mode

Now copy and paste in the UDTs section the type below:

tpTeamMember := Type({
    ItemId: Number,
    ItemPersonName: Text,
    ItemPersonMail: Text,
    ItemPersonRegion: Text
});
Enter fullscreen mode Exit fullscreen mode

And finally paste in the UDF section our expression that will insert the values into the collection:

udfPostMember(ParamMember: tpTeamMember) = {
    Patch(colTeam, 
        Coalesce(LookUp(colTeam, ItemId = ParamMember.ItemId), {}),
        {
            ItemId: (Last(colTeam).ItemId + 1),
            ItemPersonName: ParamMember.ItemPersonName,
            ItemPersonMail: ParamMember.ItemPersonMail,
            ItemPersonRegion: ParamMember.ItemPersonRegion
        }
    )
};
Enter fullscreen mode Exit fullscreen mode

Now the implementation of this becomes quite simple, just add to the save button's OnSelect the following expression:

udfPostMember(
    {
        ItemPersonName: tiExample1Field_1.Value,
        ItemPersonMail: tiExample1Field_2.Value,
        ItemPersonRegion: tiExample1Field_3.Value
    }
)
Enter fullscreen mode Exit fullscreen mode

Software Development Principles

And now let's say a new request comes up: after saving, the input controls need to be reset. But we have a problem now, because our UDF that controls the reset of the form controls also resets the collection! And here comes the SRP (Single Responsibility Principle) from SOLID. This principle makes it clear that a function should be responsible for only one thing within our system, meaning, let's create a UDF for resetting the controls, another for resetting the collection, resulting in:

udfReloadTeamData() = {
    //Reload Collection
    ClearCollect(colTeam, tbTeam);
};

udfResetTeamForm() = {
    //Reset Fields
    Reset(tiExample1Field_1);
    Reset(tiExample1Field_2);
    Reset(tiExample1Field_3);
};
Enter fullscreen mode Exit fullscreen mode

But now how does the implementation look? In the save button's OnSelect right below the udfPostMember do I call udfResetTeamForm or add udfResetTeamForm inside udfPostMember? If you thought neither, congratulations! Why are neither options good options?

Scenario 1: Add the call to both UDFs directly in OnSelect

First, this option is much better than the second, but here we're learning about reusability and best practices for all of this, meaning, leaving all the logic in a single control having the option to leave it encapsulated in the formula bar is not the best decision. This reduces the reusability options of the logic, after all, even though it's possible to use Select() on this button, if I need to use this outside this screen it would no longer be possible.

Second, it decreases testability, after all it's much easier to test encapsulated logic.

Third, clarity and maintainability: having this directly in the button's OnSelect is harder to find and if I have this same logic calling 2 UDFs in different places and the execution order is changed, I would have to change it in all places where this logic is used.

Scenario 2: Add the call to udfResetTeamForm inside udfPostMember

If you understood the explanation about SRP you must have already realized that opting for this scenario makes us go against this principle, after all by doing this we are increasing the coupling between the UDFs, this means that one will depend on the other. If the way to reset the form changes, or if you want to use udfPostMember in a context where the reset is not necessary, you'll have to modify the insert function. And obviously when coupling increases, flexibility decreases and you lose the option to decide when and how the form should be reset. The reset becomes a mandatory consequence of the insert.

Best Scenario - Scenario 3: Create a UDF that orchestrates the other two

With this you maintain the SRP principle, keep coupling low, increase reusability within the app and make the application's operation more readable and easier to understand. Win-win-win!

udfSaveAndResetForm(ParamMember: tpTeamMember) = {
    udfPostMember(ParamMember);
    udfResetTeamForm();
};
Enter fullscreen mode Exit fullscreen mode

4. Conclusion

Once again showing that principles long disseminated in software development can be easily applied to low-code tools. Even though we are limited to what the tool's controls offer us (if we don't use PCFs), it's easy to apply everything I explained - it just requires discipline and minimal planning before implementation.

Method Pros Cons
Invisible Buttons • Doesn't require preview features
• Simple to implement
• Works immediately
• Familiar to beginners
• Doesn't affect performance
Select() doesn't work between screens
• Doesn't support parameters natively
• Can pollute the screen with invisible controls
• Considered a "workaround"
Custom Components
(Enhanced Component Properties)
Is in GA
• Supports parameters (including optional)
• Works between different screens
• Shareable via component library
• Typed properties (Action, Event, Function)
• Reusable between apps
• Professional solution
• Requires more technical knowledge
• More complex initial setup
• Steeper learning curve
• Can increase app size
• Harder to debug
User Defined Functions (UDFs) • Direct declaration in formula bar
• Cleaner and more intuitive syntax
• Supports behavior functions (Set, Collect, Reset)
• No need to add extra controls
• Facilitates unit testing
• Better code organization
• Optimized performance
Still in Preview (not GA)
• Doesn't support optional parameters
• Can't create local variables
• Some technical limitations
• May change before GA
• Requires manual activation
• Not shareable between apps (yet)

5. Recommendations by Scenario

Scenario Recommended Method
Simple app, single screen logic Invisible Buttons
Enterprise app, multiple screens Custom Components
App in development, technical team UDFs (with caution)
Shared library between apps Custom Components
Rapid prototyping Invisible Buttons
Production with long-term support Custom Components

6. Important Notes

  • UDFs: Still in preview, not in GA (Generally Available)
  • Enhanced Component Properties: Are in GA and ready for production
  • UDFs syntax with behavior functions: Use braces {} for functions with behavior functions
  • UDFs have limitations: Don't support optional parameters and have some technical restrictions
  • UDTs are still an Experimental feature

7. References

Official Documentation

Blogs and Articles

Performance and Optimization

Top comments (1)

Collapse
 
ddamaceno profile image
Daniel Damaceno

Hey man, what a cool content! I really liked the way you approached the article — from the content to the structure. Congratulations! Keep bringing these enriching contents please XD