Blazor - Keeping user preferences in a tiled dashboard app

Viewed 240

I have a dashboard app, that displays certain applications to the user, loaded from the database..

enter image description here

The tiles are draggable (Jquery).. But im struggling to understand how to move a tile, and then keep the state, so that a user can come back the next day, and still have his preferences.

I have tried to follow Telerik demo, but unfortunately, i am a student, i can't afford the license.. Has anybody done something similar, to keep a users preferences?

Here is the Telerik demo, im struggling with what would be in the models.

Local storage for state

using Microsoft.JSInterop;
using System.Text.Json;
using System.Threading.Tasks;

namespace TelerikBlazorDemos.Services
{
    public class LocalStorage
    {
        protected IJSRuntime JSRuntimeInstance { get; set; }

        public LocalStorage(IJSRuntime jsRuntime)
        {
            JSRuntimeInstance = jsRuntime;
        }

        public ValueTask SetItem(string key, object data)
        {
            return JSRuntimeInstance.InvokeVoidAsync(
                "localStorage.setItem",
                new object[] {
                    key,
                    JsonSerializer.Serialize(data)
                });
        }

        public async Task<T> GetItem<T>(string key)
        {
            var data = await JSRuntimeInstance.InvokeAsync<string>("localStorage.getItem", key);
            if (!string.IsNullOrEmpty(data))
            {
                return JsonSerializer.Deserialize<T>(data);
            }

            return default;
        }

        public ValueTask RemoveItem(string key)
        {
            return JSRuntimeInstance.InvokeVoidAsync("localStorage.removeItem", key);
        }
    }
}

This is the part im struggling with.. @Code{ }

@page "/tilelayout/persist-state"

@inject LocalStorage LocalStorage
@inject IJSRuntime JsInterop

@using TelerikBlazorDemos.Shared.DemoConfigurator
<DemoConfigurator>
    <DemoConfiguratorColumn>
        <TelerikButton OnClick="@SaveState" Icon="save" Class="mr-xs">Save State</TelerikButton>
        <TelerikButton OnClick="@ReloadPage" Icon="reload" Class="mr-xs">Reload the page</TelerikButton>
        <TelerikButton OnClick="@LoadState" Icon="download" Class="mr-xs">Load last State</TelerikButton>
        <TelerikButton OnClick="@SetExplicitState" Icon="gear" Class="mr-xs">Configure State</TelerikButton>
    </DemoConfiguratorColumn>
</DemoConfigurator>

<div class="demo-alert demo-alert-info" role="alert">
    <strong>Change the Tile Layout</strong> (resize and reorder some tiles, remember their order) and
    <strong>Save</strong> the TileLayout state, then <strong>Reload</strong> the page to see the state persisted.
    <strong>Change</strong> the layout some more and <strong>Load</strong> the state to see the last one preserved.
    You can manage the TileLayout state with your own code to put it in a specific configuration through the <strong>Configure</strong> button.
    This demo will remember your last layout until your clear your browser storage or click the Configure button to put it in its initial state.
</div>

<div style="display: inline-block;">
    <TelerikTileLayout @ref="@TileLayoutInstance"
                       Columns="3"
                       ColumnWidth="285px"
                       RowHeight="285px"
                       Resizable="true"
                       Reorderable="true">
        <TileLayoutItems>
            <TileLayoutItem HeaderText="San Francisco">
                <Content>
                    <img class="k-card-image" draggable="false" src="images/cards/sanfran.jpg" />
                </Content>
            </TileLayoutItem>
            <TileLayoutItem HeaderText="South Africa">
                <Content>
                    <img class="k-card-image" draggable="false" src="images/cards/south-africa.jpg" />
                </Content>
            </TileLayoutItem>
            <TileLayoutItem HeaderText="Sofia" RowSpan="2">
                <Content>
                    <img class="k-card-image" draggable="false" src="images/cards/sofia.jpg" />
                    <div class="text-center">
                        <div class="k-card-body">
                            <p>Sofia is the largest city and capital of Bulgaria. Sofia City Province has an area of 1344 km<sup>2</sup> while the surrounding and much bigger Sofia Province is 7,059 km<sup>2</sup>. The city is situated in the western part of the country at the northern foot of the Vitosha mountain, in the Sofia Valley that is surrounded by the Balkan mountains to the north. The valley has an average altitude of 550 metres (1,800 ft). Unlike most European capitals, Sofia does not straddle any large river, but is surrounded by comparatively high mountains on all sides.</p>
                            <br />
                            <em>* The data used in this demo is taken from <a href="https://wikipedia.com" target="_blank"><em>wikipedia.com</em></a></em>
                        </div>
                    </div>
                </Content>
            </TileLayoutItem>
            <TileLayoutItem HeaderText="Rome" ColSpan="2" RowSpan="2">
                <Content>
                    <img class="k-card-image image-center" draggable="false" src="images/cards/rome.jpg" />
                    <div class="text-center">
                        <div class="k-card-body">
                            <p>
                               
                            </p>
                            <p>
                                <em>* The data used in this demo is taken from <a href="https://wikipedia.com" target="_blank"><em>wikipedia.com</em></a></em>
                            </p>
                        </div>
                    </div>
                </Content>
            </TileLayoutItem>
            <TileLayoutItem HeaderText="Barcelona">
                <Content>
                    <img class="k-card-image" draggable="false" src="images/cards/barcelona.jpg" />
                </Content>
            </TileLayoutItem>
        </TileLayoutItems>
    </TelerikTileLayout>
</div>

@code { TelerikTileLayout TileLayoutInstance { get; set; }
            TileLayoutState SavedState { get; set; }
            string stateStorageKey = "TelerikBlazorTileLayoutStateDemoKey";

            async Task SaveState()
            {
                var state = TileLayoutInstance.GetState();
                await LocalStorage.SetItem(stateStorageKey, state);
            }

            async Task LoadState()
            {
                TileLayoutState storedState = await LocalStorage.GetItem<TileLayoutState>(stateStorageKey);
                if (storedState != null)
                {
                    TileLayoutInstance.SetState(storedState);
                }
            }

            void ReloadPage()
            {
                JsInterop.InvokeVoidAsync("window.location.reload");
            }

            async void SetExplicitState()
            {
                TileLayoutState desiredState = GetDefaultDemoState();
                TileLayoutInstance.SetState(desiredState);
                await SaveState();
            }

            protected override async Task OnAfterRenderAsync(bool firstRender)
            {
                var state = await LocalStorage.GetItem<TileLayoutState>(stateStorageKey);
                if (state != null && TileLayoutInstance != null)
                {
                    TileLayoutInstance.SetState(state);
                }
            }

            TileLayoutState GetDefaultDemoState()
            {
                TileLayoutState defaultDemoState = new TileLayoutState()
                {
                    ItemStates = new List<TileLayoutItemState>()
            {
                new TileLayoutItemState { Order = 1, ColSpan = 1, RowSpan = 1 },
                new TileLayoutItemState { Order = 2, ColSpan = 1, RowSpan = 1 },
                new TileLayoutItemState { Order = 3, ColSpan = 1, RowSpan = 2 },
                new TileLayoutItemState { Order = 4, ColSpan = 2, RowSpan = 2 },
                new TileLayoutItemState { Order = 5, ColSpan = 1, RowSpan = 1 },
            }
                };
                return defaultDemoState;
            } }

<style>
    .k-card-image {
        width: 285px;
        height: 189px;
    }

    .k-card-body {
        overflow: auto;
    }

    .image-center {
        display: block;
        margin: auto;
    }
</style>

For instance TileLayoutInstance.SetState(storedState); I don't know how this is set up behind the bonnet. p.s im using .netCore5 and visual studio 2019. Any help is much appreciated.

1 Answers

In order to save a User's state I would recommend storing the order and id of the icons in a database. (I have not had much experience with Blazor Local Storage) I would then create a C# dashboard object for each user that contains an ordered list of their desired icons. I would also create a C# object for each icon with the information needed to render your TileLayoutItem.

On first use I would have the user set their dashboard and call a save method so you can create a list of icons in the order they need to be rendered. Save this list to the database corresponding to a user account if you set it up with authentication, or create a user ID specific to each user.

You can store these in any database you are familiar with, or you can go the route of Entity Framework Core EF For Blazor Server and set it up that way.

As the user requests the page make a query to pull the specific user's icons, sort this list to the user's preference if not already, and then use a @foreach() statement in the component to render TileLayoutItem based upon the information stored in each icon object.

Related