Practical ASP.NET

Sharing Data and Splitting Components in Blazor

ASP.NET Core Version 3.1 has at least two major changes that you'll want to take advantage of. Well, Peter thinks you will. Depending on your background, your response to one of them may be a resounding “meh.”

The biggest change in ASP.NET Core Version 3.1 is that you can now pass data you've gathered in your server page to the Blazor component hosted in the page. But, for me, a second change is almost as important, even though it doesn't actually change the way your application works: You can now put your component's UI markup and code in separate files. It doesn't make any difference at run time but I think it's a big deal because will make you more productive.

Sharing Your Server-Side Data
The first production release of Server-Side Blazor dropped the ability to pass data gathered on the server to the Blazor component hosted on the page. That was too bad -- there are few things that make me crazier than a page that downloads in a heartbeat…and then spins for 10 seconds while making Ajax calls to get the data for the initial display.

Version 3.1 gives you back the ability to pass data to the component before the component is rendered. Along the way to delivering that, Blazor throws in a tag helper (named component) for you to use to invoke your Blazor component.

As an example, this code invokes a Blazor component called CustomerUpdate, rendering the component on the server, and passing the View or Page's Model property to a parameter in the component called custData:
<div>
  <component type="typeof(CustomerUpdate)" 
             render-mode="ServerPrerendered" 
             custData="@Model"/> 
</div>

The RenderComponent method on the HtmlHelper has also been extended to pass parameters. When using RenderComponent, just pass an anonymous object with the data as RenderComponent's second parameter. In that anonymous object, define properties whose names match parameters in your component set those properties to the data you want to pass.

Just like my previous example, this code invokes my CustomerUpdate component, rendering it on the server, and passing whatever's in the View or Page's Model property:

@(await Html.RenderComponentAsync<CustomerUpdate>(
    RenderMode.ServerPrerendered, 
    new { custData = @Model }))

It is, of course, still your responsibility to set up the matching parameter in your component.

Separating Code from the UI
And that's cool and all, but I like the other major change almost as much.

Up until Version 3.1, a Blazor component's UI (HTML mixed with Blazor code) lived in a .cshtml file along with the code which was nested inside an @code block. This is a very familiar paradigm for Angular and React developers.

However, if you grew up (as I did) with Windows Forms, WPF, Web Forms, or (for that matter) MVC, having the UI and code in the same file feels clunky. I realize that it's probably a lifestyle choice: Separating code and the UI means that you're going to have one more tab open in the editor and lose time switching between the two files. I think there are some good reasons for preferring to separate the files and I'll get to them at the end of this column (and, I realize, I may not convince you).

Not all the tooling to support this change is in place, though: I used Visual Studio Community Preview 16.5.0 to test this and, when adding a Blazor component, I didn't get an option to add separate UI file and code files -- you're still going to be adding a single .razor file when you pick Blazor Component.

However, once you've added that .razor file (e.g. CustomerUpdate.razor), you can right-click on the folder the file is in and add a class file with the same name as your .razor file but with the .cs extension (e.g. CustomerUpdate.razor.cs). In Solution Explorer, your new file will nest underneath your .razor file, just like a Razor Page. The .razor file acts as a parent to the .razor.cs file -- deleting or renaming the .razor file automatically deletes or renames the .razor.cs file (though that renaming doesn't extend to the name of the class inside the .razor.cs file).

Here's the part I like: Switching between the UI (.razor file) and the code (.razor.cs) file is easy. In either file, pressing the F7 key takes your to wherever you left off in the other file (you can also right-click in the editor window and pick either Go to PageModel or Go to Page, depending on which file you're in). This means that if you're making changes to your UI, you can press F7 to switch to your code file and begin writing code. When you realize that you need to tweak your UI, pressing F7 takes you back to your UI, right where you left off -- no more scrolling up and down in the .razor file as you try to coordinate code and HTML.

And, of course, keeping the code and UI separate supports all sorts of best practices: It promotes loose coupling, allows you to change your logic or your UI without touching the other file, etc., etc. But the F7 thing is what I like.

By the way, you lose (almost) nothing by adopting the two-file approach. If you write your code first you'll find, when you're working with your HTML, that you'll get all the IntelliSense support that you got when everything was in one file. You can also continue to mix code in with your HTML and even have a @code block in your UI file, if that makes sense to you (which means, by the way, that you can incrementally migrate to the two-file format by moving code from your .razor file to your .razor.cs file as it makes sense to you). Most of the directives you're using in your .razor file even continue to support the code in your .razor.cs file -- you don't need to replace an @inject in your .razor file with a constructor and a private field in your .razor.cs file in order to buy into ASP.NET's dependency injection framework, for example.

So far, I've found only one downside: The @using statements in your .razor file and _Imports.razor file are ignored by your code file. Unfortunately, you'll need to copy them into your code file and rewrite them into C# syntax.

So there are two significant changes from December of last year. But wait! There's more, as I'll discuss in my next column. Those changes aren't nearly as cool, though.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube