Burgers & Bytes
September 10, 2024

Personal view for a Power Apps gallery

Sep 10, 2024  •  5   • 948 
Table of contents

In SharePoint lists, we’re familiar to creating both shared views for all users and personal views tailored to individual preferences. As you develop Power Apps, you might frequently ask, “Can I create a custom view just for me?” Users often need different information displayed in their views. This blog explains how you can accomplish this in Power Apps!

End result

The end result is a gallery including a header for which users can create their own view or use the default view.

Personal view

Setup

In this scenario the data is stored in SharePoint. Two lists are available:

  1. Job Application Tracker
  2. Personal views

The first list contains the data that needs to be displayed: Job Application Tracker

The second lists stores the information for the personal view per user:

Personal view

Basic info

The app uses different collection, to understand the solution hereby the names and purpose per collection.

NamePurpose
colDefaultViewThis collection is needed to store the information for the default view. When the users doesn’t have a personal view, this view is used
colPersonalViewThis collection is needed to store the information of the personal view, this data is stored in SharePoint column in JSON format
colDisplayViewThis collection is needed to store the information which is used to display. Filled with information of the default view or the personal view.
colEditViewThis collection is needed to store the information while editing the personal view. This collection is used so the user can also cancel the editing and go back to the previous saved view.

On the Loading screen a timer is set in which:

//Load data into collection
Clear(colJobApplicationTracker);

ForAll('Job application tracker',
    Collect(colJobApplicationTracker, 
    {
        JobID: ThisRecord.Title,
        Company:ThisRecord.Company,
        Role: ThisRecord.Role.Value,
        City: ThisRecord.City.Value,
        Deadline: ThisRecord.Deadline,
        JobType: ThisRecord.'Job type'.Value,
        Remote: ThisRecord.'Remote?'.Value,
        Supportsrelocation: ThisRecord.'Supports relocation?'
    }
    )
);

//Set settings for default view
ClearCollect(
    colDefaultView,
    [
        {ColumnID: 1, ColumnName: "JobID", Name: "Job ID", Visible: true, Width: 80, Sortable: true, Order:1},
        {ColumnID: 2, ColumnName: "Company", Name: "Company", Visible: true, Width: 150, Sortable: true, Order:2},
        {ColumnID: 3, ColumnName: "Role", Name: "Role", Visible: true, Width: 120, Sortable: true, Order:3},
        {ColumnID: 4, ColumnName: "City", Name: "City", Visible: true, Width: 120, Sortable: true, Order:4},
        {ColumnID: 5, ColumnName: "Deadline", Name: "Deadline", Visible: true, Width: 100, Sortable: true, Order:5},
        {ColumnID: 6, ColumnName: "JobType", Name: "Job type", Visible: true, Width: 100, Sortable: true, Order:6},
        {ColumnID: 7, ColumnName: "Remote", Name: "Remote", Visible: true, Width: 200, Sortable: true, Order:7},
        {ColumnID: 9, ColumnName: "Supportsrelocation", Name: "Supports relocation", Visible: true, Width: 150, Sortable: true, Order:8}
   
    ]
);

Clear(colPersonalView);
ForAll(ParseJSON(LookUp('Personal views', Title = User().Email).Viewsettings),
Collect(colPersonalView, 
{
        ColumnID: Value(ThisRecord.ColumnID),
        ColumnName: Text(ThisRecord.ColumnName), 
        Name: Text(ThisRecord.Name),
        Visible: Boolean(ThisRecord.Visible), 
        Width: Value(ThisRecord.Width), 
        Sortable: Boolean(ThisRecord.Sortable), 
        Order:Value(ThisRecord.Order)
})
);

Clear(colDisplayView);

If(CountRows(colPersonalView) = 0, 
    Collect(colDisplayView, colDefaultView), 
    Collect(colDisplayView, colPersonalView)
);

Navigate('Personal View Screen');

Main Screen

The structure of the Screen:

Screen setup

On the container (conInfo) the FillPortions is set: If(varvisibleEditView, 3, 1).

On the Gallery for the Header for every column there is a text and a button element.: Header gallery

The properties set on Gallery level:

PropertyValue
ItemsFirst(colDisplayView)
LayoutMinWidthSum(Filter(colDisplayView, Visible = true), Width)

The properties for the text elements:

PropertyValue
TextLookUp(colDisplayView, Name = “Job ID”).Name
VisibleLookUp(colDisplayView, Name = “Job ID”).Visible
WidthLookUp(colDisplayView, Name = “Job ID”).Width
XSum(Filter(colDisplayView, Order < LookUp(colDisplayView, Name = “Job ID”).Order), Width)

The properties for the button for sorting:

PropertyValue
VisibleIf(LookUp(colDisplayView, Name = “Job ID”).Visible, LookUp(colDisplayView, Name = “Job ID”).Sortable)
XtxtHeaderJobID.X + txtHeaderJobID.Width - Self.Width
OnSelectUpdateContext({SortColumn: LookUp(colDisplayView, Name = “Job ID”).ColumnName}); UpdateContext({CustomSortOrder: !CustomSortOrder});

The gallery for the items for every column there is text element.

Items gallery

The properties set on Gallery level:

PropertyValue
ItemsSortByColumns(colJobApplicationTracker,SortColumn, If(CustomSortOrder, SortOrder.Ascending, SortOrder.Descending))
WidthSum(Filter(colDisplayView, Visible = true), Width)

The properties for the text elements:

PropertyValue
TextThisItem.JobID
VisibleLookUp(colDisplayView, Name = “Job ID”).Visible
WidthLookUp(colDisplayView, Name = “Job ID”).Width
XSum(Filter(colDisplayView, Order < LookUp(colDisplayView, Name = “Job ID”).Order), Width)

Edit View

The OnSelect of the button View sets the visibility of the container for editing purposes and the collection for the personal view is stored into a collection, if already available for the current user. View button

UpdateContext({varvisibleEditView: true });

Clear(colPersonalView);
ForAll(ParseJSON(LookUp('Personal views', Title = User().Email).Viewsettings),
Collect(colPersonalView, 
{
        ColumnID: Value(ThisRecord.ColumnID),
        ColumnName: Text(ThisRecord.ColumnName), 
        Name: Text(ThisRecord.Name),
        Visible: Boolean(ThisRecord.Visible), 
        Width: Value(ThisRecord.Width), 
        Sortable: Boolean(ThisRecord.Sortable), 
        Order:Value(ThisRecord.Order)

})
);
Clear(colEditView);

If(CountRows(colPersonalView) = 0, 
    Collect(colEditView, colDefaultView), 
    Collect(colEditView,colPersonalView)
);

The elements in the container:

Edit view container

The OnSelect of the ArrowUp button (change -1 to +1 for the Arrow Down button)

Set(selectedItem, ThisItem);

// Find the item currently above the selected item
Set(itemAbove, LookUp(colEditView, Order = selectedItem.Order - 1));

// Swap Order values if there is an item above
If(
    !IsBlank(itemAbove),
    // Update the item above
    Patch(
        colEditView,
        itemAbove,
        { Order: selectedItem.Order }
    );
    
    // Update the selected item
    Patch(
        colEditView,
        selectedItem,
        { Order: selectedItem.Order - 1 }
    )
);

The OnChange for the txtWidth:

Patch(
    colEditView,
    LookUp(colEditView, ColumnID = ThisItem.ColumnID),
    { Width: Value(Self.Value) }
)

Apply or cancel

Apply cancel

On the Apply button:

If(CountRows(colPersonalView) = 0, 
// Create new item for user
    Patch('Personal views', 
    Defaults('Personal views'),
    {
        Title: User().Email,
        Viewsettings: Text(JSON(colEditView))
    }),
// Update view for user
    Patch('Personal views', 
    LookUp('Personal views', Title=User().Email),
    {
        Viewsettings: Text(JSON(colEditView))
    })
);

ClearCollect(colDisplayView, colEditView);
Clear(colEditView);
UpdateContext({varvisibleEditView: false });

On the Cancel button: This button makes it possibly for the user to cancel the change of the personal view.

Clear(colEditView);
UpdateContext({varvisibleEditView: false });

All these setup makes it possible to create a personal view per user!

Conclusion

In this setup a user can create one personal view, off course you can think of scenarios in which multiple view can be created and even view to share with all users.

comments powered by Disqus
Empowering productivity - one blog at a time!