Fancy & Functional multi-step form indicators and breadcrumbs in PowerApps – Part 2

In the first part of this series we walked through creating multi-step form indicators in PowerApps. This post will help build a simple secondary navigation scheme using breadcrumbs to indicate the location of a user within the app’s hierarchy. They come in handy when apps have several nested screens and an effective visual aid is needed from a user’s perspective for contextual information around primary app screens.

Brian Dang has a terrific video on a fully functional back button system that breadcrumbs your way back based on your clicks. It’s multi-functional and can also be used for building an undo system. This post is an inspiration from his work. Thanks to Mr. Dang for the useful design pattern!

Below is the sample breadcrumb navigation we’ll be creating. I also uploaded a sample PowerApp to the PowerApps Community Gallery with the below breadcrumb design pattern and multi-step form indicator templates. Feel to free to reuse and/or provide feedback. I’d love to know if it helped enhance your PowerApps user experience.

breadcrumbsample.PNG

We’ll also create a functional back button to help navigate the crumbs backwards. To keep this post simple and focused on the design pattern for breadcrumbing, we’ll use a Gallery of items as our navigation system rather than individual app screens.

Step 1

Create a blank screen in PowerApps studio and set the following formulas on the ‘OnVisible’ property. First we are creating a Collection of ‘NavigationItems’ that we can use for building the breadcrumbs.

ClearCollect(NavigationItems,{NavigationId:1,Title:"Home"},{NavigationId:2,Title:"Dashboard",ParentId:1},{NavigationId:3,Title:"Reports",ParentId:1},{NavigationId:4,Title:"Admin Center",ParentId:1},{NavigationId:5,Title:"Create Service Request",ParentId:2},{NavigationId:6,Title:"Request A",ParentId:2},{NavigationId:7,Title:"Request B",ParentId:2},{NavigationId:8,Title:"Request C",ParentId:2},{NavigationId:9,Title:"Vendors",ParentId:3},{NavigationId:10,Title:"Archive Service Requests",ParentId:3},{NavigationId:11,Title:"Print Report",ParentId:4},{NavigationId:12,Title:"Email Report",ParentId:4},{NavigationId:13,Title:"Download Report",ParentId:4},{NavigationId:14,Title:"Dispatch Request",ParentId:6},{NavigationId:15,Title:"Update Contact",ParentId:6},{NavigationId:16,Title:"View Invoice",ParentId:6},{NavigationId:17,Title:"Dispatch Request",ParentId:7},{NavigationId:18,Title:"Update Contact",ParentId:7},{NavigationId:19,Title:"View Invoice",ParentId:7},{NavigationId:20,Title:"Dispatch Request",ParentId:8},{NavigationId:21,Title:"Update Contact",ParentId:8},{NavigationId:22,Title:"View Invoice",ParentId:8});

Each object in the collection has –
1. NavigationId: Index for navigation items
2. Title: Navigation item display name
3. ParentId: Parent navigation id for parent-child hierarchy

Next we are setting the current naviagtion context to the the current screen with a NavigationId of 1. Lastly, we are creating a ‘NavigationLog’ collection to track user clicks in order to create breadcrumbs.

Set(
    CurrentNavigationId,
    1
);
ClearCollect(
    NavigationLog,
    {
        Name: LookUp(
            NavigationItems,
            NavigationId = CurrentNavigationId,
            Title
        )
    }
)

Step 2

Insert a blank Gallery control and set the ‘Items’ property to the following formula. Here, we filtering out the child navigation items based on the ‘CurrentNavigationId’ context.

Filter(NavigationItems, ParentId = CurrentNavigationId)

Edit the Gallery Item, Insert a label control and set the ‘Text’ to ‘ThisItem.Title’. And then insert the right chevron icon and set the ‘OnSelect’ property to the following formula –

Collect(NavigationLog, { Name: ThisItem.Title, ParentId:ThisItem.ParentId});Set(CurrentNavigationId, ThisItem.NavigationId)

The idea here is to continue to append the navigation context on every gallery item click to the “NavigationLog’ Collection so we have a history of the user’s navigation. And then we are updating the current navigation context – ‘CurrentNavigationId’ to point to the most recent screen/gallery item the user is navigating away from.

Even though we are using a gallery to mimic navigation, this can be translated to actual screens with a button based navigation as well.

Step 3

Now let’s get started on the fun part – styling our breadcrumbs. For this example I used a HtmlText control with div tags and inline css styles for a shadow effect. And added a conditional bold on text based on navigation context. Insert a HtmlText control and paste the html content from below –

"<div style='margin: 0 0 0;
  padding: 20px;
  background: #092162;
  display: block;
  box-shadow: 0 2px 16px #333;'><div style='margin-left:45px;'>"& 
  Concat(NavigationLog, 
  "<span style='font-size:18px; 
  color:"&If(Name=LookUp(
            NavigationItems,
            NavigationId = CurrentNavigationId,
            Title),"yellow","#fff")&"';
  font-weight:"&If(Name=LookUp(
            NavigationItems,
            NavigationId = CurrentNavigationId,
            Title),"bold","semi-bold")&"'>"& Name &"</span>",
  "<span style='color:#fff; font-size:20px; font-weight:800; margin:0 10px 0 10px'>></span>") &
  "</div>
</div>"

There is an outer div as a container/placeholder for the breadcrumbs. For each item in the ‘NavigationLog’ Collection we are concatenating the navigation item’s display name and a ‘>’ text as a separator. The font color & font weight have values set based on a conditional logic. Font color is set to be yellow with a bolder style if that is the current screen the user is in.

Next insert a back icon and place it at the top left corner over the HtmlText control as seen in the image below –

backbutton.PNG

Paste the below formula on the ‘OnSelect’ event of the back icon. 

Set(
    CurrentNavigationId,
    Last(NavigationLog).ParentId
);
Remove(
    NavigationLog,
    Last(NavigationLog)
)

Everytime the user clicks back, two things need to happen – 

1. Remove the current clicked crumb from the ‘NavigationLog’ Collection since the user is navigating away.
2. And then reset the current navigation context so the gallery control filters and displays child items associated to the current parent navigation hierarchy.

That’s it! Run the app to test the breadcrumbs and the back system.
Here’s a snippet of the breadcrumbs and the back system in action –

breadcrumbs.gif

Fancy & Functional multi-step form indicators and breadcrumbs in PowerApps – Part 1

According to the Endowed Progress Effect (which is a phenomenon integrated into designing any UX process based on how the human brain makes decisions) we’re more likely to complete an action if there’s an illusion of progress and an effective visual aid of contextual information for landing pages. The best examples I can think of are –

  1. Multi-step forms that break up a long intimidating form into smaller sections and provides the user a sense of accomplishment as they proceed from step to step.

  2. Breadcrumb navigation to improve findability for users especially in apps with a lot of screens.

How do we incorporate those fancy, stylish and yet functional multi-step form indicators seen on websites in PowerApps using the OOB controls without custom scripts? 

This is a two part series post with the first being multi-step form indicators in PowerApps and the second part of the series will walk through creating breadcrumb navigation from scratch. 

Below are few clickable multi-step indicator styles I’ve been experimenting & playing around with. Also, I uploaded a sample app with all of the below design templates to the PowerApps Community Gallery. Feel to free to reuse and/or provide feedback. I’d love to know if it helped enhance your PowerApps user experience.

multistepsnapshot.PNG

Different styles indicating First section of the form being filled out

multistepsendnapshot.PNG

Different styles indicating Last Section of the form being filled out

Let’s build one from scratch…

For this example we will need a PowerApp screen per form section behind the scenes but the overall visual effect on the UX side will be seamless for the user. Here’s the design we will be building in this post-

examplemultistep.PNG

This design of multi-step indicators will provide contextual information (tiny blue inverted triangle) on which section is currently being worked on and also a visual aid (solid green circle) to keep the user informed on accomplished sections.

Step 1

We will be using a Service Request app as an example with a multi-step form for submitting a new service request. Create 4 blank screens in PowerApp studio with the following names –
RequestInfoScreen, ContactInfoScreen, NotifcationInfoScreen & AgreementScreen

Set the ‘OnVisible’ property of all 4 screens to the following and replace ‘RequestInfoScreen’ with the name of the current screen you are updating the property for.

UpdateContext({selectedScreen:RequestInfoScreen});

Step 2

Next we need to create a structure of properties for the multi step indicators so they can be used dynamically on all form related screens in the app. Here’s where Collections come into play. We will create a collection of sections and related properties for the multi form indicator. Here are the 8 properties per section that we will be using to create the multi-step navigation style –

  1. Section (Indicates an index for the section

  2. Title (Display Title)

  3. Screen (Associated app screen)

  4. IsCompleted (Track the status of the section)

  5. Color (Base font color)

  6. CompletedColor (Font color for completed sections)

  7. Fill (Base shape Fill color)

  8. CompletedFill (Shape Fill color for completed sections)

All colors are in RGBA format. Adobe color wheel is a good resource for determining RGB codes.  Let’s start building out the collection of records for each of those sections. Paste the below formula into the app ‘OnStart’ property of the app.

UpdateContext({selectedScreen:RequestInfoScreen});ClearCollect(MultiFormSections,{Section:1,Title:"Service Request",Screen:RequestInfoScreen,IsCompleted: true ,Color:RGBA(0,98,155,1),CompletedColor:RGBA(255,255,255,1),Fill:RGBA(241,241,241,1),CompletedFill:RGBA(127,178,57,1)},{Section:2,Title:"Contact",Screen:ContactInfoScreen,IsCompleted: false ,Color:RGBA(0,98,155,1),CompletedColor:RGBA(255,255,255,1),Fill:RGBA(241,241,241,1),CompletedFill:RGBA(127,178,57,1)},{Section:3,Title:"Notifications",Screen:NotificationInfoScreen,IsCompleted: false ,Color:RGBA(0,98,155,1),CompletedColor:RGBA(255,255,255,1),Fill:RGBA(241,241,241,1),CompletedFill:RGBA(127,178,57,1)},{Section:4,Title:"Agreement",Screen:AgreementScreen,IsCompleted: false ,Color:RGBA(0,98,155,1),CompletedColor:RGBA(255,255,255,1),SelectedFill:RGBA(252,146,97,1),Fill:RGBA(241,241,241,1),CompletedFill:RGBA(127,178,57,1)})

The above formula is not formatted but you can paste it into the formula bar and click on ‘Format Text’ for clarity on the collection structure. Below is a snapshot of how one record in that collection looks like –

ClearCollect(
    MultiFormSections,
    {
        Section: 1,
        Title: "Service Request",
        Screen: RequestInfoScreen,
        IsCompleted: true,
        Color: RGBA(
            0,
            98,
            155,
            1
        ),
        CompletedColor: RGBA(
            255,
            255,
            255,
            1
        ),
        Fill: RGBA(
            241,
            241,
            241,
            1
        ),
        CompletedFill: RGBA(
            127,
            178,
            57,
            1
        )
    }
)

Step 3

Navigate to the first screen (RequestInfoScreen) and insert a blank Gallery control. Set the ‘Items’ property of the gallery to the Collection we created above – MultiFormSections. 

Then edit the Gallery item and insert the following 5 controls one by one –

1.Label control (To display form section index)
Width & Height: 26
Size: 18
Color: If(ThisItem.IsCompleted, ThisItem.CompletedColor,ThisItem.Color)
Setting the color based on section completion status
Text: ThisItem.Section (reading the value of section property from the collection)

2.Circle shape (To display form section index)
Width & Height: 40
Fill: If(ThisItem.IsCompleted, ThisItem.CompletedFill,ThisItem.Fill)
Setting the circle fill color based on section completion status
Text: ThisItem.Section (reading the value of section property from the collection)

3.Label control (To display form section title)
Width & Height: 262 & 28
Size: 12
Align: Center
Color: ThisItem.CompletedColor
Setting the color to white

4.Rectangle shape (To act as a shell/container for all the controls above)
Width & Height: 275 &102
Fill: RGBA(9, 33, 98, 1)

5.HtmlText control (Using html for visual indication on current section being worked on)
Width & Height: 63 & 39
Visible: If(ThisItem.Screen = selectedScreen, true,false)
Only visible if that’s the current section.
HtmlText: Pure html/css to build an inverted blue triangle.
Css tricks is a good resource for building pure css based shapes without requiring images or icons. Paste below html content to create the inverted triangle.

"<div style='width: 0;
	height: 0;
	border-left: 20px solid transparent;
	border-right: 20px solid transparent;
	border-top: 20px solid #092162;'>
</div>"

Now that we have all the controls for the Gallery item added, move/rearrange them to match their placements in the image below –

galleryitem.PNG

Lastly set the ‘OnSelect’ property of the Gallery control to the following – 

Navigate(ThisItem.Screen, ScreenTransition.Fade)

This will help navigate back and forth form sections by clicking on the multi-step indicators.

Step 4

To be able to track user progress and show visual confirmation, let’s add a ‘Submit’ button to the screen. Set the ‘OnSelect’ property of the button with the fomula below:

Patch(
    MultiFormSections,
    LookUp(
        MultiFormSections,
        Section = 1
    ),
    {IsCompleted: true}
);
Navigate(
    LookUp(
        MultiFormSections,
        Section = 2
    ).Screen,
    ScreenTransition.Fade
)

On submitting a section, this formula looks up the object for that section in the ‘MultiFormSections’ collection and Patches the ‘IsCompleted’ value to be true. Then navigates the user to the next section.

You could specify the screen name directly without needing to do a lookup. The above approach helps keep your formula dynamic. You’d only need to update any of the section’s property values at one location (‘OnStart’ property where this collection was initialized) rather than updating every screen where the property is referenced.

Step 5

We have done all the setup for just 1 screen so far and need to replicate it on the other screens as well so that from a UX perspective it would look like you are tabbing through form sections while they are individual screens behind the scenes. Luckily the PowerApps copy/paste controls feature works great across screens. Right click to copy the Gallery control and the Submit button from the left tree view and then paste them into the the screen you want. This should bring over all the formulas and property values.

Update the formula for the submit buttons on all screens to reflect the right Section indexes.

We are now done designing the multi-step form indicators. Run the app and Submit the sections one by one to see the colors and indicators change.

Here’s how the multi-step form indicators look like in action –

In the next part of this series, we’ll create a simple breadcrumb navigation  that indicates the location of the user within the app’s hierarchy along with a back button to navigate the crumbs backwards.

Role Based Security In PowerApps using SharePoint Groups

Recently a customer asked if PowerApps can support role based security controlled by SharePoint Security Groups. For example, can you make an Admin screen that is visible only to users who belong to a specific SharePoint Security Group? Yes, you can and this is where Microsoft Flow comes to the rescue!

This blog post is an attempt to share an approach for finding out the SharePoint Group membership of a signed in user and make certain features or screens of an app available to them.

Prerequisites

Create a SharePoint security group with members that you would want to use for role based security in your PowerApps App and navigate to the group settings .

grpsetting.png

And enable “Everyone” access to view the members of this group

grpsec.PNG

We will be creating a Flow to check group membership using a SharePoint HTTP REST call. Users triggering the Flow from your app may not necessarily have admin privileges to read group membership details. To enable all users to read group membership it is necessary to allow “Everyone” to view members of the security group.

Steps

  • Create a blank flow using a Site Collection Admin/Flow owner account.

  • Add a PowerApps Trigger step so you can call this Flow from the app. Then add an Initialize variable step to store a boolean value (IsAdministrator). Next add another Initialize variable step with a string variable (UserGroupInfo) to store the user group information we will be retrieving in the next step.

1.PNG
  • Search for ‘Send an HTTP request to SharePoint’ action under SharePoint actions and add it. Now let’s configure this action to make a SharePoint REST call to determine user group membership.

Site Address: Select the site collection where your SharePoint Security group exists

Method: Get

Uri: api/web/sitegroups/getByName(‘SP Group Name’)/Users?$filter=Email eq ‘’

Replace ‘SP Group Name’ with your group name. Place cursor in between the single quotes after $filter=Email eq and select ‘Ask In PowerApps’ under manual content. This will auto generate a variable name that will be used as an input parameter for this Flow. The goal here is to pass the logged in user’s email id as a parameter from PowerApps to Flow.

Below is how the action looks after configuration. ‘RequestAdmins’ is the SharePoint User Group I used for this example.

chkUsergrp.png

This REST call will return an empty object if the user is not member of the group.

emptyresults.PNG

It will return an object with User properties in the following format if the user is a member of the group.

resultsjson.PNG
  • Now that we have an output from the REST call above, we can parse it to extract the results section. Add a ‘Set Variable’ action to set the variable ‘UserGroupInfo’ created earlier with the value set to expression body(‘CheckUserGroup’)[‘d’][‘results’]

extractUsrgrp.png

Here ‘CheckUserGroup’is the name of the previous action. If your action name has spaces, replace the spaces with underscore (_) character. At this stage, we have extracted the results which can be used to determine if the User is a member of the group.

  • Add a ‘Condition’ step to evaluate the results value. If the results object is empty then the user is not a member.

Use the expression @not(equals(variables(‘UserGroupInfo’), ‘[]’)) to evaluate the object.

‘UserGroupInfo’ is the variable used to store the object value and ‘[]’ compares to an empty object.

Set the variable ‘IsAdministrator’ that was initialized earlier to true if the condition evaluated to be true. If not, set it to be false.

isadmin.png

  • One final step in the flow would be to pass the results of our user group membership check back to PowerApps as an output parameter that can be used to enable certain features of the app for the logged in user. Add ‘Respond to PowerApps’ action and choose a text output. An output of type boolean would have been ideal but is not available at this time and we’ll stick to a text output. Provide a name for the text output parameter (I used ‘IsAdminUser’) and set the value to variable ‘IsAdministrator’.

respondtpPA.png

Here’s how entire Flow looks like and it’s time to save it and see it in action.

fullflow.png
  • Now that we have Flow ready, let’s implement and test it on the app. Navigate to your PowerApps app, create a new blank screen, click on Properties dropdown and select the ‘OnVisible’ event of the screen. Next click on the ‘Action’ tab and select ‘Flows’. Select the Flow you created to add it to the formula bar to associate the Flow to the ‘OnVisible’ event of the screen. Type the below functions on the ‘OnVisible’ formula bar.

First we are creating a variable (isAdmin) to store the user group membership status in Boolean format and setting the default value to be false. Next we are triggering the Flow ( ‘CheckUserPermission’ is the name of the Flow I created) and passing in an encoded format of the current logged in user’s email as the input parameter.

The output returned from Flow is being stored in a variable ‘UserGroupInfo’. Lastly, we are validating the value of the output parameter ‘isadminuser’ we configured earlier and setting the ‘isAdmin’ variable with Boolean equivalent.

  • Drop a button on the screen and set the “Visible’ property of the button to variable ‘isAdmin’. This will show/hide the button based on the value of the variable. You can set the ‘OnSelect’ event of the button to navigate to an Admin Only Screen.

And we are done! Publish the app and run. If all goes well, you should see that the flow ran successfully.

On the app side, Flow is triggered (when the screen is visible) with the current user’s encoded email id as the input parameter which in turns makes a SharePoint Rest Call to determine the user’s membership.It returns a Text output of value “True” or “False”. The app then validates the returned value and shows/hides the button based on the user’s group membership.

Here’s how my sample app looks like if the current user is an admin:

If the current user is not an admin then the admin button is hidden.

Resources

Here are some resources I found to be very useful to help design this solution:

  • Doctor Flow’s post how you can use SharePoint REST APIs in Flow

  • For those interested in the SharePoint REST/OData API, you can find the complete set of REST/OData APIs here.