Currently, on mobile devices (where is no hover), to open the child menu you need to click/tap the parent and the only way close it afterwards is if you click away. This is not very convenient for mobile usage. I want to be able to close the child menu on click/tap of the parent as well.
It is a very common occurrence to need to open a menu link in a new tab. Currently the prescribed way to do this is to create a Template for the Menu Items. This involves a lot of manual implementation (verbose template code, helper methods, changing the menu item object to not use the Url property so as to override the default UrlField behavior). This is a lot of extra work to accomplish a very common and simple task.
I propose that a new property be introduced to the Menu component (to be added to Menu Items) - a boolean field that defines whether or not to open the link in a new tab (i.e. "NewTab", or "External", or something of the like). It could default to false so that, in most cases, it could be ignored. But if set to true, the Menu component would handle adding "target='_blank'" and "rel='noopener noreferer'" to the link, while leaving all of the other functionality and styling in place.
It would greatly simplify the usage. And I would suggest that every programming who is building navigation menus would have a case where it's needed.
I would think, though I haven't looked at the core code yet, that this would be a relatively simple feature to add.
public class MenuItem
{
public string Text { get; set; }
public ISvgIcon? Icon { get; set; }
public string Url { get; set; } = string.Empty;
public bool NewTab { get; set; } = false;
public List<MenuItem>? Items { get; set; }
public MenuItem(string text, ISvgIcon? icon, string url, bool newTab, List<MenuItem>? items)
{
Text = text;
Icon = icon;
Url= url;
NewTab = newTab;
Items = items;
}
}I'm using the Menu component and I am handling the OnClick event. I noticed that when an exception is thrown in its handler, it does not reach the ErrorBoundary.
===
ADMIN EDIT
===
This issue also affects the SplitButton component.
Currently, if a sub-menu is opened and the user moves the mouse back to a different parent item, the whole menu is closed.
Video: https://app.screencast.com/rcdHp9oklAU4z
Reproduction: https://blazorrepl.telerik.com/mxuClvaB36CB47v425
===
ADMIN EDIT
===
This bug also affects the ContextMenu component.
Implementation of one or both of these features:
- Screen boundary detection: The list of child items expands to the opposite direction when necessary to prevent screen boundaries from being crossed.
- ExpandDirection: Gets or sets the direction in which child items will open.
While the menu is usable the way it is, it could be better. If you take a look at the example menu in the best practices link that I provided in the original post, you will see what I mean. Some of the differences include the following:
Best practices example: https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html
The current implementation of the component has hardcoded values for the alignment of the popup - e.g.:
for horizontal orientation: left horizontal align and bottom vertical align
for vertical orientation: right horizontal align and top vertical align
Ideally, those should be customizable to facilitate various use-cases.
Hello,
If I disable a Menu item at runtime, it prohibits access to child items via the mouse, but still opens the child group of items if I use the keyboard navigation.
Here is a test page with a workaround included (which is to recreate the Menu).
<TelerikButton OnClick="@DisableItem">Disable Services item</TelerikButton>
<TelerikButton OnClick="@EnableItem">Enable Services item</TelerikButton>
@if (ShowMenu)
{
<TelerikMenu Data="@MenuItems" />
}
@code {
List<MenuItem> MenuItems { get; set; }
bool ShowMenu { get; set; } = true;
async Task DisableItem()
{
MenuItems.Find(x => x.Text == "Services").Disabled = true;
MenuItems = new List<MenuItem>(MenuItems);
// workaround start
ShowMenu = false;
await Task.Delay(1);
ShowMenu = true;
// workaround end
}
async Task EnableItem()
{
MenuItems.Find(x => x.Text == "Services").Disabled = false;
MenuItems = new List<MenuItem>(MenuItems);
}
protected override void OnInitialized()
{
MenuItems = new List<MenuItem>()
{
new MenuItem()
{
Text = "Company",
Items = new List<MenuItem>()
{
new MenuItem()
{
Text = "Overview"
},
new MenuItem()
{
Text = "Events"
}
}
},
new MenuItem()
{
Text = "Services",
Items = new List<MenuItem>()
{
new MenuItem()
{
Text = "Consulting"
},
new MenuItem()
{
Text = "Education"
}
}
}
};
base.OnInitialized();
}
public class MenuItem
{
public string Text { get; set; }
public bool Disabled { get; set; }
public List<MenuItem> Items { get; set; }
}
}
Dear,
the blazor menu UI adapt it self for hamburguer menu when mobile?
best,
Jeff
---
ADMIN EDIT
I have attached to this post a sample implementation that you can use as base. Another option is to add more conditional markup to remove the menu altogether and replace it with another structure for small screens. A third option is to use the Drawer component as it collapses anyway (see here and here).
----
Is there a way to not create menu programmatically but only using "html" elements ? Something like this :
<TelerikMenu>
<TelerikMenuItem Text="File">
<TelerikMenuItem Text="Open" OnClick="@OnFileOpenMenuClick"/>
<TelerikMenuItem Text="Save" OnClick="@OnFileSaveMenuClick"/>
</TelerikMenuItem>
</TelerikMenu>
At the moment, when you click a menu item it does not hide.
A method can be exposed to hide the expanded items that can be invoked from the OnClick handler.
---
ADMIN EDIT
Here is a potential workaround - when the menu item is clicked, we use a little bit of JS to go over the menu items at the root and make the browser think that the user moved the mouse away from them which is the signal for the menu dropdowns to hide. Do test this carefully before using in production, though.
@inject IJSRuntime _js
@* Move this script together with other scripts in the project, it is here to make the snippet shorter *@
<script suppress-error="BL9992">
function closeMenu() {
setTimeout(function () {
var mouseLeaveEvent = new Event('mouseleave');
var rootNodes = document.querySelectorAll("li.k-menu-item");
rootNodes.forEach(function (elem) { elem.dispatchEvent(mouseLeaveEvent); })
}, 30);
}
</script>
<TelerikMenu Data="@MenuItems"
ItemsField="@nameof(MenuItem.SubSectionList)"
TextField="@nameof(MenuItem.Section)"
UrlField="@nameof(MenuItem.Page)"
OnClick="@((MenuItem item) => OnClickHandler(item))">
</TelerikMenu>
@code {
public List<MenuItem> MenuItems { get; set; }
async Task OnClickHandler(MenuItem item)
{
await _js.InvokeVoidAsync("closeMenu");
}
public class MenuItem
{
public string Section { get; set; }
public string Page { get; set; }
public List<MenuItem> SubSectionList { get; set; }
}
protected override void OnInitialized()
{
MenuItems = new List<MenuItem>()
{
new MenuItem()
{
Section = "fetchdata",
Page = "fetchdata"
},
new MenuItem()
{
Section = "counter",
Page = "counter"
},
// sample URLs for SPA navigation
new MenuItem()
{
Section = "Company",
SubSectionList = new List<MenuItem>()
{
new MenuItem()
{
Section = "Overview",
Page = "fetchdata"
},
new MenuItem()
{
Section = "Events",
Page = "fetchdata"
},
new MenuItem()
{
Section = "Careers",
Page = "counter"
}
}
},
// sample URLs for external navigation
new MenuItem()
{
Section = "Services",
SubSectionList = new List<MenuItem>()
{
new MenuItem()
{
Section = "Consulting",
Page = "counter"
},
new MenuItem()
{
Section = "Education",
Page = "fetchdata"
}
}
},
new MenuItem()
{
Section = "Contact",
Page = "counter"
}
};
base.OnInitialized();
}
}
---