Blazor EditForms, an essential tool or too much magic?
July 23, 2020 · 6 minute read · Tags: blazor
Blazor ships with something called an EditForm
.
But what is it, do you have to use it, and what if you don’t fancy relying on magic to make your application work?
What is it?
Here’s a Blazor EditForm
in action.
<EditForm Model="Command" OnValidSubmit="HandleValidSubmit">
<label for="title">Title</label>
<InputText id="title" @bind-Value="Command.Title" class="form-control"/>
<label for="slug">Slug</label>
<InputText id="slug" @bind-Value="Command.Slug" class="form-control"/>
<InputTextArea @bind-Value="Command.Body" class="form-control" rows="20"/>
<button type="submit" class="btn btn-primary">Publish</button>
</EditForm>
We have the EditForm
component itself, which we’ve pointed at an instance of a C# class (Command
in this case) via the Model
property.
We’ve assigned a method to the OnValidSubmit
attribute, so when the form is submitted (and if it’s valid, more on that in a moment), HandleValidSubmit
will be invoked.
This simple example also utilises InputText
and InputTextArea
components to render each field in the form.
Run this in the browser and you’ll get a pretty standard looking form…
<form>
<label for="title">Title</label>
<input id="title" class="valid">
<label for="slug">Slug</label>
<input id="slug" class="valid">
<textarea rows="20" class="valid"></textarea>
<button type="submit">Publish</button>
</form>
So far so good, EditForm
hasn’t required us to jump through too many hoops; the resulting form is un-opinionated and largely the same as the one you might have hand-crafted yourself.
The only ’extra’ thing ‘EditForm’ has done for us, is to mark up each input with a valid
CSS class.
Oh, and of course it will keep the values in our Command
model in sync with the values entered by the user, which is handy!
But if that’s all the EditForm
did you might be left wondering why bother? The answer, it turns out, is EditForm
’s true superpower…
Validation - a necessary evil?
Let’s be honest, wiring up validation in your forms is really important, but pretty boring!
You probably don’t fall asleep every night dreaming of all the different types of validation you can implement in your application.
So the big question, can EditForms
make validation so simple you don’t need to worry about it (and can focus on more interesting things!)
Here’s the simplest way to add validation to our EditForm
.
<EditForm Model="Command" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<label for="title">Title</label>
<InputText id="title" @bind-Value="Command.Title" class="form-control"/>
<label for="slug">Slug</label>
<InputText id="slug" @bind-Value="Command.Slug" class="form-control"/>
<InputTextArea @bind-Value="Command.Body" class="form-control" rows="20"/>
<button type="submit" class="btn btn-primary">Publish</button>
</EditForm>
Simply by adding the <DataAnnotationsValidator />
component we’ve enabled validation for the entire form.
This will make sure our EditForm
considers any validation rules on our Command
model, if they’re marked up using DataAnnotations
.
public class Command {
[Required]
public string Title { get;set; }
public string Slug { get; set; }
public string Body { get;set; }
}
With this [Required]
attribute we’ve indicated that the user must enter a Title
.
By adding the <DataAnnotationsValidator />
component to our form, any attempt to submit said form will result in errors if this field is left blank.
Now when you run this in the browser, if you leave Title
blank but enter values for the other fields and hit the submit button you’ll end up with this rendered HTML…
<form>
<label for="title">Title</label>
<input id="title" class="invalid">
<label for="slug">Slug</label>
<input id="slug" class="modified valid">
<textarea rows="20" class="modified valid"></textarea>
<button type="submit">Publish</button>
</form>
This behaves largely as you’d expect.
- If you attempt to submit the form without entering anything into any of the inputs the
title
input will get a class ofinvalid
- Any input you type something into will be decorated with a
modified
CSS class - If you type something into the
title
input but then delete it again it will be decorated with bothmodified
andinvalid
classes
Now you just have to implement a little bit of CSS to highlight any fields with valid
or invalid
css classes (or use Bootstrap which has these covered).
If you want to show a summary of all the validation errors you can simply render an instance of the <ValidationSummary />
component in your EditForm
.
What else can you do?
So now you have an EditForm
what else can you do with it?
Style it up
You can easily style your forms as normal.
Any classes you assign to any of your inputs will be respected. Here’s our same form dressed up with a few more Bootstrap CSS classes.
<EditForm Model="Command" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<label for="title">Title</label>
<InputText id="title" @bind-Value="Command.Title" class="form-control"/>
</div>
<div class="form-group">
<label for="slug">Slug</label>
<InputText id="slug" @bind-Value="Command.Slug" class="form-control"/>
</div>
<div class="form-group">
<InputTextArea @bind-Value="Command.Body" class="form-control" rows="20"/>
</div>
<div class="text-right">
<button type="submit" class="btn btn-primary">Publish</button>
</div>
<ValidationSummary />
</EditForm>
Handle invalid submissions
We’ve seen how our simple EditForm
handled valid submissions. It’s no surprise you can also handle the form being submitted whilst invalid…
<EditForm Model="Command" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInvalidSubmit">
</EditForm>
Take more direct control
Under the hood EditForm
keeps track of the current state of the form (which fields have been modified) and any validation errors which have been triggered.
It stores this in something called an EditContext
.
If you wish, you can access that EditContext
.
<EditForm OnSubmit="HandleSubmit" EditContext="MyEditContext">
</EditForm>
Note we’ve dropped the Model
attribute assignment and swapped it for an EditContext
instead. You can specify either a Model
or EditContext
but not both.
We’ve also replaced OnValidSubmit
with OnSubmit
which will be invoked on submit whether the form is valid or invalid.
When you assign a model using the Model
attribute your EditForm
will create and manage its own EditContext
. Conversely, when you assign your own EditContext
you need to create it yourself.
Here’s how we can create our own EditContext
to make this work.
@code {
protected Add.Command Command { get; set; } = new Add.Command();
protected EditContext MyEditContext { get; set; }
protected override void OnInitialized()
{
MyEditContext = new EditContext(Command);
}
}
Note how we point our new EditContext
to an instance of our model (Command
) when we instantiate it.
Now you can access MyEditContext
to trigger validation, check if anything has been modified etc.
Whether you need direct access to EditContext
will vary depending on your requirements.
Oftentimes using Model
will suffice, but it’s good to know you can dig a little deeper when needed.
Just enough magic?
So that’s how the EditForm
works in Blazor, but do you have to use it?
Technically there’s nothing stopping you creating your own forms, writing logic to perform validation etc. and using binding to update the UI accordingly.
But, as framework magic goes EditForm
is pretty unassuming.
It doesn’t really stop you from doing anything, and massively reduces the amount of boilerplate you’d otherwise have to write to make something like validation work so smoothly.
So overall, I put it in the ‘useful abstraction’ rather than the ‘so magic I have no idea what it’s doing’ camp!