dotnet / dotnet/aspnetcore

API-Proposal: Add functions to EditContext to filter Validationmessages based on Model property in FieldIdentifier

Open
#40,306 4 comments 4 reactions 0 assignees View on GitHub
api-suggestion area-blazor enhancement feature-blazor-form-validation
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

## Background and Motivation

When Editing large objects that contain other (child)objects it's not easily possible to determine if there are errors on a specific (child)object.
This API proposal adds three functions that use the provided (child)object to get ValidationMessages or get/set the IsModified state.
The current API can either get or set values based on the entire supplied model, or on 1 specific ``FieldIdentifier`` this proposal adds the possibility to have an additional filter that uses the ``Model`` property of the ``FieldIdentifier``.

## Proposed API

```cs
public class EditContext
{
public IEnumerable GetValidationMessages(object childObject)

public bool IsModified(object childObject)

public void MarkAsUnmodified(object childObject)
}
```
Implemented like:
```cs
public class EditContext
{
public IEnumerable GetValidationMessages(object childObject)
{
foreach (var state in _fieldStates)
{
if (state.Key.Model == childObject)
{
foreach (var message in state.Value.GetValidationMessages())
{
yield return message;
}
}
}
}

public bool IsModified(object childObject)
{
foreach (var state in _fieldStates)
{
if (state.Key.Model == childObject && state.Value.IsModified)
{
return true;
}
}
return false;
}

public void MarkAsUnmodified(object childObject)
{
foreach (var state in _fieldStates)
{
if (state.Key.Model == childObject)
{
state.Value.IsModified = false;
}
}
}
}

```
## Usage Examples

Imagine you have a Client object, this object holds values for personal details (name etc.). It also holds a collection of Addresses and a collection of CommunicationMethods (Phone numbers/ Email addresses etc.)

```cs
class Client
{
Person PersonalData { get; }
string ClientCode { get; set; }
}

class Person
{
string Name { get; set; }
IEnumerable

Addresses { get; set; }
IEnumerable Communicationss { get; set; }
}
```

As you can see in the below image, the user sees a red exclamation mark with Addresses:
![image](https://user-images.githubusercontent.com/12493059/154741733-b87cedc3-f2ae-4eb8-802c-d6257aa4f18f.png)

And when the users select the Addresses Tab, indeed the user will see the problem.
![image](https://user-images.githubusercontent.com/12493059/154741978-b3e5b1c7-2e9e-438e-bb82-7775764cdb8b.png)

## Implementation **without** API proposal
I see 2 possibilities to achieve this functionality with the current API.

1.) Enclose the entire Client component in 1 ``EditForm``, to check if a specific subset (for example an address) has errors on it a user will have to create a ``foreach`` loop that queries all ``FieldIdentifier`` (or ``Expression>`` accessor used by that Address. although feasible it's not very flexible, as a user will need to supply a list of ``FieldIdentifiers`` or ``Expressions`` used by that Address.

2.) Use separate ``EditForm`` Components surrounding specific subsets of the data (1 for Personal Data, 1 for each Address, and 1 for each CommunicationMethod), and every time the validation state changes cache that state on the Main Client component so it can be used to display the error icon on Tabs.

## Implementation **with** API proposal

With this API proposal, the solution would be greatly simplified by enclosing the entire Client component in 1 ``EditForm``
Now each tab can determine if there are errors by calling ``EditContext.GetValidationMessages(address).Any()``
This even works when the Tab is virtual (Meaning its contents is only instantiated when the Tab is Active) and has the added benefit that as the EditForm persists even when a Tab is deselected, as soon as the Tab is instantiated again, the errors will still be there. without needing to call ``Validate`` on the ``EditContext``

## Risks
As this proposal adds 3 functions, without changes to the underlying Class, or API surface the risks are low.
I see a small risk for users to get confused.
For example, a user could be confused to which function the user should choose: ``IsModified()`` or ``IsModified(childObject)`` this could (possibly) be mitigated by changing the functions name to be more specific like IsChildModified(object childobject)

I am willing to submit a PR for this proposal if accepted.

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.