Home > Mobile >  How to display selected item in the input tag of a custom Blazor dropdown-typeahead component
How to display selected item in the input tag of a custom Blazor dropdown-typeahead component

Time:03-17

I'm trying to build a custom dropdown-typeahead component in Blazor. I want it to be as following: 

Instead of the classic <select> tag which on click displays a list, i want it to have an <input> tag where the user can write something and search the list. On each character they write, the dropdown <div> opens below with results depending on the search text. The user can select any of the displaying results of the list. So far so good. 

The problem I'm dealing with is that I cannot display the selected item inside the <input> tag. My code is the following:

Parent Component

...
<div  @onfocusout="ClearHstrCommonSearchTextHandler">
   <label >Location</label>
   <TypeaheadDropdownComponent OnEmptySearchText="ClearHstrCommonSearchTextHandler"
                               SearchMethod="SearchHstrCommon" />
   
   @if (showDropdownResults && HstrCommonDisplayed.Count > 0)
   {
      <div >
         @foreach (HstrCommonDTO hstr in HstrCommonDisplayed)
         {
            <div  @onclick="() => { locationHstrId = hstr.HstrId; showDropdownResults = false; }">
               @hstr.Title <br />
            </div>
         }
      </div>
   }
</div>
...

@code {
   [Parameter] public List<HstrCommonDTO> HstrCommon { get; set; }
   
   private int locationHstrId = -1;
   private bool showDropdownResults = false;
   private List<HstrCommonDTO> HstrCommonDisplayed = new();

   ...

   private void SearchHstrCommon(string searchText)
   {
      searchText = searchText.RemoveDiacritics()
                             .Trim()
                             .ToUpper();
      showDropdownResults = true;
      HstrCommonDisplayed = HstrCommon.FindAll(x => x.Title.Contains(searchText, StringComparison.OrdinalIgnoreCase)).ToList();
   }

   private void ClearHstrCommonSearchTextHandler()
   {
      showDropdownResults = false;
      HstrCommonDisplayed = new();
      locationHstrId = -1;
   }
}

TypeaheadDropdownComponent

<div style="position: relative">
    <input 
           type="text"
           autocomplete="off"
           placeholder="@Placeholder"
           @bind-value="@SearchText"
           @bind-value:event="oninput" />
</div>

@code {
    [Parameter] public string Placeholder { get; set; } = "Search";
    [Parameter] public int MinimumLength { get; set; } = 1;
    [Parameter] public int Debounce { get; set; } = 200;
    [Parameter] public EventCallback<string> SearchMethod { get; set; }
    [Parameter] public EventCallback OnEmptySearchText { get; set; }

    private Timer _debounceTimer;
    private bool _isSearching = false;
    private string _searchText = string.Empty;

    private string SearchText
    {
        get => _searchText;
        set
        {
            _searchText = value;
            if (value.Length == 0)
            {
                _debounceTimer.Stop();
                OnEmptySearchText.InvokeAsync();
            }
            else if (value.Length >= MinimumLength)
            {
                _debounceTimer.Stop();
                _debounceTimer.Start();
            }
        }
    }

    protected override void OnInitialized()
    {
        _debounceTimer = new Timer();
        _debounceTimer.Interval = Debounce;
        _debounceTimer.AutoReset = false;
        _debounceTimer.Elapsed  = Search;

        base.OnInitialized();
    }

    protected async void Search(Object source, ElapsedEventArgs e)
    {
        _isSearching = true;
        await InvokeAsync(StateHasChanged);
        await SearchMethod.InvokeAsync(_searchText);
        _isSearching = false;
        await InvokeAsync(StateHasChanged);
    }
}

With my code, I take the required value (locationHstrId) as expected, but in the <input> tag of the TypeaheadDropdownComponent, the text which the user wrote for searching is displayed.

I cannot figure out what I have to add to my code in order the selected word of the dropdown list to be displayed in the input tag.

Thanks in advance for your time!

CodePudding user response:

Even though the design of the component is 'strange' (I don't understand why the results are not in the same component?), I will provide you with a solution to your problem.

Add a public function to your TypeaheadDropdownComponent that sets the search value.

public void SetSearchValue(string value){
 _searchText = value;
 StateHasChanged();
}

Now, in the parent component, get the dropdown component's reference and call the SetSearchValue when selecting an item.



 ...
<div  @onfocusout="ClearHstrCommonSearchTextHandler">
   <label >Location</label>
   <TypeaheadDropdownComponent @ref="TypeaheadRef" OnEmptySearchText="ClearHstrCommonSearchTextHandler"
                               SearchMethod="SearchHstrCommon" />
   
   @if (showDropdownResults && HstrCommonDisplayed.Count > 0)
   {
      <div >
         @foreach (HstrCommonDTO hstr in HstrCommonDisplayed)
         {
            <div  @onclick="() => OnItemSelected(hstr)">
               @hstr.Title <br />
            </div>
         }
      </div>
   }
</div>
...
@code{
 TypeheadDropdownComponent TypeaheadRef;


 public void OnItemSelected(HstrCommonDTO hstr){
  locationHstrId = hstr.HstrId; 
  showDropdownResults = false; 
  TypeaheadRef.SetSearchValue("<your value>");
 }
}

CodePudding user response:

A Possible solution is using Mud Blazor (component library). The autocomplete field solve your problem. And it's easy to use (also with sample codes to see how to implement it), if you dont want to use a component library, Bodgan B code seems to work

For more info mud blazor link here.

  • Related