Unplanned
Last Updated: 02 Feb 2023 11:33 by David

Cases:

  • I am using the Template to control the rendering of the Drawer. When I place an <a> tag and focus it, I cannot use the Enter key to navigate to the desired link. The component traps the onkeypress event, which it should not.
  • When I place a TreeView inside the Drawer Template and focus it, the keyboard navigation does not work because the Drawer traps the HTML events like onkeydown.

<Admin>

Workaround for the first case:

<script suppress-error="BL9992">
    window.navigateToHref = (ev) => {
        if (ev.key === "Enter") {
            location.href = ev.target.href
        }
    };
</script>

@* This example shows how to create header and footer for the Drawer and select an item manually. *@
<TelerikDrawer @bind-Expanded="@DrawerExpanded"
               Data="@Data"
               MiniMode="true"
               Mode="@DrawerMode.Push"
               @bind-SelectedItem="@SelectedItem"
               @ref="@DrawerRef">
    <Template>
        @* the header *@
        <div>
            <TelerikButton OnClick="@(() => DrawerRef.ToggleAsync())" Icon="@FontIcon.Menu" />
            @if (DrawerExpanded)
            {
                <div class="text-info" style="border-bottom:solid; font-weight: bold; margin-bottom: 3em; white-space:nowrap">
                    <a href="https://google.com" onkeydown="navigateToHref(event)">
                        My Custom Navigation to Google
                    </a>
                </div>
            }
            else
            {
                <div class="text-info" style="border-bottom:solid; font-weight: bold;">
                    Nav
                </div>
            }
        </div>

        @* custom items rendering and item selection *@

        <div class="k-drawer-items">
            <ul>
                @if (SelectedItem != null && DrawerExpanded)
                {
                    <li class="k-drawer-item" style="white-space:nowrap">
                        <div>
                            <p><strong>@SelectedItem.Text</strong></p>
                            <p>@SelectedItem.Description</p>
                        </div>
                    </li>
                }

                @foreach (var item in Data)
                {
                    @* Use onclick to handle manual item selection *@
                    <li @onclick="@(() => SelectedItem = item)"
                    class="k-drawer-item @GetSelectedItemClass(item)" style="white-space:nowrap">
                        <TelerikFontIcon Icon="@item.Icon"></TelerikFontIcon>
                        @if (DrawerExpanded)
                        {
                            <div>
                                <div>@item.Text</div>
                            </div>
                        }
                    </li>
                }
            </ul>
        </div>

        @* the footer *@
        @if (DrawerExpanded)
        {
            <div style="text-align: center; margin-top: 3em; padding-top: 2em; border-top: 2px solid black; white-space:nowrap">
                <img src="user-avatar.png" alt="my avatar" style="border-radius: 50%; width: 50px; height: 50px;" />
                <br /><br />
                <TelerikButton Icon="@FontIcon.Logout" ThemeColor="primary">Log Out</TelerikButton>
            </div>
        }
    </Template>
    <DrawerContent>
        <div class="m-5">Content for @SelectedItem?.Text - @SelectedItem?.Description</div>
    </DrawerContent>
</TelerikDrawer>


@code {
    public TelerikDrawer<DrawerItem> DrawerRef { get; set; }
    public DrawerItem SelectedItem { get; set; }
    public bool DrawerExpanded { get; set; } = true;
    public IEnumerable<DrawerItem> Data { get; set; } = new List<DrawerItem>
    {
        new DrawerItem {Text = "Shopping Cart", Icon = FontIcon.Cart, Description = "Items in shopping cart"},
        new DrawerItem {Text = "Settings", Icon = FontIcon.Gear, Description = "My profile settings"},
        new DrawerItem {Text = "Notifications", Icon = FontIcon.ExclamationCircle, Description = "My profile notifications"},
        new DrawerItem {Text = "Calendar", Icon = FontIcon.Calendar, Description = "My events"},
    };

    public string GetSelectedItemClass(DrawerItem item)
    {
        if (SelectedItem == null) return string.Empty;
        return SelectedItem.Text.ToLowerInvariant().Equals(item.Text.ToLowerInvariant()) ? "text-info" : "";
    }

    public class DrawerItem
    {
        public string Text { get; set; }
        public FontIcon? Icon { get; set; }
        public string Description { get; set; }
    }
}

</Admin>

Declined
Last Updated: 11 Nov 2020 14:22 by ADMIN

I'm using a custom drawer item to handle toggling the drawer open and close. Every time I click on my custom toggle item I call Drawer.ToggleAsync() (which I trigger on the SelectedItemChanged event handler). If the drawer is expanded to begin with, it is correctly collapsed to the mini view. However, if I click the toggle item again, the drawer expands and then immediately re-collapses itself. I put a breakpoint on the code that calls Drawer.ToggleAsync(), and it's only getting hit once for each click. This is very strange behavior. Please help me work out what's going on.

P.S If there is a better way to handle the click on my custom toggle drawer item (rather than using SelectedItemChanged), please let me know.

Code

@inherits LayoutComponentBase

<TelerikRootComponent>

    <div class="page">
        <div class="drawer-container">
            <TelerikDrawer 
                           @ref="@Drawer" 
                           Data="Data" 
                           MiniMode="true"
                           Expanded="true"
                           Mode="@DrawerMode.Push" 
                           SelectedItemChanged="((DrawerItem item) => SelectedItemChangedHandler(item))">
                <Content>
                    <div class="main">
                        <div class="content px-4">
                            @Body
                        </div>
                    </div>
                </Content>
            </TelerikDrawer>
        </div>
    </div>
</TelerikRootComponent>

@code {
    public TelerikDrawer<DrawerItem> Drawer { get; set; }
    public DrawerItem SelectedItem { get; set; }
    public IEnumerable<DrawerItem> Data { get; set; } =
        new List<DrawerItem>
        {
            new DrawerItem { Text = "Item1", Icon = IconName.Inbox },
            new DrawerItem { Text = "Item2", Icon = IconName.Information },
            new DrawerItem { Text = "Item3", Icon = IconName.MarkerPin },
            new DrawerItem { IsSeparator = true },
            new DrawerItem { Text = "Item4", Icon = IconName.Inbox },
            new DrawerItem { Text = "Item5", Icon = IconName.Inbox },
            new DrawerItem { Text = "Toggle", Icon = IconName.ArrowChevronLeft, IsToggle = true },
            };

    protected override Task OnInitializedAsync()
    {
        SelectedItem = Data.First();

        return base.OnInitializedAsync();
    }

    public class DrawerItem
    {
        public string Text { get; set; }
        public string Icon { get; set; }
        public bool IsSeparator { get; set; }
        public bool IsToggle { get; set; }
    }

    private async Task SelectedItemChangedHandler(DrawerItem item)
    {
        SelectedItem = item;

        if (item.IsToggle)
        {
            await Drawer.ToggleAsync();

            if (Drawer.Expanded)
            {
                item.Icon = IconName.ArrowChevronRight;
            }
            else
            {
                item.Icon = IconName.ArrowChevronLeft;
            }
        }
    }
} 


Declined
Last Updated: 27 Aug 2020 09:17 by ADMIN

I hate to refer to this as a bug, as I'm sure there is something we are missing. Here's are the bullet points of the scenario:

  • We are using Telerik Drawer as our "NavMenu" component
  • We are using Azure AD for Authentication

In the OnLogInSucceeded section of our Authentication.razor, we are building the user specific menu from SQL tables, structuring it in a hierarchical DrawerItem list.

When I pass that back to the Data property of the TelerikDrawer, the display does not update. StateHasChanged doesn't have any impact. We have tried tying it to a "service" to listen for changes. We have even called an event that would manually make the update.


MAIN LAYOUT

@inherits LayoutComponentBase
@inject NavigationManager navigationManager
@inject PublicClient Http
@inject IMatToaster toast

@using G2_Field.Shared
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

@inject IAuthorizationService AuthorizationService
@inject Employee dbUSER

@attribute [Authorize]


    <TelerikRootComponent>
        <div>
            <NavMenu @ref="nm" items="@Data" />
        </div>

        <div class="app-main">
            <div class="app-bar">
                <div style="float:left">
                    <TelerikButton OnClick="@ToggleDrawer" Icon="@Telerik.Blazor.IconName.Menu" Class="k-flat" />
                </div>
                <div style="float:right">
                    <LoginDisplay />
                </div>
            </div>
            <div>
                @Body
            </div>
        </div>
    </TelerikRootComponent>
    <MatToastContainer />

    @code {
        public TelerikDrawer<DrawerItem> Drawer { get; set; }
        public DrawerItem SelectedItem { get; set; }
        public bool Expanded { get; set; } = true;
        NavMenu nm;

        public List<DrawerItem> Data { get; set; } =
        new List<DrawerItem>
        {
            new DrawerItem { Text = "HOME", Icon = Telerik.Blazor.IconName.Window, Description = "Home", URL="" }
        };

        public async Task ToggleDrawer() 
        {
            await nm.ToggleDrawer();
        }

        public async Task OnSidebarChange(List<DrawerItem> di)
        {
            Data = di;
            Console.WriteLine(di.Last().Children.Last().Text);
            await nm.RefreshMenu(di);
        }...

AUTHENTICATION

@page "/authentication/{Action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using G2_Field.Shared
@using System.Security.Claims
@inject Employee dbUSER
@inject PublicClient Http
@using System.Threading.Tasks

<RemoteAuthenticatorView Action="@Action" OnLogInSucceeded=@OnLogInSucceeded>
    <LoggingIn>
        You are about to be redirected to https://login.microsoftonline.com.
    </LoggingIn>
</RemoteAuthenticatorView> 

@code{
    [Parameter] public string Action { get; set; }
    AppCategories[] iMenu = new AppCategories[0];
    [CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }

    [CascadingParameter] public Task<AuthenticationState> AuthenticationState { get; set; }
    [CascadingParameter] public List<DrawerItem> d { get; set; }
    [CascadingParameter(Name = "theMainLayout")] public MainLayout ml { get; set; }

    public async void OnLogInSucceeded()
    {
        var user = (await AuthenticationState).User;
        var un = (await AuthenticationState).User.Claims.ToList();
        var username = "";
        if (user.Identity.IsAuthenticated)
        {
            // Do some stuff
            Console.WriteLine("Log In Succeeded Event Fired");

            foreach (Claim u in un)
            {
                //Console.WriteLine(u.Type + " = " +  u.Value.ToString());
                if (u.Type == "preferred_username") { username = u.Value.ToString(); }
            }
            dbUSER = await Http.Client.GetJsonAsync<Employee>("/api/Index/GetCurrentEmployee/" + username);

            Console.WriteLine("EmpUserName=" + dbUSER.EmpUserName);

            var gUserName = dbUSER.EmpUserName;
            iMenu = await Http.Client.GetJsonAsync<AppCategories[]>("/api/Index/GetUserAppCategories/" + gUserName);

            d = new List<DrawerItem>();

            d.Add(new DrawerItem { Text = "HOME", Icon = "fa-home", Description = "Home", URL = "" });
            foreach (var app in iMenu)
            {
                List<DrawerItem> y = new List<DrawerItem>();
                foreach (var cat in app.ItemList)
                {
                    DrawerItem z = new DrawerItem
                    {
                        Text = cat.MenuItemTitle,
                        Icon = cat.MenuItemIcon,
                        Description = cat.MenuItemDescription,
                        URL = cat.MenuItemURL
                    };
                    y.Add(z);
                }

                DrawerItem x = new DrawerItem {
                    Text = app.MenuCategoryTitle,
                    Icon = Telerik.Blazor.IconName.Menu,
                    Description = app.MenuCategoryTitle,
                    Children = y
                };
                d.Add(x);
            }
            StateHasChanged();
            = new MainLayout();
            ml.OnSidebarChange(d);

        }
    }...



NAVMENU

@inject NavigationManager navigationManager
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using G2_Field.Shared


<TelerikDrawer @ref="@Drawer" Data="@items" MiniMode="false" Mode="@DrawerMode.Overlay" TItem="DrawerItem" SelectedItemChanged="@OnItemSelect" @bind-Expanded="@Expanded">
    <Template Context="AuthDrawerContext">
        <div class="k-drawer-items" role="menubar" aria-orientation="vertical">
            <ul>
                @foreach (var item in AuthDrawerContext)
                {
                    var selectedClass = item == SelectedItem ? "k-state-selected" : string.Empty;
                    <li @onclick="@(() => OnItemSelect(item))" class="k-drawer-item @selectedClass">
                        <div class="k-level-@(item.Level)">
                            <TelerikIcon Icon="@item.Icon"></TelerikIcon>
                            <span class="k-item-text">@item.Text</span>
                        </div>

                        @if (item.Expanded && (item.Children?.Any() ?? false))
                        {
                            <span class="k-icon k-i-arrow-chevron-down" style="position:absolute; right:0; line-height: inherit; margin: 0 8px"></span>
                        }
                        else if (!item.Expanded && (item.Children?.Any() ?? false))
                        {
                            <span class="k-icon k-i-arrow-chevron-right" style="position:absolute; right:0; line-height: inherit; margin: 0 8px"></span>
                        }
                    </li>
                }

            </ul>
        </div>
    </Template>
</TelerikDrawer>


@code {
    [Parameter] public List<DrawerItem> items { get; set; }
    public TelerikDrawer<DrawerItem> Drawer { get; set; }
    public DrawerItem SelectedItem { get; set; }
    public bool Expanded { get; set; } = true;
    public List<DrawerItem> Data { get; set; } = new List<DrawerItem> { new DrawerItem { Text = "HOME", Icon = Telerik.Blazor.IconName.Window, Description = "Home", URL = "" } };

    public async Task ToggleDrawer() => await Drawer.ToggleAsync();

    protected override void OnInitialized()
    {
        items = Data;
        SelectedItem = items.First();
        StateHasChanged();
    }

    public void OnHover()
    {

    }

    public async Task RefreshMenu(List<DrawerItem> di)
    {
        items = di;
    }