Sometimes, a Canvas App can save a project. This would be almost impossible for me to say even a year ago; however, the way my current project evolved in the last two weeks, I can think of no better tool to quickly deliver the functionality required by the client.
And, so, if you are more on the professional development side, and if you are still not quite buying those Canvas Apps, here is the story you may want to read. There will be a Canvas App, there will be a model-driven app, there will even be a plugin just so you don’t get bored… besides, there will be json parsing, regular expressions, tablets, web browsers. And it actually won’t take that long.
Either way, here is an additional requirement I got from the client having spent a few months developing a model-driven app: “we need to be able to do product inspections as quickly as possible, and we can’t afford all those mistakes the inspectors can make if they have to learn how to navigate in the model-driven app”. That was the essence of it, and there were some caveats about extra validations, color coding, etc. Basically, what I was hearing is that “a model-driven app will work for everyone else but not for the inspector”. Oh, nice…
First I went into the panic mode, then I thought of the options, and this is when I realized that a Canvas App can cover all of that, even though a developer in me is naturally biased against “excel-like” development.
For the rest of this post(s?), I‘ll be using a simplified version of what I ended up with, but, I think, that will still be a good enough evidence of why a Canvas App can, sometimes, save a project.
I’ll be using two entities for the demo solution:
- “Inspection” entity
- “Inspection item” entity
The idea is that a user can create an inspection, add inspection items, and pass/fail each of the items so that the whole inspection will pass or fail. In the model-driven app, here is how it would look like:
Remember this is a simplified version – on the actual project, there were inspection types, templates, dates, they would have to be booked in advance by different users, the products to inspect might not be available, etc. And, since the inspectors would be using this application only sporadically, you can probably imagine why they would not be comfortable doing it unless the process were streamlined. Not to mention they’d rather be doing it on their tablets.
Come to think of it, that particular part of the model-driven application can be implemented as a canvas app with only three screens:
That’s, really, all the inspectors need.
Again, in the actual app there was a bit more to it. There were about 30 inspection items per inspection, different tabs, different colors, inspection item templates, inspection setup logic, etc. But those screenshots above should already give you a good idea of why a Canvas app can work better in this scenario. And, yes, it can all run on tablets.
I don’t, really, mean to list each and every step we have to take to create a canvas app, so I’ll only focus on what I found challenging or not that straightforward. Namely:
- Formula-based development
- Interface and designer limitations
- Run-time and design-time being mixed up in the PowerApp Studio
- Workaround for the absence of reusable functions
- Working with JSON and regular expressions
- Implementing business logic in a CDS plugin
1. Formula-based development
Basically, you are working with some version of Excel. I would not go as far as to say that “if you know Excel you know PowerApps”, but there is some truth there.
Just about everything is either a static value or a formula. It’s not a function you can define and reuse, it’s a formula you can write to calculate a value for a property. For example, on the screenshot below, “Text” property of the label will take its value from the ProductNumber variable defined somewhere else, and, as soon as that variable gets updated, that “Text” property will immediately reflect updated value:
Can you set that “Text” property directly? Nope.
I am not quite sure why – we can set variables, but we can’t set properties. Go figure.
Can we use a more complicated formula? Not a problem, except that you may need to get used to the syntax:
In that sense, Excel it is.
For the whole list of out-of-the-box functions you can use in those formulas, have a look here:
2. Interface and designer limitations
This is a weird one. There are out of the box controls we can add to the screens. There are certain properties we can configure. Anything beyond that? Not an option.
Well, unless you go for an experimental PCF control, which can be very interesting and promising, but it’s not quite feasible for production yet:
Just to give you an example. I wanted to have a 3-state checkbox for those pass-fail-n/a options. Of course I would not be able to do it in the model-driver application either without some kind of custom development, but, at least, I’d have web resources and PCF on that side. In the Canvas App, I had to opt for a radio buttons group. Which works, but it’s not quite ideal if you consider how much space those radio button groups will be taking on the screen when there are 30 different “groups”.
Why would not I be using a separate screen per tab? Because “Select” method can only work with the controls on the same screen – I’ll talk about it in the “workaround for reusable functions” below. It helps that in Canvas Apps we can group controls:
We can select multiple controls (either using a mouse, or using by clicking on them one after the other while holding “shift”), and, then, group them.
One advantage is that we can, then, control visibility of all those controls using group properties:
And, then, if you have multiple groups on the same screen, you can hide all but one, update control settings in that group, then switch to the other, etc. That way, you can design each group separately.
Although, this is where you will quickly realize that there is no big difference between run-time and design-time in the PowerApps Studio, and that causes a bit of a problem, though that also presents an interesting workaround. Because, for example, if you use a variable to control group visibility(that’s what you would need to make it all work at run-time), you will not have control over the Visible property in the designer anymore – it’ll be greyed out:
And what if you wanted to switch to a different group? The trick is, you need to start your screen, ensure the condition for that “Visible” property are met for your group, and, then, return to the designer. The group will be visible, and you can keep working on the design of the grouped controls:
When trying to implement something like a “tab control”, this seems to help… a lot.
3. Run-time and design-time being mixed up in the PowerApp Studio
It’s very unusual when your design-time is affected by the results of the most recent start of your application. It’s also unusual when you can start any screen independently without going through all the screens which are supposed to be opened first.
At the same time, there is a difference between how the application behaves in the PowerApp Studio and how the end users work with the application. When we click start button, that opens up specific application screen:
The end users don’t have that option – in order to open that particular application screen, they would have to go through the previous screens.
Another way to think about it would be to say that we are not just designing the app, but we are actually debugging an app (and we are allowed to design it at the same time). We can get to a screen and switch to the “design mode”. From there, we can check current values of all the variables and/or collections:
Maybe we can’t update those values directly, but we can bring up another screen and do it. For that matter, we can even create a special screen which would not ever become visible for the regular users since there would be no navigation path to that screen, and, then, use it to set the variables the way we need them. Sounds weird? Well, leave your biases behind for now, and just give it a try. Chances are, a CanvasApp would still be the best fit for what you need to do under some circumstances.
4. Workaround for the absence of reusable functions
There are no reusable functions. I am not sure why Microsoft did it this way. Don’t get me wrong – I now have a couple of examples when a Canvas App saved a project for me, but there are things I just don’t understand, and this is one of them.
No matter what, it’s almost impossible to do any kind of development without having some sort of a function – it’s simply impossible to keep copy-pasting code from one place to another.
And there is a workaround, but, as any workaround, it’s not perfect.
See, we can use “Select” function.
We can create a button, put it on the screen, make it invisible, add some code to the “OnSelect” property, and, then, use “Select(buttonName)” to call that code from anywhere on the same screen.
Here is an example. Let’s say I wanted to store “fail/pass” results for each item in a variables (separately for each item). Of course I could do it in the “OnChange” of each radio button control, but I could also add a new button to the screen, hide it, and add the following code to the “OnSelect” of that button:
Which would allow me to call “Select(buttonSetVariables);” from any other place on the same screen:
How does it help?
What if I wanted to add common actions to each of those OnChange above? For instance, what if I wanted to update a record in the CDS database every time a radio button is updated? Now I could add a Patch call to just one place, pass all those variables to the Patch, and I would not have to remember to update every single OnChange instead.
Of course you might say that using Patch that way would cause an update to each and every attribute, and that’s not, necessarily, what I may want to do, but you can see how it simplifies code maintenance. Essentially, we are getting some kind of a function. If only Microsoft could introduce real functions…
It seems there is a related technique. You can use global variables, and you can add initialization code to the “OnVisible” property of the application screens. For example, in the inspection application above, I would initialize all global variables on the second screen (once the product # has been selected), and, then would keep updating them on the third screen through that hidden button. The idea is to keep those big chunks of code in one place, even if that means doing a bit more than you would, normally, do in a function.
At this point, there are two remaining topic which would better be discussed together, but which would qualify for a separate post: JSON/Regex and plugin-based business logic.
Hence, have fun for now, and stay tuned for a follow up post.