Require one field or another
Asked Answered
I

5

8

Basically what I'm trying to figure out is how to require at least one of two fields to be filled out in a View.

In my View, I have two text fields called ISBN and ISBN13. It doesn't matter which one the user fills in as long as one of them gets filled in.

I'm not sure what to do here expect to look into writing a custom validator so I thought I would ask first. I would have included some code but since it's just two simple fields I thought this explanation would be better.

Intrinsic answered 15/4, 2017 at 14:38 Comment(5)
You can make a small change to the answer posted on this to get what you want #11959931Jato
Custom code is absolutely appropriate here. In fact you don't even need a custom validator; this is core domain business logic, not just a UI concern. It should be impossible to create an entity in an invalid state (for example you can put throwing-code in the construction to validate this. A separate javascript validation in the UI would be helpful to give the user instantaneous feedbackGeoffrey
When a user receives a good answer to their question, that user has the option to "accept" an answer. Acceptance is indicated by a colored checkmark next to the answer that has been accepted by the original author of the question.Raker
@Raker Though your answer was very good it's not what I ended up going with. The solution I decided on was having the model itself perform the validation.Intrinsic
@ChristopherJohnson then I suggest you answer your own question to help the public see another alternative resolution.Raker
R
16

You may do a manual validation in your controller action. The AddModelError method will help you use the validation stack.

[HttpPost]
public ActionResult Edit(EditModel model)
{
    if (string.IsNullOrEmpty(model.ISBN) && string.IsNullOrEmpty(model.ISBN13))
    {
        var validationMessage = "Please provide ISBN or ISBN13.";
        this.ModelState.AddModelError("ISBN", validationMessage);
        this.ModelState.AddModelError("ISBN13", validationMessage);
    }

    if (!string.IsNullOrEmpty(model.ISBN) && !string.IsNullOrEmpty(model.ISBN13))
    {
        var validationMessage = "Please provide either the ISBN or the ISBN13.";
        this.ModelState.AddModelError("ISBN", validationMessage);
        this.ModelState.AddModelError("ISBN13", validationMessage);
    }

    if (this.ModelState.IsValid)
    {
        // do something with the model
    }

    return this.View(model);
}

Some may say that it is not the responsibility of the controller to do the validation of the query. I think that the controller's responsibility is to adapt the web request into a domain request. Therefore, the controller can have validation logic. If you do not have a domain/business layer, this consideration has no sense.

Raker answered 15/4, 2017 at 18:31 Comment(2)
Why do this in the controller? What happens when you want to check more than only two properties? The if-statements would become longer. I prefer to add the validation in the model instead, like in my answer below. That way, you can use attributes like MinLength, MaxLength, Range, etc, and the controller remains clean.Napkin
There are many ways to proceed. This one is the "simplest" as it does not rely on a library or complex code setup. Why use the controller? Read the last paragraph.Raker
S
10

Use MVC Foolproof NuGet package and then you can use the RequiredIf attribute like below:

[RequiredIf("ISBN==\"\"")] // backslash is used for escaping the quotes
public string ISBN13 { get; set; }

[RequiredIf("ISBN13==\"\"")]
public string ISBN { get; set; }
Sessler answered 15/4, 2017 at 15:32 Comment(0)
N
3

With data annotations, I think this is a good option, if you want to avoid third party. Skip required attributes and set the rule on a property that has a getter only:

public class EditModel
{
    public string ISBN { get; set; }
    public string ISBN13 { get; set; }
    
    [Range(1, Double.MaxValue, ErrorMessage = "At least one field must be given a value")]
    public int Count
    {
        get
        {
            var totalLength = 0;
            totalLength += ISBN?.Length ?? 0;
            totalLength += ISBN13?.Length ?? 0;
            return totalLength;
        }
    }
}

For other data types, and with specific attributes, it works with something like this:

public class EditModel
{
    [MinLength(10)]
    public string ISBN { get; set; }
    [MinLength(13)]
    public string ISBN13 { get; set; }
    [MinLength(1)]
    public List<Guid> ISBNItems { get; set; }
    [Range(1, 100)]
    public int? SomeNumber { get; set; }
    
    [Range(1, Double.MaxValue, ErrorMessage = "At least one field must be given a value")]
    public int Count
    {
        get
        {
            var totalLength = 0;
            totalLength += ISBN?.Length ?? 0;
            totalLength += ISBN13?.Length ?? 0;
            totalLength += ISBNItems?.Count ?? 0;
            totalLength += SomeNumber ?? 0;
            return totalLength;
        }
    }
}

Edit: I just thought of another way, where I replace the Count-property with this:

[RegularExpression("True|true", ErrorMessage = "At least one field must be given a value")]
public bool Any => ISBN != null || ISBN13 != null;

Basically, it's the same thing, just a getter that checks the properties are not null.

Napkin answered 18/10, 2021 at 19:17 Comment(0)
U
1

I imagine adding something like this to your controller, in the create section just before saving changes to the database.

int countISBN = Product.ISBN.Count() + Product.ISBN13.Count();
if (countISBN <= 9)
{
    // Add in an error message.
    return View();
}

What this will do is count the characters in the two fields, will sum them. If their sum is below 10 it will throw an error.

Ulterior answered 15/4, 2017 at 15:25 Comment(0)
M
0

.net framework: Foolproof

.net Core: FoolProof.Core

[RequiredIfEmpty] is exist in new version. for example:

    [RequiredIfEmpty(dependentProperty: "ISBN", DependentPropertyDisplayName = "ISBN", ErrorMessage = "If ISBN is null then ISBN13 is require")]

Also has many other attribute:

[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]

to see reference click on this link

Merengue answered 5/6, 2021 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.