3

I've got an MVC app which features a DropDownList and TextBox. I'm using MVC validation and I'm happy with it.

At the moment The DropDownList is [Required], but I want to make it so the TextBox can function as an 'other: please specify' style input for the DropDownList.

How do I make it so that the [Required] attribute on the DropDownList is conditional on the TextBox being empty?

This question is similar, but it's over a year old. Anything in the current version of MVC that makes this easy to do?

Community
  • 1
  • 1
roryok
  • 9,325
  • 17
  • 71
  • 138

2 Answers2

8

It would be pretty easy to write a custom validation attribute:

public class RequiredIfPropertyIsEmptyAttribute : RequiredAttribute
{
    private readonly string _otherProperty;
    public RequiredIfPropertyIsEmptyAttribute(string otherProperty)
    {
        _otherProperty = otherProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_otherProperty);
        if (property == null)
        {
            return new ValidationResult(string.Format("Unknown property {0}", _otherProperty));
        }

        var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
        if (otherPropertyValue == null)
        {
            return base.IsValid(value, validationContext);
        }
        return null;
    }
}

then you could have a view model:

public class MyViewModel
{
    public string Foo { get; set; }

    [RequiredIfPropertyIsEmpty("Foo")]
    public string SelectedItemId { get; set; }

    public IEnumerable<SelectListItem> Items {
        get
        {
            return new[] 
            {
                new SelectListItem { Value = "1", Text = "item 1" },
                new SelectListItem { Value = "2", Text = "item 2" },
                new SelectListItem { Value = "3", Text = "item 3" },
            };
        }
    }
}

A controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

And of course a view:

@model MyViewModel

@using (Html.BeginForm())
{
    <div>
        @Html.LabelFor(x => x.Foo)
        @Html.EditorFor(x => x.Foo)
    </div>
    <div>
        @Html.LabelFor(x => x.SelectedItemId)
        @Html.DropDownListFor(x => x.SelectedItemId, Model.Items, "-- select an item --")
        @Html.ValidationMessageFor(x => x.SelectedItemId)
    </div>

    <input type="submit" value="OK" />
}

or you could do as I do: download and use the FluentValidation.NET library, forget about data annotations and write the following validation logic which seems pretty self-explanatory:

public class MyViewModelValidator: AbstractValidator<MyViewModel> 
{
    public MyViewModelValidator() 
    {
        RuleFor(x => x.SelectedItemId)
            .NotEmpty()
            .When(x => !string.IsNullOrEmpty(x.Foo));
    }
}

Just go ahead and Install-Package FluentValidation.MVC3 and make your life easier.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Wouldn't the FluentValidation approach mean I'd have to write a separate validation class for ever ViewModel I use? I do admire the syntax, but it seems like I'd be making extra work for myself, writing a separate ModelValidator class and constructor containing RuleFor(x => x.name).NotNull() when I could just write [Required]? – roryok Nov 03 '11 at 14:24
  • Actually the first method above doesn't seem to work for me. No compile or runtime errors or anything, it just doesn't happen. Still catching the empty DropDownList even when the TextBox is populated. I might try FluentValidation after all – roryok Nov 03 '11 at 14:35
  • roryok - Wouldn't you be using separate data annotations for every ViewModel you write? – Dismissile Nov 03 '11 at 14:48
  • 1
    @roryok, yes, with FluentValidation, it would mean separate class for performing validation. And that's the great part about it. Validation is separate from the view model. It can be also unit tested separately and in isolation. Believe me, once you have tried it you will never ever write a single data annotation anymore. – Darin Dimitrov Nov 03 '11 at 15:29
  • @roryok, you must have missed something if it doesn't work for you. I have tested the code and it worked. Create a new ASP.NET MVC 3 project, and paste the code exactly as shown in my answer. You probably have a `Required` attribute or something else on your view model. – Darin Dimitrov Nov 03 '11 at 15:32
  • Got FluentValidation working, I have to admit it's very nice to use. Thanks for the help! – roryok Nov 07 '11 at 09:44
1

You can create a ConditionalAttribute.

ASP.net MVC conditional validation

ASP.NET MVC Conditional validation

Community
  • 1
  • 1
jrummell
  • 42,637
  • 17
  • 112
  • 171