Blazor: Implementing Client Side Search As You Type Using bind:event

Updated 12/1/2019 to work with Blazor 3.0+

tldr;

Use bind:event="oninput"instead of bind in order to get real feedback as you type.  bind only databinds during the onchange event which requires losing focus on the input whereas bind:event="oninput"databinds on every keystroke.  Note you will also have to add a bind="PropertyNameHere" as well. See line 3 below:


@page "/"
<input @bind="SearchTerm" @bind:event="oninput" />
<span class="text-muted ml-5">
Showing @FilteredToDos.Count out of @ToDoItems.Count
</span>
<h4 class="mt-4">To Do's</h4>
<ul>
@foreach (var toDo in FilteredToDos)
{
<li>@toDo.Name</li>
}
</ul>
@code {
// Initialize SearchTerm to "" to prevent null's
string SearchTerm { get; set; } = "";
// Imagine this was retrieved from an API, just hardcoding for demo purposes
List<ToDoItem> ToDoItems => new List<ToDoItem>
{
new ToDoItem { Name = "Garbage" },
new ToDoItem { Name = "Dishes" },
new ToDoItem { Name = "Wash clothes" },
new ToDoItem { Name = "Water flowers" }
};
List<ToDoItem> FilteredToDos => ToDoItems.Where(i => i.Name.ToLower().Contains(SearchTerm.ToLower())).ToList();
}

view raw

ToDoList.cshtml

hosted with ❤ by GitHub

 

What is Blazor?

Blazor is an experimental SPA framework, built by Microsoft, running on top of WebAssembly that lets you write C# that runs client-side in a browser.  This allows you to share logic between your server-side C# code and your client-side C# code (such as models, validation, etc.).  Blazor limits (if not entirely eliminates) your need to write JavaScript to make a SPA.  You can learn more by visiting https://blazor.net/.

 

Scenario: Searching a List Client-Side

I want to build a Search feature that allows the user to search an unordered list (<ul>), purely client-side.  I don’t want to go back to the server to search, because in my scenario my data results are fairly small, don’t update often, and I want the response time to the user to be fast and avoid a round-trip.  I was looking around and didn’t see much on the Interwebs showcasing this, which is why I wrote this post.

Note: This scenario could also be tweaked to search through a table (when pulling in a grid component is overkill), searching FAQs in an accordion, or really anything that needs a client-side search, it doesn’t have to be a list.  The same concept applies.

Let’s build a simple component that loops over a list of ToDoItems and displays them in a list.  You could imagine this could be used for a ToDo app, but I’m going to remove all the typical ToDo functionality (like checking off tasks as completed) since it will just add noise to this example.

It’ll look like this on the front-end:

ToDo

 

And the component that powers it looks like this:


@page "/"
<input bind="@SearchTerm" />
<span class="text-muted ml-5">
Showing @FilteredToDos.Count out of @ToDoItems.Count
</span>
<h4 class="mt-4">To Do's</h4>
<ul>
@foreach (var toDo in FilteredToDos)
{
<li>@toDo.Name</li>
}
</ul>
@functions {
// Initialize SearchTerm to "" to prevent null's
string SearchTerm { get; set; } = "";
// Imagine this was retrieved from an API, just hardcoding for demo purposes
List<ToDoItem> ToDoItems => new List<ToDoItem>
{
new ToDoItem { Name = "Garbage" },
new ToDoItem { Name = "Dishes" },
new ToDoItem { Name = "Wash clothes" },
new ToDoItem { Name = "Water flowers" }
};
List<ToDoItem> FilteredToDos => ToDoItems.Where(i => i.Name.ToLower().Contains(SearchTerm.ToLower())).ToList();
}

view raw

ToDoList.cshtml

hosted with ❤ by GitHub

 

On line 3 we are taking an input and using the bind attribute to databind the input’s value to the SearchTerm property.  That SearchTerm property is then used on line 30 to search through our ToDoItems and return any matches in the FilteredToDos property.  Our list loops over the FilteredToDos and displays them out.  Let’s take a look how this works in real life with this gif:

Blazor Bind

 

What’s the problem?  bind fires with onchange

By default, Blazor’s default bind fires on the onchange event.  The onchange event doesn’t fire until the input element loses focus.  This means when a user types in the Search input they need to lose focus on the input before the Search takes place.  While this is okay UX-wise, it’s not exactly ideal.  My ideal world would be to have it fire while the user is typing so they can get real-time feedback without having to do any extra work (like clicking/tabbing out of the input or clicking a search button).

 

Solution: Use bind:event=”oninput” instead of bind

A little known feature about Blazor’s data binding is that bind has options that let you bind to different events, not just onchange.  For our scenario, we want to databind to the oninput event which fires immediately every time the value has changed.  We can do this by adding bind:event="oninput" on line 3 here:


@page "/"
<input @bind="SearchTerm" @bind:event="oninput" />
<span class="text-muted ml-5">
Showing @FilteredToDos.Count out of @ToDoItems.Count
</span>
<h4 class="mt-4">To Do's</h4>
<ul>
@foreach (var toDo in FilteredToDos)
{
<li>@toDo.Name</li>
}
</ul>
@code {
// Initialize SearchTerm to "" to prevent null's
string SearchTerm { get; set; } = "";
// Imagine this was retrieved from an API, just hardcoding for demo purposes
List<ToDoItem> ToDoItems => new List<ToDoItem>
{
new ToDoItem { Name = "Garbage" },
new ToDoItem { Name = "Dishes" },
new ToDoItem { Name = "Wash clothes" },
new ToDoItem { Name = "Water flowers" }
};
List<ToDoItem> FilteredToDos => ToDoItems.Where(i => i.Name.ToLower().Contains(SearchTerm.ToLower())).ToList();
}

view raw

ToDoList.cshtml

hosted with ❤ by GitHub

 

Now when we run our app, we get a much better UX:

Blazor Bind Value OnInput

 

Hope this helps someone else in a similar scenario!

10 thoughts on “Blazor: Implementing Client Side Search As You Type Using bind:event

  1. I’ve tried this – and the events never fire and the list stays the same. Not sure where to go next?

  2. In preview6 of Blazor: Specifying event handlers in bind attributes are no longer supported. Specify it using the bind:event=… attribute instead. Your article pointed me to the right direction though, thanks. Following works in preview6:

  3. If you need a “typeahead” over dynamic data loading from some lookup data source – you will need to handle filtering on setting value for SearchTerm. So you will either need to call some SearchBy(SearchTerm) method from set { … } (which looks weird to me), or use input binding (available since version 7) like this:

    where OnSearchTermInput is a handler (can be async by the way) loading data based on current SearchTerm value.

    Docs: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-7.0#use-bindgetbindset-modifiers-and-avoid-event-handlers-for-two-way-data-binding

Leave a Reply