I'm using Blazor WASM on .NET6. I'm trying to filter names of links on a mega menu.
I have my reactive filter which is a html input, and based on the text entered in the input, if any text is matching a link name, the matching part of the link name is highlighted in red. It is working properly, except that it requires a mouse click (anywhere) or hitting the enter key after the text is entered in the filter to update the UI with the highlighted text. I tried raising the "onkeypress" event on the input, that calls the "RefreshUI" method that invokes StateHasChanged() asyncronously (I will provide a small sample below).
I put the classes in the razor file so that you can see a working sample (I do put them in separate class files). GetMenuData() was altered here to provide a smaller, bogus set of data. The real one just consumes JSON from a web api and converts it into the object.
I verified that the event gets raised and StateHasChanged() is called, but I'm not understanding why the DOM is not updating. Any help would be greatly appreciated.
@page "/counter"
@using System.Text.RegularExpressions
<PageTitle>Counter</PageTitle>
<ul>
<li id="filter" class="show">
<div class="form-group fg--search">
<input type="search" class="input" placeholder="Search for Menu Items..." @bind="filterTerm" @onkeypress="RefreshUI">
</div>
</li>
<li>
<a href="#">Business Metrics</a>
<ul class="business-metrics flexbox">
<li class="col3">
@if (menuItems != null)
{
foreach (SubMenu sm in menuItems.Where(x => x.MenuText == "Business Metrics").Select(x => x.Submenus).FirstOrDefault())
{
<div class="menu-div">
<ul>
<li><h6><a href="#">@sm.SubmenuText</a></h6></li>
@foreach (IntranetMenuData imd in sm.SubmenuLinks)
{
<li><a href="imd.LinkUrl">@((MarkupString)FilterMenu(imd.LinkTitle))</a></li>
}
</ul>
</div>
}
}
</li>
</ul>
</li>
</ul>
@code {
private string filterTerm;
private List<TopMenuHeading> menuItems;
protected override void OnInitialized()
{
GetMenuData();
base.OnInitialized();
}
private string FilterMenu(string link)
{
if (!string.IsNullOrWhiteSpace(link) && !string.IsNullOrWhiteSpace(filterTerm))
{
Match matchedText = Regex.Match(link, @"(?i:" + filterTerm + @")");
if (matchedText.Success)
{
return link.Replace(matchedText.Value, "<span style=\"color: #e8112d;text-decoration: underline;font-weight: 600;\">" + matchedText.Value + "</span>", StringComparison.InvariantCultureIgnoreCase);
}
else
{
return link;
}
}
else
{
return link;
}
}
private void GetMenuData()
{
IntranetMenuData imd1 = new IntranetMenuData();
IntranetMenuData imd2 = new IntranetMenuData();
List<IntranetMenuData> mItems = new List<IntranetMenuData>();
SubMenu sub1 = new SubMenu();
List<SubMenu> subMenus = new List<SubMenu>();
TopMenuHeading tmh = new TopMenuHeading();
List<TopMenuHeading> topMenuHeadings = new List<TopMenuHeading>();
imd1.LinkTitle = "Capacity Utilization";
imd2.LinkTitle = "Relieved Hours";
mItems.Add(imd1);
mItems.Add(imd2);
sub1.SubmenuText = "Capacity";
sub1.SubmenuLinks = mItems;
subMenus.Add(sub1);
tmh.MenuText = "Business Metrics";
tmh.Submenus = subMenus;
topMenuHeadings.Add(tmh);
menuItems = topMenuHeadings;
}
private async Task RefreshUI()
{
await InvokeAsync(() => StateHasChanged());
}
public class IntranetBookmarks
{
public long EmployeeNumber { get; set; }
public string Submenu { get; set; }
public string Text { get; set; }
public string Url { get;set; }
}
public class IntranetMenuData
{
public long LinkMenuId { get; set; }
public string LinkOnClick { get; set; }
public string LinkTitle { get; set; }
public string LinkUrl { get; set; }
}
public class SubMenu
{
public List<IntranetMenuData> SubmenuLinks { get; set; }
public string SubmenuText { get; set; }
public SubMenu()
{
SubmenuLinks = new List<IntranetMenuData>();
}
}
public class TopMenuHeading
{
public string MenuText { get; set; }
public List<SubMenu> Submenus { get; set; }
public TopMenuHeading()
{
Submenus = new List<SubMenu>();
}
}
}