Sunday, September 23, 2018

Passing Parameters into your App

One of the great things about doing any Web App is just the ability to throw some additional variables/data at it inline via URL (see anything after the likely ? you see in the URL that got you here).  These inline variables are handy ways to do basic integration for data that you don't really care about securing.  PowerApps makes it as simple as any other web app to send data in.  It is accessing/using that data to make decisions before you do anything else that can be annoying.

I have recently (June 2019) completely re-written this post as Microsoft has fixed some of the initial and most annoying aspects of this process.


PowerApps uses the following function to read information passed inline via the URL:

  • Param("YourParameter")
For the details below, assume we are sending in some data to the application similar to:

  • http://...&ID=120&ViewType=3

As a simple example, let's say that if somebody passes in an ID of a particular record, then you'd like your app to go straight to a screen to edit or view that record vs. say your main screen.  

Then you could use the following commands to drop these into two global variables:

Set(recordID,Param("ID"));
Set(viewType,Param("ViewType"))

NOTE: You cannot use local variables (e.g. UpdateContext({...}) here because we aren't yet on a "screen".  Local variables are local to the screen on which they are created/used.

This is reasonably simple, if we ignore the whole global/local assignment idiocy and weirdly specific requirements per ('s, {'s, ;'s.  For a "no-coding" 4GL environment there sure seems to be some very specific issues per language formatting.  In particular, we've just taken these variables and thrown them right in w/o realizing that they are passed in as TEXT and we probably actually want these to be NUMBERS.  But we'll get to that later...

If you are just starting out in PowerApps or even have done this awhile, you'd assume that these two lines of code should be run at the VERY FIRST STEP of your application.  So you drop this code in the App | OnStart event.  This used to not work, now it does!  Hooray for progress!

We used to also not be able to use the Navigate() function during the OnStart event.  That's fixed too!  Everything's coming up Milhouse!

But these are still Text values and we haven't done any error checking soooo...

Best practices

While some of these are personal preference, for the most part this is how you should treat Parameters inside your app:
  • Error check incomingvalues (don't trust that what you were sent is valid)
  • Don't send secured/private data as a parameter (just...don't)
  • Store your parameters inside a global variable set in the AppStart event (you don't technically have to, but it limits confusion for anyone else who edits your app)
  • Don't use these for passing values into embedded Teams apps (it won't work - ?yet?)

Error-checking on Parameters

Taking data from outside sources w/o verification is just asking for it.  What we've done in the above step is to assume that there are no bad actors out there.  I love that world and its associated assumptions.  If it only extended beyond the confines of my development environment...

So you really have to verify that your items are valid.  However, this is where you start to recognize that conditional IF statements are fairly simplistic in PowerApps.  For example, you cannot go IF A THEN DO B,C,D.  It is always IF A THEN B (ELSE C).  You can only do one item at a time.  So if you want to do 3 or 4 things if one thing is true, then you need to do that over and over and over (IF A THEN B, IF A THEN C, IF A THEN D...).

NOTE: It appears that recent updates may have extended the If() statement to be more useful along these lines.  However, Microsoft's documentation still essentially reflects what I have outlined below as "correct". You are free to try out putting a semicolon (;) at the end of a command within your if() statement and follow it with another command allowing for a more traditional if/then experience.  This seems to work currently (Nov-2019).

For Error-checking, it can get complex and we only want to move forward to do any real assignment of parameters to variables if they are values we expect.  So if we expect numbers, then they'd better be numbers.  If we expect values between 1-4 then we need to ensure that too.  If we expect then to be values between 1-4 and they should exist in a SharePoint list then we now need to also check that.  So you can see how each condition makes it more complex.

For PowerApps there are of course many ways to accomplish this, but often it winds up a combination of If and potentially Switch statements.  Consider the following:


If(
    IsNumeric(Param("ID")) And IsNumeric(Param("ViewType")),
    Set(
        OK,
        true
    ),
    Set(
        OK,
        false
    )
);
If(
    OK,
    Set(
        recordID,
        Value(Param("ID"))//Note the Value() function used here to change TEXT to a Number
    )
);
If(
    OK,
    Set(
        viewType,
        Value(Param("ViewType"))
    )//...and here too
);

This is a pretty basic bit of verification before you get into anything application-specific.  Some might argue that you should just test each item individually, while I prefer to nuke everything if even one item isn't something I expect.  At a bare minimum, this is required for any application to ensure you are getting values you expect.  Of course, as I mentioned, you might also want to check to see if this item exists in your applicable list as well.  Also, you probably have only a certain # of "ViewTypes" that you have for your data, so you'll want to restrict that as well.  It then winds up a little more hairy.

Assuming I have a DataSource I've linked to called "ItemList" (but I've created a fake list in the first step below) and only ViewType values of 1-4 are acceptable here's an example of what our App | OnStart event might look like.

ClearCollect(  //a list of sample data to work with
    ItemList,
    {ID: 1},
    {ID: 2},
    {ID: 3}
);
If(  //make sure it is numeric before we allow any further processing
    IsNumeric(Param("ID")) And IsNumeric(Param("ViewType")),
    Set(
        OK,
        true
    ),
    Set(
        OK,
        false
    )
);
If(  //if it was numeric then see if the ID exists in the list and make sure the View exists
    OK,
    If(
        !IsBlank(
            LookUp(
                ItemList,
                ID = Value(Param("ID"))
            ).ID
        ) And Value(Param("ViewType")) > 0 And Value(Param("ViewType")) <= 4,
        Set(
            OK,
            true
        ),
        Set(
            OK,
            false
        )
    )
);
If(  //if it was numeric and the record/view exists, then set the ID for the record we'll be working with
    OK,
    Set(
        recordID,
        Value(Param("ID"))
    )
);
If( 
    OK,
    Set(
        viewType,
        Value(Param("ViewType"))
    )
)

Now we're catching first if everything is a number, then we're doing a Lookup to make sure the items exists (e.g. we would only accept ID's of 1-3 in the above example) while also verifying that we have a valid ViewType (e.g. values from 1 to 4).  Now imagine that you have ViewTypes that are dependent on the specific ID.  Code-free environment my @55.

At this point, you might start to run into problems w/ branching If/then or Switch statements that are just getting a little too complex.  For now though, let's stop here and try to see how to Navigate works in this environment.

If/Then or Switch Navigating

Before Navigating ANYWHERE, you need to understand that the Navigate command isn't like a traditional "GoTo" kind of command.  If you Navigate away from this screen, the statements that follow the Navigate command are still run.  So you can wind up doing things you don't intend if you are not careful.  For example, which screen does the user see in this example if x=1?

If(
    x = 1,
    Navigate(
        Screen1,
        None
    )
);
If(
    x = 2,
    Navigate(
        Screen2,
        None
    ),
    Navigate(
        Screen3,
        None
    )
)

Duh.  Screen1.  Nope.  Screen3.  The evaluation of statements doesn't stop just because you Navigate.  So even though x=1 and you initially say to go to Screen1, the very next statement has an else that says if x<>2 then go to Screen3.

In a simple world we could be done w/ this quickly w/ a slightly fancy Switch statement to wrap up the end of our App | OnStart event:

...
If(
    OK,
    Set(
        viewType,
        Value(Param("ViewType"))
    )
);
If (OK,
  Switch(true,
    viewType=1,
    Navigate(View1,None),
    viewType=2,
    Navigate(View2,None),
    viewType=3, 
    Navigate(View3,None),
    viewType=4, 
    Navigate(View4,None)),
  Navigate(MainMenu,None))

Now PREVIOUSLY, this would have thrown a significant error.  PowerApps didn't want you trying to navigate during certain events (like OnStart, OnVisible, etc.).  However, I've now deleted MANY paragraphs of text on building timers and whatnot to yet again work around a shortcoming in this platform.  Sooooo...yay?  Yes.  Yay.

Definitely Yay.
If we created screens named "View1" through "View4" now we'd have a fully functional app.  Sorta...maybe you'd like to do something on those screens?

Calling the URL to your App

The very last thing you of course need is the exact URL to your application.  Once you have this, you can add your parameters to it and embed that in your calling application's logic.

To get the URL for your application:
  1. Go to https://powerapps.microsoft.com  (log in if you aren't already)
  2. Click on Apps
  3. Click on the three little dots next to your app's name (if you hover then the help text More Commands will appear)
  4. Click on Details
Duuhhrr...so obvious
Once you are there, look for Web Link item.  It contains the URL for your application.  In fact, any user can click on that link and run the application (if you've given them the rights to access it).

Take that URL and append the following items to it to pass in the parameters listed above like a traditional inline variable URL.  

So if the URL to your app is:
  • https://web.powerapps.com/apps/1234
Then you can append the inline variables like so:
  • https://web.powerapps.com/apps/1234?ID=120&ViewType=3
Of course you'd need your application to build the parameters dynamically, but that's all on you.

Final Thoughts

This particular blog post is significantly different from what it was a year ago.  The PowerApps team absolutely improved how to handle parameters.  The difference is drastic and a worthwhile review of PowerApps as a platform.

This is a RAD platform.  DO NOT, get tied up in over-building your application to fix its perceived deficiencies.  You can do some minor work to get around something that doesn't work properly, but don't get all crazy about it.  Microsoft is doing MAJOR work on this environment and it will continue to be updated regularly.

For anyone building in PowerApps, I would remind them (and myself time and again) of this aspect of the environment.  Build it quick, build it simple and get it into user's hands.  Fix the other stuff on the back side and don't get too tied up into what you can/cannot do here.  Except it for what it is, get past it as quickly as possible and keep abreast of new releases and how they address earlier issues on a monthly basis.

No comments:

Post a Comment

Because some d-bag is throwing 'bot posts at my blog I've turned on full Moderation. If your comment doesn't show up immediately then that's why.

DIAF Visualpath team