Friday, December 31, 2021

PowerApps: Pagination In Gallery

Hello Friends,

Welcome back with another post on PowerApps. Have you ever encountered a scenario where you have hundreds or thousands of records in PowerApps gallery. The most tedious job at that time is to view all records by scrolling. It would be great if we have a pagination kind of functionality similar to other technologies. Unfortunately, PowerApps doesn't provide any pagination concept in Gallery as well as in Data table. Let's try to do with Gallery.

  1. Here, we will first fetch all records from SharePoint List. For this will take help of my earlier article PowerApps: Getting More Than 2000 Items (Using Sequence).
  2. Then we will try to create pagination upon gallery with search feature as well.I had created a list having 4 column with around 25000 items.

  3. Now open the PowerApps maker portal by clicking here.
  4. You may also go to PowerApps maker portal by clicking on Integrate >> PowerApps >> See all apps.
  5. Create a new PowerApps app by clicking "+ Create" >> "Canvas app from blank".
  6. Give in the name. Choose format as Tablet. Click on Create. It will take couple of minutes to create a blank canvas app. The moment, it creates and shows you the blank canvas screen to design, the first most important task you need to do is to Save the app. Click on File >> Save to save the app. The benefit of doing so is that until you save it, your app will remain in browser memory and if anyhow, browser gets closed, your hard work will get vanished. But, if you have saved the app at very beginning, then it gets saved on cloud and PowerApps maker portal start saving the changes every 1-2 minutes automatically.
  7. Let's move on. You will get Screen1 by default. Let's rename it ScreenGalleryPagination by clicking on 3 dots just next to the Screen1 name.
  8. Now we will add a SharePoint connection. For this click on cylinder icon (baiscally it is a database icon). Click on "+ Add data". Search for SharePoint and then click on SharePoint connection.
  9. It will show you the connection with your login. Click on same. Then it will show you the possible sites at right side. Select your SharePoint site. Then it will show you the SharePoint lists. Choose the list and click on Connect.
  10. Now we will fetch the items from this SharePoint list to PowerApps collection. For this click on Apps and then choose "OnStart" property.
  11. Now click on function box and input below code-
  12. ClearCollect(
        Coll_AllItems,
        []
    );
    ClearCollect(
        DataSourceFiltered,
        []
    );
    Set(
        FirstRecord,
        First(TestListSJ)
    );
    Set(
        LastRecord,
        First(
            Sort(
                TestListSJ,
                ID,
                Descending
            )
        )
    );
    Set(
        BatchSize,
        500
    );
    Set(
        MaxIteration,
        RoundUp(
            (LastRecord.ID - FirstRecord.ID) / BatchSize,
            0
        )
    );
    ClearCollect(
        TotalIterations,
        AddColumns(
            AddColumns(
                ForAll(
                    Sequence(MaxIteration),
                    Value
                ),
                "min",
                (FirstRecord.ID + (Value - 1) * BatchSize)
            ),
            "max",
            (FirstRecord.ID + Value * BatchSize)
        )
    );
    ForAll(
        TotalIterations,
        Collect(
            Coll_AllItems,
            Filter(
                TestListSJ,
                ItemID >= min && ItemID < max
            )
        )
    );
    ClearCollect(DataSourceForGalleryItems,Coll_AllItems);
    
  13. Let's explain one by one. Please match with color-
    1. Coll_AllItems: It is a collection to hold all items fetched from SharePoint list.
    2. DataSourceFiltered: It is a collection to hold the result obtained when we click on "Search Record" button.
    3. FirstRecord / LastRecord: These are variables to hold the First item and the Last item of SharePoint list (Sort By ID). 
    4. BatchSize: It is the number of records we will fetch in each call to SharePoint.
    5. MaxIteration: It is the number of total calls we need to made to SharePoint in order to fetch all records.
    6. TotalIterations: It is a collection which is holding the starting ID and the ending ID which will be passed in Filter call to SharePoint. (i.e. 0-500, 501-1000, 1001-1500, ...)
    7. ForAll: It is a function call using which we will fetch all records from SharePoint
    8. ClearCollect: This command we are using to make clone of Coll_AllItems collection. This clone will be used to assign back if we had made a call of Search Record and then we are making call of Search Page / Reload.
  14. With this, we have fetched all records from data source.
  15. Now, click on screen "ScreenGalleryPagination" and add a Gallery (Vertical). Name this gallery as "GalleryListData". Set-
    1. Layout: Title, Subtitle, and body
    2. TemplateSize: 50
    3. Height: Parent.Height-GalleryListData.Y-100
    4. Width: Parent.Width
  16. Title1 label-
    1. Rename it to lblID
    2. X: 0
    3. Y: 0
    4. Width: 100
    5. Height: GalleryListData.TemplateHeight
  17. Subtitle1 label-
    1. Rename it to lblTitle
    2. X: lblID.X+lblID.Width
    3. Y: 0
    4. Width: 400
    5. Height: GalleryListData.TemplateHeight
  18. Body1 label-
    1. Rename it to lblGUID
    2. X: lblTitle.X+lblTitle.Width
    3. Y: 0
    4. Width: 600
    5. Height: GalleryListData.TemplateHeight
  19. I have renamed according to my list columns. You may rename according to your requirements. Other cosmetic surgeries is up to you. 😊
  20. Set GalleryListData >> DataSource to DataSourceForGalleryItems

  21. Now click on Edit link just ahead of Fields property and set the which field is to be displayed in which label.
  22. Now, click on ScreenGalleryPagination and set it's "OnVisible" property. Here we will set 2 variables-
    1. PageSize: It the number of items to be displayed in gallery at a time.
    2. intFilterRecordsFromGallerySource: It is a number which will be used to filter the number of such items from entire collection using FirstN so that we can then use LastN with PageSize to get the actual items to be displayed on screen when user will click on Next/Previous icon.
  23. The code is-
  24. UpdateContext({PageSize: 0});
    UpdateContext({intFilterRecordsFromGallerySource: 0});
    UpdateContext(
        {
            PageSize: RoundDown(
                GalleryListData.Height / GalleryListData.TemplateHeight,
                0
            )
        }
    );
    UpdateContext({intFilterRecordsFromGallerySource: PageSize});
    
  25. Now, click on GalleryListData gallery and choose Items property and assign the below code.
  26. LastN(
        FirstN(
            DataSourceForGalleryItems,
            intFilterRecordsFromGallerySource
        ),
        If(
            Mod(
                intFilterRecordsFromGallerySource,
                PageSize
            ) = 0,
            PageSize,
            Mod(
                intFilterRecordsFromGallerySource,
                PageSize
            )
        )
    )
  27. This code is what we were talking about in above step. The moment you add this code, you may find that the gallery becomes blank. This is due to non initialization of variable. So, save the app and play it once.
  28. With this, we have completed the basic display part. Now the actual work starts where we will implement the pagination and will see, how it works.
  29. Let's add a reload button at top. For this, add a Reload icon at top and update it's "OnSelect" property as-
    1. ClearCollect(DataSourceForGalleryItems,Coll_AllItems);
      
  30. The other changes are-
    1. Rename it to IconReload
    2. Height: 40
    3. Width: 40
    4. Padding Left/Right/Top/Bottom: 5
    5. Color: RGBA(255, 255, 255, 1)
    6. Fill: RGBA(50, 86, 160, 1)
    7. Y: 0
    8. X: 1326 (or set it accordingly to make it extreme right)
  31. Now, we will add Previous / Next button. For this, add Left icon and update it's "OnSelect" property as-
    1. If(
          intFilterRecordsFromGallerySource < 2 * PageSize,
          UpdateContext({intFilterRecordsFromGallerySource: PageSize}),
          UpdateContext(
              {
                  intFilterRecordsFromGallerySource: intFilterRecordsFromGallerySource - If(
                      intFilterRecordsFromGallerySource = CountRows(Coll_AllItems),
                      If(
                          Mod(
                              CountRows(Coll_AllItems),
                              PageSize
                          ) = 0,
                          PageSize,
                          Mod(
                              CountRows(Coll_AllItems),
                              PageSize
                          )
                      ),
                      PageSize
                  )
              }
          )
      
      ); 
  32. The other changes are-
    1. Rename it to IconPrevious
    2. Height: 40
    3. Width: 80
    4. Padding Left/Right/Top/Bottom: 5
    5. Color: RGBA(255, 255, 255, 1)
    6. Fill: RGBA(50, 86, 160, 1)
    7. Y: GalleryListData.Y+GalleryListData.Height
    8. X: 0
  33. Now, add right icon and update it's "OnSelect" property as-
    1. UpdateContext(
          {
              intFilterRecordsFromGallerySource: intFilterRecordsFromGallerySource + If(
                  intFilterRecordsFromGallerySource + PageSize <= CountRows(Coll_AllItems),
                  PageSize,
                  If(
                      intFilterRecordsFromGallerySource = CountRows(Coll_AllItems),
                      0,
                      Mod(
                          CountRows(Coll_AllItems),
                          PageSize
                      )
                  )
              )
          }
      
  34. The other changes are-
    1. Rename it to IconRight
    2. Height: 40
    3. Width: 80
    4. Padding Left/Right/Top/Bottom: 5
    5. Color: RGBA(255, 255, 255, 1)
    6. Fill: RGBA(50, 86, 160, 1)
    7. Y: GalleryListData.Y+GalleryListData.Height
    8. X: Parent.Width-IconRight.Width
  35. Another section has been completed. Now save the app and play it. Click on Next / Previous buttons. The gallery is getting loaded accordingly.
  36. Now, we will try to show the 
    1. Page Number / Total Pages
    2. Total Records
    3. First Page
    4. Last Page
    5. Search By Page Number
    6. Search By Record Title
  37. Let's add Page Number / Total Pages. For this, add a label.
    1. Rename it to lblCurrentPage
    2. Width: 75
    3. Height: 40
    4. X: 0
    5. Y: Parent.Height-lblCurrentPage.Height
    6. BorderThickness: 1
    7. Text: RoundUp(intFilterRecordsFromGallerySource/PageSize,0)
    8. Align: Center
  38. Add another label.
    1. Rename it to lblSeparatorSignLabel
    2. Width: 30
    3. Height: 40
    4. X: lblCurrentPage.X+lblCurrentPage.Width
    5. Y: Parent.Height-lblSeparatorSignLabel.Height
    6. BorderThickness: 1
    7. Text: /
    8. Align: Center

  39. Add another label.
    1. Rename it to lblTotalPages
    2. Width: 80
    3. Height: 40
    4. X: lblSeparatorSignLabel.X+lblSeparatorSignLabel.Width
    5. Y: Parent.Height-lblTotalPages.Height
    6. BorderThickness: 1
    7. Text: RoundUp(CountRows(DataSourceForGalleryItems)/PageSize,0)
    8. Align: Center

  40. This way Page Number / Total Pages is done.
  41. Now we will Total Number of Records.
  42. Add a label.
    1. Rename it to lblTotalRecordsText
    2. Width: 130
    3. Height: 40
    4. X: lblTotalPages.X+lblTotalPages.Width+20
    5. Y: Parent.Height-lblTotalRecordsText.Height
    6. Italic: true
    7. Text: Total Records:

  43. Add another label.
    1. Rename it to lblTotalRecordsValue
    2. Width: 75
    3. Height: 40
    4. X: lblTotalRecordsText.X+lblTotalRecordsText.Width
    5. Y: Parent.Height-lblTotalRecordsValue.Height
    6. Text: CountRows(DataSourceForGalleryItems)

  44. This way, Total Records display gets done.
  45. Now we will work upon First Page & Last Page. Let's Add Last Page button.
  46. Add a button-
    1. Rename it to btnLastPage
    2. Width: 125
    3. Height: 40
    4. X: Parent.Width-btnLastPage.Width
    5. Y: Parent.Height-btnLastPage.Height
    6. Text: Last Page
    7. OnSelect: UpdateContext({intFilterRecordsFromGallerySource:CountRows(DataSourceForGalleryItems)});

  47. Add another button-
    1. Rename it to btnFirstPage
    2. Width: 125
    3. Height: 40
    4. X: Parent.Width-btnLastPage.Width-btnFirstPage.Width-20
    5. Y: Parent.Height-btnLFirstPage.Height
    6. Text: First Page
    7. OnSelect: UpdateContext({intFilterRecordsFromGallerySource:PageSize});
  48. This way First Page & Last Page also done.
  49. Now, we will implement Search Page / Search Record functionality. For this-
  50. Add a button-
    1. Rename it to btnSearchPage
    2. Width: 150
    3. Height: 40
    4. X: lblTotalRecordsValue.X+lblTotalRecordsValue.Width+20
    5. Y: Parent.Height-btnSearchPage.Height
    6. Text: Search Page
    7. OnSelect: 
      1. If(
            Value(txtSearchBox.Text) <= RoundUp(
                CountRows(DataSourceForGalleryItems) / PageSize,
                0
            ),
            UpdateContext({intFilterRecordsFromGallerySource: Value(txtSearchBox.Text) * PageSize})
        )
        
  51. The moment, you add the OnSelect code, it will error because, till now, we haven't added textbox, from which it will pick the data. So don't worry,, we are going to add it next.
  52. Add a textbox (Text input).
    1. Rename it to txtSearchBox
    2. Default: "" (Blank)
    3. Width: 250
    4. Height: 40
    5. X: btnSearchPage.X+btnSearchPage.Width+20
    6. Y: Parent.Height-txtSearchBox.Height
  53. Add another button
    1. Rename it to btnSearchRecord
    2. Width: 150
    3. Height: 40
    4. X: txtSearchBox.X+txtSearchBox.Width+20
    5. Y: Parent.Height-btnSearchRecord.Height
    6. Text: Search Record
    7. OnSelect: 
      1. ClearCollect(
            DataSourceFiltered,
            Search(
                Coll_AllItems,
                txtSearchBox.Text,
                "Title"
            )
        );
        ClearCollect(
            DataSourceForGalleryItems,
            DataSourceFiltered
        );
        UpdateContext({intFilterRecordsFromGallerySource: PageSize});
        

  54. This way, we have completed our functionality. Below are the screenshots.
  55. Home Screen-
  56. Next Button-
  57. Previous Button-
  58. Search Page-
  59. Search Record-
  60. Reload-
  61. Last Page-
  62. First Page-
  63. This way you can implement pagination.
  64. You may also implement numeric pagination. For this, you need to visit my next post. PowerApps: Pagination In Gallery (Implement Numeric Pagination).
With this, I am concluding this post.
Happy Coding !!!
Will see you again with some new topics.

Stay Safe !
Stay Healthy !


Bye... Bye... 2021
Wishing All Readers & Coders . . .        !!! A Very Happy New Year !!!

Wednesday, December 29, 2021

Power Automate: Get All Records (Do-Until)

Hello Friends,

Welcome back with another post on Power Automate. In this post, we will discuss about getting all records from SharePoint list irrespective of items count. As we know, that the "Get Items" action results only 100 records and so is the "Send an HTTP request to SharePoint". In "Get Items" action, we can stretch this limit by using Top Count which itself has max limit of 5000 items irrespective of Power Automate license. If any list have more than 10000 items then how do we get all items.

The answer is using Do-Until. It is a great feature using which we can loop on SharePoint list and get all Items. All these items can be stored in an array and then can be processed. So, what are we waiting for? Let's start-

  1. First of all we will create a list and add thousands of records ( 😉 ). I had created a list and added around 25000 records (using a manual flow).
  2. Now, open the Power Automate maker portal and create an "Instant cloud flow" of type "Manually trigger a flow". I am using this flow for demo purpose. You may use any flow.

  3. Add an action "Initialize variable", declare a variable "intItemID" of type Integer and set it to ZERO (0).
  4. Click on Save to save the flow.
  5. Add an action "Initialize variable", declare a variable "arrAllItems" of type Array and set it to [].
  6. Click on Save to save the flow.
  7. Add an action "Initialize variable", declare a variable "blnIsEmpty" of type Boolean and set it to false.
  8. Now, the process starts. Add an action "Do until".
  9. Add the validity condition as "blnIsEmpty" is equal to "true".
  10. Now you will see a link "Change limits". Click on it. It will show 2 type of limits-
    1. Count: Maximum number of recursive calls, this Do-until action will perform. Max limit is 5000
    2. Timeout: It is a value which we can use to stop loop if the Loop condition is not met to avoid indefinitely running. More details can be found here.
  11. Change Count to 3000.
  12. Now click on "Add an action" inside this Do-until action and choose "Get Items" action.
  13. Choose Site Address, List Name, Filter Query (as ID gt intItemID), Order By (ID).
  14. Now add a "Select" action. It will take input from Get Items output and in Map, we will map fields according to our requirement.
  15. Map ID & Title fields.
  16. Next, we will make a union (join) of this output with arrAllItems using Compose action. Here we will use "union" function.
  17. Now, we will assign the output of this compose action to the variable arrAllItems so that in next loop call, the updated array will be available to Compose action for union. For this, use "Set variable" action.
  18. Now, we will reset the intItemId with the last record, we received from "Get Items" action. Again use "Set variable" action. To get the last item id, we will use the "last' function.
  19. The last action we need to add is to set blnIsEmpty variable. For this we will check if the output of "Get Items" action is blank. If blank, then make blnIsEmpty as true. The "empty" function server this condition perfectly. Let add another action "Set variable".
  20. Now, the last thing, we need to add is "Configure run after". In case, if the output of "Get Items" action is blank, the "Set intItemID" action will get failed and throw error. It will result in failure of execution of our flow. So, to avoid that scenario and terminating the flow of Do-until properly, we will update the "Configure run after" property of "Set blnIsEmpty" as "is successful" and "has failed".
  21. After selecting both check boxes, click on Done.
  22. That's all. Now Save the flow and execute it.
  23. As we were having 25700 items and each iteration (Get Items) returns 100 records, it took 258 iterations to get the data. The 258th iteration is what where it found blnIsEmpty as true and terminated the Do-until process.
  24. To verify the output, I had fetched the length of the array "arrAllItems". It comes out to be 25700 which is exactly same as in the list.
  25. This way, we can get all items in Power Automate.
With this, I am concluding this post.
Happy Coding !!!
Will see you again with some new topics.

Stay Safe !
Stay Healthy !

Tuesday, December 28, 2021

Get User Permissions From SharePoint Group In PowerApps: Part 5

 Hello Friends.

Welcome back in the User Permissions series. In previous post, we had fetched SharePoint Group name, Permissions and Users. Links of past 4 posts are-

  1. Get User Permissions From SharePoint Group In PowerApps: Part 1
  2. Get User Permissions From SharePoint Group In PowerApps: Part 2
  3. Get User Permissions From SharePoint Group In PowerApps: Part 3
  4. Get User Permissions From SharePoint Group In PowerApps: Part 4
This is the last post of this series. In this post, we will process the output of Users and apply for each loop to check if the requested user is present in the group. If present, save the group permission details.
Let's start-
  1. Add a Condition action after the Parse Users action and name it Check User Match. Now, as the data may be case sensitive hence we will use toLower function.

  2. Now choose the UserPrincipalName in left input box. The moment, you choose it, an additional Apply to each action is applied to this condition action. This is because there could be multiple users. Rename this action as For Each User.

  3. Now we will re apply the condition (Check User Match) input values by applying toLower function.
  4. Now come to If Yes condition. Here we will add "Append to string variable" action and save the name of the permission name (Full control / Contribute ...) and other stuff from RoleDefinitionBindings to strUserPermissions variable.
  5. Now the moment we will add the "Name" field from "Parse_RoleDefinitionBindings", it will add a "Apply to each" action wrapper upon this Append action.


  6. Rename this "Apply to each" to "For Each RoleDefinitionBindings".
  7.  
  8. Add a delimiter at the end of value.
  9. You may add multiple information values separated by a Data Delimiter (semicolon here) and a Row Delimiter (Hash here)
  10. Now the last step is to send this data as output response back to PowerApps. For this, collapse all the actions and it will looks like-
  11. Add new action "Respond to a PowerApp or flow'-
  12. Then click on Text output type-
  13. Give the output title as "Response" or whatever you wish. It will provide you the output in PowerApps and value as "strUserPermissions"

  14. This way, it completes the flow and this series as well.
  15. In PowerApps, you need to split data based on delimiters and use accordingly.
  16. You may visit below URL to do the same-
  17. PowerApps: Convert Delimiter String To Collection Using Ungroup And ForAll
With this, I am concluding this post.
Happy Coding !!!
Will see you again with some new topics.

Stay Safe !
Stay Healthy !