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.

Unleash Blazor's Potential

Blazor promises to make it much easier (and faster) to build modern, responsive web applications, using the tools you already know and understand.

Subscribe to my Practical ASP.NET Blazor newsletter and get instant access to the vault.

In there you'll find step-by-step tutorials, source code and videos to help you get up and running using Blazor's component model.

I respect your email privacy. Unsubscribe with one click.

    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 of invalid
    • 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 both modified and invalid 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!

    Join the Practical ASP.NET Newsletter

    Ship better Blazor apps, faster. One practical tip every Tuesday.

    I respect your email privacy. Unsubscribe with one click.

      Next up

      Finally! Improved Blazor Server reconnection UX
      .NET 9 changes how your Blazor Server app behaves when server connection is lost
      .NET 9 improves JavaScript module importing for Blazor
      .NET 9 ensures your users always get the latest version of your JS modules
      How to use .NET 9 to ensure users always get the latest version of your stylesheets
      .NET 9 changes how static files are served, and it solves a long-standing problem