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
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.
Blazor components are supported out-of-the-box in .NET Core 3 and up, but are not enabled by default in an MVC application. Let's start and enable Blazor!
For Blazor to work, we first need to add the required Service Side Blazor services to our service collection. Luckily for us, there is a built-in Service Collection extension that we can use to add underneath our Umbraco Services configuration section.
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.
The full ConfigureServices & Configure method should look as follows:
(Note the two lines that are commented out. We will get back to those later!)
It is important that we configure our Blazor endpoint after Umbraco has initialized its' routing-middleware, since this extension depends on the EndpointRoutingMiddleware.
For the application to recognize the logic used in Blazor components, we need to add the "_Imports.razor" file to the root of the project, and import a few namespaces. This is also the location where we can import project-specific namespaces, so that our Blazor components can be recognized in all .razor files!
For the client to be able to establish the aforementioned SignalR connection with the server, the blazor.server.js file needs to be loaded into the application's client, which can be done by adding a script-tag at end of the <body> tag. In our project, we will be adding this directory 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:
At last... 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). See attachment 1.
Attachment 2. Creating the Display.razor file
In our Display.Razor file, we have 3 sections:
- Usings & Injections
- 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! 🔥
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/