dotnet / dotnet/aspnetcore

[Blazor] EditContext: add a way to check if all registered fields were modified

Open
#63,463 0 comments 3 reactions 0 assignees View on GitHub
api-suggestion area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

## Background and Motivation

I am starting to learn Blazor and I faced small challenge, that frankly should be easy - enabling "submit" button if the form is valid. In Angular it's very simple:
```html
Submit
```
But in Blazor? Suggested solution is to listen to field change and call `Validate()` on that. But my validator (eg. ``) is already doing that! So why should I do validation twice to get this functionality? And in documentation it's stated that this will cause generation of validation messages for all properties. First suggestion: "Don't use a [ValidationSummary](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms.validationsummary) component on the form."

W...what? And second is some workaround. This is bad! For someone coming from Angular, this is terrible.

How can we make it better? Well, we could do something like this:
```cs
public bool IsSubmittable => !_editContext.GetValidationMessages().Any() && _editContext.IsModified();
```

But! There's a catch. `_editContext.IsModified()` returns true if ***any*** property on the model is modified.
And so there's no way for me to check if all fields were modified. Well I can! Using reflection but that's ugly! Especially when `FIeldState` is an internal class. And everything is sealed so there's no way for me to extend it without essentially rebuilding entire `EditForm`, `EditContext` and `InputText` aswell as `InputBase`. Which I don't want to. So here's my proposition:

## Proposed API

```diff
namespace Microsoft.AspNetCore.Components.Forms;

public sealed class EditContext
{
+ public bool AllModified()
+ {
+ foreach (var state in _fieldStates)
+ {
+ if (!state.Value.IsModified)
+ {
+ return false;;
+ }
+ }

+ return true;
+ }
}
```

But that won't work without one more modification I guess. I haven't analyzed it thoroughly but I don't see that `InputBase` "tells" `EditContext` that it exist when it's initialized. So each input control would need to be registered in `EditContext` but since each control inherits from `InputBase` it will require only modification on `InputBase`. But I am not sure about that, maybe it already works this way.

## Usage Examples

```csharp
public bool IsSubmittable => ! _editContext.GetValidationMessages().Any() && _editContext.AllModified();
```

```html
Submit
```

## Alternative Designs

Alternative design would be to add some way to iterate over register fields that would give more flexibility to the developer on how to process that.

Even more alternative design would be to add [reactive forms](https://angular.dev/guide/forms/reactive-forms) just like they exist in Angular but that would require dependency on [reactivex](https://github.com/dotnet/reactive). Would be very cool nevertheless.

## Risks

No risks, it's a new method that only reads from internal state of `EditContext`. There could be small risk if modification to `InputBase` is needed, because that would change slightly it's behaviour.

## In summary
I think this improvement would be very nice, especially for people new to Blazor. And I wish Blazor could one day overrule all those awful JavaScript frameworks ^_^

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.