Declined
Last Updated: 15 Sep 2023 13:02 by ADMIN
Will
Created on: 11 Sep 2023 21:40
Category: UI for Blazor
Type: Feature Request
0
Treeview FocusOut

Hi!

Would it be possible to expose a focus in/out (or similar) functionality for the Treeview? We currently use the component for eyebrow navigation menus, and it would be nice to have the ability to close trees when the user clicks somewhere else. We've tried doing an onclick for the body of the site, which works but also interferes with other clicks on the page.  Thanks!

1 comment
ADMIN
Hristian Stefanov
Posted on: 15 Sep 2023 13:02

Hi Will,

I confirm that attaining the desired functionality is currently possible by implementing a small JavaScript function, which binds a 'focusout' event handler to the TreeView HTML element.

To demonstrate, I've prepared an illustrative example:

@inject IJSRuntime js

<style>
    .my-treeview {
        width: 40%;
    }
</style>

<TelerikTreeView Data="@FlatData" Class="my-treeview"
                 @bind-ExpandedItems="@ExpandedItems"
                 @bind-SelectedItems="@SelectedItems"
                 SelectionMode="@TreeViewSelectionMode.Single" />

<script suppress-error="BL9992">
    var dotNet;

    function attachTreeViewBlur(dotNetReference) {
        var treeView = document.querySelector(".k-treeview");
        if (treeView) {
            treeView.addEventListener("focusout", onTreeViewBlur);
        }
        dotNet = dotNetReference;
    }

    function onTreeViewBlur(e) {
        setTimeout(function () {
            if (!document.activeElement || !document.activeElement.closest(".k-treeview")) {
                dotNet.invokeMethodAsync("CollapseAll");
            }
        }, 1);
    }
</script>

@code {
    public IEnumerable<TreeItem> FlatData { get; set; }
    public IEnumerable<object> ExpandedItems { get; set; } = new List<TreeItem>();
    public IEnumerable<object> SelectedItems { get; set; } = new List<TreeItem>();
    Random rnd { get; set; } = new Random();

    private DotNetObjectReference<Index>? DotNetRef;

    [JSInvokable("CollapseAll")]
    public async Task CollapseAll()
    {
        ExpandedItems = new List<TreeItem>();
        SelectedItems = new List<TreeItem>();
        StateHasChanged();
    }

    public void Dispose()
    {
        DotNetRef?.Dispose();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await Task.Delay(1);
            await js.InvokeVoidAsync("attachTreeViewBlur", DotNetRef);
        }
        await base.OnAfterRenderAsync(firstRender);
    }

    protected override void OnInitialized()
    {
        FlatData = LoadFlat();
        ExpandedItems = FlatData.Where(x => x.HasChildren == true);
        DotNetRef = DotNetObjectReference.Create(this);
    }

    int TreeLevels { get; set; } = 3;
    int ItemsPerLevel { get; set; } = 3;
    int IdCounter { get; set; } = 1;
    List<TreeItem> LoadFlat()
    {
        List<TreeItem> items = new List<TreeItem>();
        PopulateTreeItems(items, null, 1);
        return items;
    }
    void PopulateTreeItems(List<TreeItem> items, int? parentId, int level)
    {
        for (int i = 1; i <= ItemsPerLevel; i++)
        {
            var itemId = IdCounter++;
            items.Add(new TreeItem()
                {
                    Id = itemId,
                    Text = $"Level {level} Item {i} Id {itemId}",
                    ParentId = parentId,
                    HasChildren = level < TreeLevels
                });
            if (level < TreeLevels)
            {
                PopulateTreeItems(items, itemId, level + 1);
            }
        }
    }
    public class TreeItem
    {
        public int Id { get; set; }
        public string Text { get; set; }
        public int? ParentId { get; set; }
        public bool HasChildren { get; set; }
    }
}

I encourage you to run and test it to see whether the results align with your specific requirements.

As of the current stage, since a viable approach is available, I am marking this item as 'Declined.' Nevertheless, I want to assure you that we will continue monitoring our customers' needs and will gladly reconsider implementing such a built-in functionality in the future, should there be sufficient demand.

Regards,
Hristian Stefanov
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Telerik family, check out our getting started resources!