Trix editor as a Blazor Component
By Martijn Storck
Trix is, quoting the authors, a rich text editor
for everyday writing that is popular with Ruby on Rails developers. I’ve used
it in the past on several Rails projects and have always received positive
user feedback on the editor component. In this post I will explain how to
build your own Razor Component to easily use Trix in a Blazor Server application.
We will create our own subclass of InputText
and configure some JavaScript
interop to make the value bindings work.
Add Trix assets to the project
A new Blazor server application comes without any sort of Javascript toolchain,
so we’ll simply add a hosted copy of the JS and CSS assets for Trix to Pages/_Host.cshtml
:
<head>
…
<link href="https://cdnjs.cloudflare.com/ajax/libs/trix/1.3.1/trix.min.css" rel="stylesheet" />
</head>
<body>
…
<script src="https://cdnjs.cloudflare.com/ajax/libs/trix/1.3.1/trix.min.js"></script>
</body>
Preparing some JavaScript interop code
The Blazor value bindings rely on change events being sent to the HTML input backing the Trix Editor. Trix doesn’t do this by default, so we will create a small JavaScript function that we can call from our component using JS InterOp.
Again, we’re not depending on any JS toolchain so add the following code to a JavaScript file
that gets loaded from your _Host.cshtml
:
window.setupTrixEvent = () => {
document.addEventListener("trix-change", (event) =>
Trix.triggerEvent("change", { "onElement": event.target.inputElement }));
}
This triggers the change
event on the input whever Trix fires the trix-change
event,
allowing it to get picked up by the Blazor Server framework.
Create a new Input component
Your current plain text input will probably look something like:
<InputText id="content" @bind-value="@MyModel.content" />
Our goal is to create a drop in replacement for this that initializes a Trix Editor instance and cleans up afterwards. Luckily Trix, since Trix is designed to work with Hotwire and Turbo it’s a great fit for Blazor Server out of the box.
This is our component, save this in Shared/TrixEditor.razor
:
|
|
Let’s go over this line by line:
- Line 1 sets up our parent class; InputText. We’ll override the view but keep everything else.
- Line 2 injects the JavaScript runtime that we use to call our interop function.
- Line 4 through 7 contain the HTML for the component. It consists of a hidden input and a
<trix-editor>
tag that is used by the JavaScript. The large@onchange
handler is copied verbatim from the source code if the InputText component. Everything is wrapped in a top-leveldiv
to ensure the extra siblings totrix-editor
that are added by the JavaScript are cleaned up. - Lines 10-11 define the
id
parameter that is necessary for the component to function. - Lines 13-17 calls our
setupTrixEvent
Javascript function when the component is first rendered. By overridingOnAfterRenderAsync
instead ofOnInitializedAsync
we prevent ourselves from breaking prerendering.
Using our new component
Using the component is simple:
<EditForm Model="@MyModel">
<DataAnnotationsValidator />
<ValidationSummary />
<TrixEditor id="content" @bind-value="MyModel.Description" />
</EditForm>
Because we bind the hidden component to our value, validations work the same way as
using a regular text input, making the TrixEditor
a drop-in replacement for TextInput
.
For those unfamiliar with Blazor: it’s a feature of ASP.NET that allows you to write client apps with C#. The application can be hosted on the server (with updates streaming over Websocket, not unlike Phoenix LiveView and Basecamp Hotwire) or by running the .NET runtime in WebAssembly. What you end up with is an application that’s as responsive and full-featured as a JavaScript SPA without having to create a JavaScript SPA. You can read more about it on Microsoft.com.