Is it possible to create a Virtualized MudAutoComplete MultiColumn search. See screenshot below. I was able to implement this in Telerik and would like to recreate this in MudBlazor. Note, the binding is on a hidden field ID and the combo box is Virtualized. If Virtualization is not possible the Search can begin after 3 characters of entry.
Below is how I implemented it in Telerik. Perhaps that will clarify what I am looking for.
@page "/GenSingle"
@inject SerOHRDatabase serOHRDatabase
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<div>Generator Name Search:</div>
<TelerikMultiColumnComboBox TItem="ModtblGenerator"
OnRead="@ReadItems"
TValue="int"
ValueField="@nameof(ModtblGenerator.Id)"
TextField="@nameof(ModtblGenerator.GenName)"
Filterable="true"
@bind-Value="@intSelectedGenID"
ItemHeight="28"
ListHeight="520px"
PageSize="25"
ScrollMode="@DropDownScrollMode.Virtual"
Width= "250px"
OnChange="@GetSelectedGeneratorRecord"
>
<MultiColumnComboBoxColumns>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.GenName)"
Title="Gen Name"
HeaderClass="header"
Class="genNameCell"
Width="250px"></MultiColumnComboBoxColumn>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.GenNum)"
Title="Gen Num"
HeaderClass="header"
Width="150px"></MultiColumnComboBoxColumn>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.Street)"
Title="Street"
HeaderClass="header"
Width="200px"></MultiColumnComboBoxColumn>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.City)"
Title="City"
HeaderClass="header"
Width="150px"></MultiColumnComboBoxColumn>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.Province)"
Title="Province"
HeaderClass="header"
Width="150px"></MultiColumnComboBoxColumn>
<MultiColumnComboBoxColumn Field="@nameof(ModtblGenerator.PostalCode)"
Title="Postal Code"
HeaderClass="header"
Width="150px"></MultiColumnComboBoxColumn>
</MultiColumnComboBoxColumns>
</TelerikMultiColumnComboBox>
<br />
<br />
@if (selectedGenerator != null)
{
<ComGenerator Generator="@selectedGenerator" />
}
@code {
List<ModtblGenerator> lstGenerators;
int intSelectedGenID;
ModtblGenerator selectedGenerator;
private void GetSelectedGeneratorRecord()
{
selectedGenerator = lstGenerators.Find(x => x.Id == intSelectedGenID);
}
//**************************************** Combo Events
protected async Task ReadItems(MultiColumnComboBoxReadEventArgs args)
{
await LoadData();
var result = lstGenerators.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
}
private async Task LoadData()
{
if (lstGenerators == null)
{
lstGenerators = await serOHRDatabase.GetAllGenerators();
}
}
}
<style>
.header {
font-weight: bold;
color: black;
}
.genNameCell {
color: darkblue;
font-weight: bolder;
}
</style>
I don't think the Task.Delay is a good user experience so I spent some time finding a way around it. Here I use a javascript function to detect if there's a click on page outside of the popover to close it. I've also added a clear mechanism. Script shouldn't really be in the markup, but shown here for brevity.
@inject IJSRuntime jsRuntime
<script>
window.registerOutsideClickHandler = (dotNetObj, popoverId) => {
document.addEventListener('click', (event) => {
const popoverElement = document.getElementById(popoverId);
if (popoverElement && !popoverElement.contains(event.target)) {
dotNetObj.invokeMethodAsync('ClosePopover');
}
});
};
</script>
<MudContainer Class="mt-8">
<MudGrid>
<MudItem xs="12">
<div id="popoverWrapper" @onclick="HasFocus">
<MudTextField @bind-Value="SearchFor" Label="Search" Immediate="true"
Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Clear" OnAdornmentClick="@ClearSearch"></MudTextField>
<MudPopover Open="@IsOpen"
AnchorOrigin="@Origin.BottomCenter" TransformOrigin="@Origin.TopCenter"
RelativeWidth="true" Fixed="true">
<MudTable T="Element" Items="@Data" Hover="true"
RowClass="cursor-pointer" OnRowClick="RowClickEvent" Filter="new Func<Element, bool>(FilterElements)">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Num</MudTh>
<MudTh>Street</MudTh>
<MudTh>City</MudTh>
<MudTh>Province</MudTh>
<MudTh>PostalCode</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Num">@context.Num</MudTd>
<MudTd DataLabel="Street">@context.Street</MudTd>
<MudTd DataLabel="City">@context.City</MudTd>
<MudTd DataLabel="Province">@context.Province</MudTd>
<MudTd DataLabel="Postal Code">@context.PostalCode</MudTd>
</RowTemplate>
</MudTable>
</MudPopover>
</div>
</MudItem>
<MudItem xs="12">
<MudField Label="Selected Value" Variant="Variant.Text">
@((SearchID ?? 0) > 0 ? SearchID : "Nothing Selected")
</MudField>
</MudItem>
</MudGrid>
</MudContainer>
@code {
private string SearchFor { get; set; } = string.Empty;
private int? SearchID { get; set; } = null;
private bool IsOpen { get; set; } = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Register the click listener to detect clicks outside the popover
await jsRuntime.InvokeVoidAsync("registerOutsideClickHandler", DotNetObjectReference.Create(this), "popoverWrapper");
}
}
[JSInvokable]
public void ClosePopover()
{
if (IsOpen)
{
IsOpen = false;
StateHasChanged();
}
}
private void HasFocus()
{
IsOpen = true;
}
private void ClearSearch()
{
SearchFor = string.Empty;
SearchID = null;
IsOpen = false;
}
private void RowClickEvent(TableRowClickEventArgs<Element> Row)
{
SearchID = Row.Item.ID;
SearchFor = Row.Item.Name;
IsOpen = false;
}
private bool FilterElements(Element Row) => FilterFunc(Row, SearchFor);
private bool FilterFunc(Element Row, string searchString)
{
if (string.IsNullOrWhiteSpace(searchString)) return true;
foreach (var prop in Row.GetType().GetProperties())
{
if (prop.GetValue(Row)?.ToString().Contains(searchString, StringComparison.OrdinalIgnoreCase) == true)
return true;
}
return false;
}
public class Element
{
public int ID { get; set; }
public string Name { get; set; }
public string Num { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string PostalCode { get; set; }
}
public List<Element> Data { get; set; } = new List<Element> {
new Element { ID = 1, Name = "John Doe", Num = "1234", Street = "Main St", City = "Springfield", Province = "IL", PostalCode = "62701" },
new Element { ID = 2, Name = "Jane Smith", Num = "5678", Street = "Maple Ave", City = "Greenfield", Province = "CA", PostalCode = "93927" },
new Element { ID = 3, Name = "Michael Brown", Num = "9101", Street = "Elm St", City = "Metropolis", Province = "NY", PostalCode = "10001" }
};
}