Blazor: Implementing Client Side Search As You Type Using bind-value-oninput

tldr;

Use bind-value-oninput instead of bind in order to get real feedback as you type.  bind only databinds during the onchange event which requires losing focus whereas bind-value-oninput databinds on every keystroke.  See line 3 below:

 

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:

 

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-value-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 swapping out bind="@SearchTerm" for bind-value-oninput="@SearchTerm" on line 3 here:

 

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!

Dynamically setting Content Type in ASP.NET Core with FileExtensionContentTypeProvider

tldr;

If you have a scenario where you have multiple file types (.pdf, .docx, etc.) stored somewhere (in a database, file system, etc.), that need to be downloaded, you can automatically figure out the Content Type by newing up a FileExtensionContentTypeProvider and call TryGetContentType to get the Content Type and pass that to the Fileresult helper.  See lines 8-16 below

 

What is a Content Type?

A Content Type is how the server tells the browser what type of file the resource being served is.  That way the browser knows how to render whether it’s HTML, CSS, JSON, PDF, etc.  The way the server does this is by passing a Content-Type HTTP Header.  This value of the header will look like “text/html” for HTML, “text/css” for CSS, “application/json” for JSON, “application/pdf” for PDF’s, and so on.  A complete list can be found on the IANA official docs.

Note: A Content Type can also be called a MIME type, but because the header is called Content-Type, and ASP.NET Core calls it the Content Type in the code I’m going to be showing, I’m going to call it Content Type for consistency throughout this post.

 

How do I set the Content Type in ASP.NET Core?

The good news is, for a vast majority of the static files you’re going to serve, the Static Files Middleware will set the Content Type for you.  For scenarios where you need to set the Content Type yourself, you can use the FileContentResult in your Controllers or PageModels via the File helper method used on line 11 below.

 

What happens if I set the wrong Content Type?

If you set the wrong Content Type, then you may cause issues for your application.  For example, the PDF rendered in the code above will render a PDF in the browser like this:

pdf returned

But what happens if I replace that “application/pdf” string with “application/json” to try to tell the browser the PDF is really JSON?  Well… let’s find out:

returned json

Well that’s not good.  So, setting the correct Content Type is pretty important.  (Also, yes I know I need to update Chrome…. don’t judge me.  I have a bunch of tabs open in another window that I’m totally going to look at some day, ok?)

 

Scenario

Let’s say you have a scenario where you allow admin users to upload files that allows some customer users to download those files.  Those admin users can uploads all sorts of file extensions such as a pdf, pptx, docx, xlsx, etc. that customers can then download themselves.  This means that you can’t assume and be sure what the Content Type should be, so you need to inspect the file extension to figure it out.  No big deal, there are a few ways to solve this, but the simplest is to just write a trusty ol’ switch statement like lines 11-25 below to handle every file type we allow.

The problem with that is trying to maintain a list of all those mappings yourself is annoying, and likely leads you to adding “just one more” when your users want to support another file type you didn’t previously have.  It’s also prone to typo’s, because some of these content types are ridiculously convoluted.

 

Solution

Luckily, ASP.NET Core already has maintained this list for us via the FileExtensionContentTypeProvider.  So all you have to do is new it up, and call TryGetContentType which acts like a lot of the TryX-out pattern you see sprinkled throughout .NET.  It returns a bool and an out variable with the content type.  Usage looks like lines 8-16 below:

 

So there you have it, now you don’t have to maintain some crazy list of Content Type mappings, you can just lean on ASP.NET Core which maintains those mappings for you.

 

Hope this helps.

Announcing: Blazor Snippets for VS Code

I wrote some Blazor Snippets for VS Code. Download it here!

 

Now that VS Code has Razor support built-in to the C# extension, I wanted to write some Blazor in VS Code.  I was surprised to not see any Blazor-specific snippets created so I created my first VS Code extension for the Blazor Snippets.

 

Examples

  • Using bc to create a blank Blazor Component, bpara to create a parameter, boi to create OnInitAsync, and binjhttpto inject an HttpClient.

Snippets used together

  • Using bchttp to scaffold out a Blazor Component with an HttpClient call.

Snippets used together

 

There are over 20 snippets total right now and the full list of snippets is documented out on the GitHub readme.

 

Closing

Log an issue on the GitHub repo if you want to see new snippets or if you see any issues.

 

Hope this helps!

Strongly Typing Dapper Queries Using nameof

tldr;

Use nameof in order to strongly type your Dapper queries, so renaming a property on your C# doesn’t break your Dapper query or require you to remember to go fix them.

 

Before Stringly Typed (note lines 12-14):

 

 

 

 

After Strongly Typed (note lines 12-14):

 

 

 

What is Dapper?

Dapper is a lightweight micro-ORM, made by the Stack Exchange team, that transforms your SQL into strongly typed objects.  No longer do you have to deal with DataTables, DataSets, DataReaders, and the like.  You can just deal with your objects like a Customer object with FirstName, LastName, and ZipCode properties.

Dapper is a great alternative to a more full-featured ORM like Entity Framework.  Sometimes, Entity Framework is too much for a query (because the LINQ is hard/impossible to write) or sometimes it’s just overkill.

Dapper is implemented as a set of extension methods hanging off of DbConnection and a simple example is below:

 

 

 

 

 

Dapper will open and close the connection for you, parameterize your SQL query, and then it will hydrate the Customer object’s FirstNameLastName, and ZipCode properties, because they match the SQL column names.

But let’s say you have SQL column names that quite don’t match your C# properties like F_NameL_Name, and Zip_Code.  No big deal, all you do is alias the column name to match the C# property like below (lines 12-14):

 

 

 

As you can see, it’s pretty simple to use and results in super clean code.  Dapper is also much faster than Entity Framework, because it’s closer to the metal, but you also give up some features compared to something like Entity Framework such as LINQ queries, Change Tracking, Query Filters, Migrations, etc.

It’s really common for the projects I work on to use Dapper and EF together depending on what the best tool is for the job.

 

Downsides of Dapper

However, there are of course downsides to Dapper compared to Entity Framework.  With Entity Framework, you can be almost 100% guaranteed that your query won’t blow up when you go to run it (short of a schema problem, but that would be an issue in any data access library).

With Dapper, it’s up to you to write the correct SQL in a string.  If you misspell a column name, the C# compiler won’t yell at you, and you won’t find out until you go to run it.  Same goes for all the normal SQL goof ups like inserting too many or too few commas, forgetting a space, etc.

To  mitigate this, I like to write my queries in SQL Management Studio, with the help of RedGate’s SQL Prompt extension, and then copy and paste that query into my C# code to use with Dapper.

 

Problem When Renaming C# Properties

Unfortunately, using SQL Management Studio to build your queries doesn’t solve all the problems.  Let’s say I want to rename the ZipCode property to ZipCode9 to be more obvious that this is a 9 digit Zip Code and not a 5 or 4 digit one.  The problem is when I rename my property, if I don’t remember to go fix the Dapper query, then the ZipCode9 property will be null and everything will fail silently.  This is…. less than ideal.

 

Solution

To fix this, we can use the C# 6 feature nameof which lets you get the name of a variable, property, etc.  So if I change the query to look like below:

 

 

 

 

This will drop the Customer. at the beginning and just leave you with ZipCode9.  So it ends up being the exact same query that we had before, but now it’s strongly typed.

Now when you go to rename a property on your C# model, either your editor (like Visual Studio, Rider, etc.) will rename the property for you.  Or… if you forget to let your editor do the hard work for you, then your app won’t even compile and it’ll tell you this field is broken.  This effectively gives you strongly typed queries for your Dapper code and eliminates one of the headaches that most people have with Dapper.

Obviously, it comes at some cost of readability, but after using this approach for a few months, I can sift through the noise really quick and it’s saved me a handful of times.

 

Conclusion

A few months ago I tweeted this:

 

This was in reference to this nameof solution.  I fully recognize some of you will love this, some of you will hate this, and some of you will sit in the middle like I did originally.

That said, members of my team and I have started using this approach as our default and I’ve actually been fairly happy with it.  Like I said earlier, I can sift through the nameof noise quickly and the strongly typedness has already saved me a handful of times.

 

Hope this helps!