Blazor lets you build interactive web UIs using C# instead of JavaScript. Since the release (candidate) of Umbraco 9, we have the opportunity to use Blazor Components within our Umbraco application. In this blog, we will be setting up Server-Side Blazor within Umbraco 9!
Our Goal
By the end of this blog, you will be able to create your own Blazor components within your Umbraco 9 instance. Our finished application will contain the following:
- An Umbraco 9 instance with Server-Side Blazor installed & configured.
- An Umbraco Doctype Template that renders our Blazor component.
- A Dependency Injected Service that is responsible for sending live updates to our Blazor component.
- An Umbraco Notification (Event) setup to send updates to our Service when we publish an item in the back-office!
In short: We will have a live update feed of all the Umbraco items that we publish, using Blazor!
You can find the whole git repository for this project over at GitHub!
For reference, our final project Solution structure will look as follows:
Attachment 1. Blazor Application File Structure
Getting Started
We have covered how to setup a clean install of Umbraco 9 several times in the past, so we will not be covering that today. If you've missed it, you can check out the details here.
For the basic configurations of our Blazor application, we will be using the Skrift.io article written by Jeroen Koppenol & Lennard Fonteijn as inspiration (More details here)!
Blazor Services
To get started with Blazor in our Umbraco 9 application, we have to start by enabling the Blazor services. Server Side Blazor requires a lot of different services, factories, handlers and configurations to work properly (Storage handlers, SignalR configuration, Authentication providers, etc.). Luckily for us .NET Core 3 and up ships with an extension method to handle the registration of all these different pieces of code for us! We can use the following extension method on our IServiceCollection within the ConfigureServices method of our Startup class to handle the registration of said services.
services.AddServerSideBlazor();
"Server-side Blazor heavily depends on a SignalR connection between the client and the server. This connection enables our Blazor components to communicate with the server before (re-)rendering itself. For the SignalR connection to work, we need to register an endpoint. As with the Service Collection, another built-in extension provides the logic to add this endpoint." - Jeroen Koppenol & Lennard Fonteijn, Skrift.io
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
});
The full ConfigureServices & Configure method should look as follows:
(Note the two lines that are commented out. We will get back to those later!)
The order in which we register our endpoints is important, as our BlazorHub endpoint is dependent on the ASP.Net Core EndpointRoutingMiddleware. Therefor, we register our endpoint after Umbraco has finished the initialization of it's middleware and endpoints.
Razor Directives
After we have configured our Blazor services, we need to define which directives our application will import into our Blazor Components. This is done by creating an '_Imports.razor' file in the root directory of our application, which will contain the usings that are imported into all other .razor files that are within the same folder as the imports file, and all it's subfolders.
Blazor script
As mentioned previously, Blazor makes use of an SignalR connection to communicate between client and server. To establish this connection, we make use of the 'blazor.server.js' file that ships with ASP.NET Core 3 as an embedded resource within the Microsoft.AspNetCore.Components.Server.dll assembly. We can load this script by simply adding a script-tag with a reference to the javascript file at the end of our <body> as shown in the snippet below!
In our project, we will be adding the script-tag directly into our Homepage.cshtml template (With its corresponding Homepage Doctype in Umbraco, for demonstration purpose!). You can of course add this to your Master template instead, but let's keep this as simple as possible for now!
We will also render our homepage's Title & Content field so that our page looks a little less dull, and to demonstrate that we are in fact using an Umbraco page for our Blazor component later on!
Underneath the Model Fields, we will render our Blazor Component, which we will call Display (more information on that later) and specify it's RenderMode as ServerPrerendered, so that Blazor recognizes this components as a Blazor component.
Creating our Service
In our demonstration, we want to create a single location for us to keep track of the OnPublishedEvents that happen in Umbraco, so that we can display them to our front-end. This service will have two responsibilities:
- Storing when and what Umbraco item got published
- Notifying our front-end to update it's content.
Let's start with our Interface for the, what I call, IBlazorPublishEventService. We will define a property to store our PublishEventModels, and a method to add new events to a List of events. For this you will need to create the following class & interface.
Note the INotifyPropertyChanged interface. We will be using this interface in our Service Implementation to notify our front-end that we added a new Item to our list, and that we want our front-end to update it's view!
Next up is the actual implementation of the BlazorPublishEventService, which includes the INotifyPropertyChanged & IBlazorPublishEventService implementation. After this, we can register our component as a Singleton Service in our Startup.cs (In our case, we can uncomment the Services.AddSingleton line from first codesnippet above)
Handling our ContentPublishedNotification
We have our Service setup to use in our Blazor component, but before we can create our component, let's create our Notification Handler first! What used to be an OnPublishedEvent in Umbraco 8, has been replaced with an OnPublishedNotification in Umbraco 9. More information can be found on the Umbraco docs.
First step is to create a new class to handle our Notification, let's call it OnContentPublishNotification.cs. In this class, we will inject our newly created IBlazorPublishEventService using Constructor Injection.
Next, we will need to make our OnPublishedNotification class implement the INotificationHandler<ContentPublishedNotification> interface, so that we can Handle our Notification. In our handle function, we will log a new PublishEventModel to our _blazorPublishEventService's List using the AddEvent method we created!
Final step is to register our NotificationHandler in our Startup.cs class using the .AddNotificationHandler extension method, in between the AddComposers() and Build() extension. Our fully completed class should look as follows:
Let's create our Blazor Component! 🔥
We now have all the pieces in place to create our Blazor component! For this, we will create a Display.razor file that represents the view of our component. We will place this in a folder called "Shared" in our applications' Root directory (as by Microsoft's convention). Right click on the Shared folder, and under the "Add" section you should see the "Razor Component..." option available! If this isn't available, simply create any old file and rename it to 'Display.Razor'!
In our Display.Razor file, we have 3 sections:
- Usings & Injections
- HTML/Razor
- Blazor Code
The first part is our Usings & Injections. Like a regular .cshtml file, this should look quite familiar. The thing that's new, is the @inject statement. This allows .NET to (dependency) inject our IBlazorPublishEventService into our Blazor component automatically!
The second part is our HTML/Razor code. In here we simply loop through the List we created in our Service, and dump out the two fields we store in that Lists' Model.
Finally we have our Blazor code. The first method we implemented/override is the OnInitialized() method. This method is part of the Razor/Blazor Lifecycle, and gets invoked when our component is ready to start. On Intialized, we will register a new method to the PropertyChanged event of our Service (available because we had it implement the INotifyPropertyChanged interface).
When a property has been changed, our Service will Notify all registered classes & services that are subscribed to the PropertyChanged event. (Which we do when we call the AddEvent method on our Service, remember?!). There's just one little workaround we have to do, and that is to create an extra wrapper method to be able to invoke the Blazors' 'StateHasChanged' method asynchronously... and we're done!
🚀 Congratulations, you have successfully created your first Blazor component within an Umbraco 9 application! 🔥
Conclusion
We have successfully created a new Blazor component to our Umbraco 9 application! Whenever we publish an Item in Umbraco, our ContentPublishNotification class will handle this event fired off in Umbraco, and add a new entry to our BlazorPublishEventService' list. Once this entry has been added, we make our Service fire off a NotifyPropertyChanged() event, in which all pages that contain our Blazor component will automatically update their contents! 🚀
If you have any further questions, feel free to contact me over at my socials available at the Contact page, and I'd love to hear about the amazing creations you're able to come up with, with this setup! For more details and/or the full code involved in this project, check out my GitHub Repository!
Attachment 2. Demo of Blazor Publishing
Sources used in this article:
Skrift.io - Umbraco & Blazor by Jeroen Koppenol & Lennard Fonteijn, https://skrift.io/issues/umbraco-and-blazor/
ASP.NET Core Blazor Configuration, https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/configuration?view=aspnetcore-5.0
Subscribing to Notifications, https://our.umbraco.com/documentation/Fundamentals/Code/Subscribing-To-Notifications/