Issue
In our previous ASP.NET MVC projects we used to rely on Ajax and jQuery to autocomplete values being inputted in to the form (such as getting a list of staff names to choose from whilst still typing) – pure JavaScript example of this.
I’m now creating a new project from scratch in Blazor Server model (.NET 6), and this last bit of work just doesn’t seem to be achievable in an elegant way. Or should I say, it works well for a single field, but the trouble comes the approach should be used for multiple fields on the same page.
So the logic is following:
- Make
<InputText>
control hidden and bound the value to the value in the EF data model. - Add
<input>
control with uniqueid
and@ref
tags and make it call C# autocomplete method - Once the input field is clicked, the C# autocomplete method then calls a JavaScript function using
IJSObjectReference
, that deals with the autocomplete logic (provides suggested values based on what’s been typed in so far) - Once user chooses one of the suggested values (i.e. user name), JavaScript is then returning user ID via
JSInvokable
method in C# - The
JSInvokable
C# method then populates the relevant user ID value in the data model, and so this value is then going to be updated in both UI and database via EF.
The approach itself is the same as in the official Blazor documentation and it works, like I said.
However, my issue is making it work in some elegant way for multiple input fields on the same form. Is there even an elegant way to achieve this, if I just want to run a pure JavaScript and not jQuery or Ajax?
The biggest question is how do I make C# side of things to decide which input field relates to which field in the data model, whilst making JavaScript calls in between? I would like to avoid relying on hardcoded strings and naming conventions as well as (ideally) I would like to avoid creating multiple ElementReference
s and JSInvokable
methods essentially doing the same job.
Or perhaps my choice of pure JavaScript for this kind of task within Blazor project is essentially wrong and there are much better alternatives?
Here’s some code excerpts (omitting non-essential parts), that hopefully will make it easier to understand.
HTML:
<div class="form-group">
<label>User</label>
<div class="autocomplete">
<InputText class="form-control" type="hidden" @bind-Value="data.UserId" />
<input id="@string.Format("{0}{1}", "userAutocomplete", autocompleteId)"
type="text"
name="@string.Format("{0}{1}", "userAutocomplete", autocompleteId)"
@onclick="AutocompleteUsers"
@ref="userAutocomplete" />
</div>
</div>
C#:
public Data? data { get; set; }
private ElementReference userAutocomplete;
private DotNetObjectReference<RazorPage>? dotNetHelper;
private IJSObjectReference? autocompletelJSObjectReference;
private string autocompleteId = $"_{Guid.NewGuid().ToString().Replace("-", "")}";
private async Task AutocompleteUsers()
{
try
{
dotNetHelper = DotNetObjectReference.Create(this);
autocompletelJSObjectReference = await JS.InvokeAsync<IJSObjectReference>("import", "./js/autocomplete.js");
await autocompletelJSObjectReference.InvokeVoidAsync("Autocomplete", dotNetHelper, userAutocomplete, userListJSON, autocompleteId);
}
}
[JSInvokable]
public async Task UpdateAutocomplete(string _userId, string _autocompleteId)
{
data.UserId = _userId;
}
JavaScript:
export function Autocomplete(dotNetHelper, inp, userList, autocompleteId)
{
inp.addEventListener("input", function (e)
{
var userListJSON = JSON.parse(userList);
for (i = 0; i < userList.length; i++)
{
var dict = userListJSON[i];
b.addEventListener("click", function (e)
{
inp.value = this.getElementsByTagName("input")[0].value;
dotNetHelper.invokeMethodAsync("UpdateAutocomplete", inp.value, autocompleteId);
});
}
});
}
Solution
With Blazor
you need to rely less on JavaScript
. Often functionality such as Autocomplete
have already been implemented in Blazor free UI frameworks. I recommend you choose one as it will save you time and energy.
One of amazing free frameworks that I recommend is MudBlazor
. Below is an example of how to implement Autocomplete
with this framework such as the one shown in w3schools example you provided.
First you need to have a list of options, this can be a List
or Enum
Enum for Countries:
You can get this list from https://gist.github.com/jplwood/4f77b55cfedf2820dce0dfcd3ee0c3ea and change attribute to Display(Name = "country")
.
public enum Countries
{
[Display(Name = "Afghanistan")] AF = 1,
[Display(Name = "Ă…land Islands")] AX = 2,
[Display(Name = "Albania")] AL = 3,
[Display(Name = "Algeria")] DZ = 4,
[Display(Name = "American Samoa")] AS = 5,
[Display(Name = "Andorra")] AD = 6,
// ...
}
Create Enum extension to get country names:
public static class EnumExtensions
{
public static string GetCountryName(this Countries country)
{
return (country == 0) ? String.Empty :
country.GetType()
.GetMember(country.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
}
Page with Autocomplete Field:
@page "/"
@using MudBlazorTemplates2.Enums
@using Microsoft.AspNetCore.Components
@using MudBlazorTemplates2.Extensions
<PageTitle>Index</PageTitle>
<MudForm Model="@location">
<MudAutocomplete T="Countries"
Label="Country"
@bind-Value="location.Country"
For="@(() => location.Country)"
Variant="@Variant.Outlined"
SearchFunc="@Search"
ResetValueOnEmptyText="true"
CoerceText="true" CoerceValue="true"
AdornmentIcon="@Icons.Material.Filled.Search"
AdornmentColor="Color.Secondary"
ToStringFunc="@(c => c.GetCountryName())"/>
</MudForm>
<br/>
<p>You selected :@location.Country.GetCountryName()</p>
@code {
private Location location = new Location();
private async Task<IEnumerable<Countries>> Search(string value)
{
var countries = new List<Countries>();
foreach (Countries country in Enum.GetValues(typeof(Countries)))
countries.Add(country);
if (string.IsNullOrEmpty(value))
return null;
// customize if needed
return countries.Where(c => c.GetCountryName()
.StartsWith(value, StringComparison.InvariantCultureIgnoreCase));
}
public class Location
{
public Countries Country { get; set; }
}
}
You can find more simpler examples for Autocomplete
at https://mudblazor.com/components/autocomplete#usage
Answered By – Ibrahim
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0