0

In this example, form 1 and 2 use the same data model (User).

In form 1 all fields are mandatory.

In form 2, all fields are mandatory except the Name.

I would like to know how I can manually modify the validation of the Name field in this second form to suit this rule.

User.cs

    public class User
    {
        [Required]
        public string Name { get; set; }
        [Required]
        public string Cpf { get; set; }
        [Required]
        public string Rg { get; set; }
        [Required]
        public string Phone { get; set; }
    }

Page.razor

//Form 1
<EditForm Model="user">
  ...
</EditForm>

//Form 2
<EditForm Model="user">
  ...
</EditForm>

@code {
  User user = new User();
}
José Guilherme
  • 57
  • 1
  • 10

3 Answers3

1

Well since the attributes are kinda coupled with your properties one way is to have an abstract User Class containing all the properties except Name.

Then extend this class with 2 child classes one with a Required Name the other with an optional Name.

Another way is to implement your own custom requiredIF attribute.

See this example and you can customize it adding some context to your form. RequiredIf Conditional Validation Attribute

Anestis Kivranoglou
  • 7,728
  • 5
  • 44
  • 47
  • Actually what I meant was that I would like the property to not be mandatory if the field is not rendered on screen (ie the property is not declared in that form). – José Guilherme Aug 16 '22 at 19:18
0

Solution 1: In Form 2 you can use OnSubmit instead of OnValidSubmit so the validation won't stop you. And in the method you passed to OnSubmit you can do the validation yourself.

FormExample:

@(canSendData ? "Sent" : "Not sent")

@if (User is not null)
{
    <EditForm Model="User" OnSubmit="Submit">
        <DataAnnotationsValidator />

        <label>Phone</label>
        <InputText @bind-Value="User.Phone" />
        <ValidationMessage For="@(() => User.Phone)" />
        
        <label>Name</label>
        <InputText @bind-Value="User.Name" />
        <ValidationMessage For="@(() => User.Name)" />

        <button class="btn btn-success" type="submit">Save</button>
    </EditForm>
}

@code {
    private bool canSendData;

    [Parameter]
    public User User { get; set; } = null!;

    private void Submit(EditContext editContext) 
    {
        var phoneFieldIdentifier = editContext.Field("Phone");
        var nameFieldIdentifier = editContext.Field("Name");

        editContext.NotifyFieldChanged(phoneFieldIdentifier);

        var validationMessagesCount = editContext.GetValidationMessages().Count();
        if (validationMessagesCount == 0)
        {// every field is valid
            canSendData = true;
            StateHasChanged();
        }
        else if (validationMessagesCount == editContext.GetValidationMessages(nameFieldIdentifier).Count())
        {// every field is valid except the field for the `Name` property, but we dont care about it
            canSendData = true;
            StateHasChanged();
        }
        else 
        {// there is/are some invalid field/s that we care about
            canSendData = false;
            StateHasChanged();
        }
    }
}

I tried it and it works- it validates and even shows validation messages!

Some links that provided info: Binding a form (docs) and this answer.

I would say that this solution is easy and fast to implement, but it has a downside... Let's say you fill the name field, click Save, but some field was invalid so it didn't send the data and showed the validation message... but before you click Save again, you (for some reason) decide that you don't want the name field to be filled anymore, so you delete its content, now you click Save and the problem has arrived... The validation message for Name property shows. I don't know why but it does... On the other hand, even though the validation message shows the form will be saved. So it seems everything works "properly", BUT for some reason in this scenario the name field validation message is shown.

Solution 2: This another solution is more difficult to implement, but in my opinion it might be the most proper way how to do this- implementation of the custom validator. More here.

Bonus: While searching for info I came across this interesting blog post. It looks exactly like what you need but it says:

"If you use FluentValidation in a commercial project, please sponsor the project financially."

0

You need to separate out your edit context from your base record.

The user record:

public class User
{
    public string Name { get; set; } = String.Empty;
    public string Cpf { get; set; } = String.Empty;
    public string Rg { get; set; } = String.Empty;
    public string Phone { get; set; } = String.Empty;
}

And then two edit model classes that are used by the edit forms:

public class UserEditModel1
{
    [Required]
    public string Name { get; set; } = String.Empty;
    [Required]
    public string Cpf { get; set; } = String.Empty;
    [Required]
    public string Rg { get; set; } = String.Empty;
    [Required]
    public string Phone { get; set; } = String.Empty;

    public UserEditModel1(User user)
    {
        this.Name = user.Name;
        this.Cpf = user.Cpf;
        this.Rg = user.Rg;
        this.Phone = user.Phone;
    }

    public User User => new User
    {
        Name = this.Name,
        Cpf = this.Cpf,
        Rg = this.Rg,
        Phone = this.Phone
    };
}

public class UserEditModel2
{
    public string Name { get; set; } = String.Empty;
    [Required]
    public string Cpf { get; set; } = String.Empty;
    [Required]
    public string Rg { get; set; } = String.Empty;
    [Required]
    public string Phone { get; set; } = String.Empty;

    public UserEditModel2(User user)
    {
        this.Name = user.Name;
        this.Cpf = user.Cpf;
        this.Rg = user.Rg;
        this.Phone = user.Phone;
    }

    public User User => new User
    {
        Name = this.Name,
        Cpf = this.Cpf,
        Rg = this.Rg,
        Phone = this.Phone
    };
}

Comment: My User class would be a record rather that a class which I use to check edit state.

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31