Alex
Alex

Reputation: 23300

Make a complex typed property required in an MVC4 form

I can't figure out how to "customize" the rules for the [Required] attribute when I stick it to a custom typed property. Code looks like this:

public class MyProp
{
    public Guid Id {get;set;}
    public string Target {get;set;}
}
public class MyType : IValidatableObject
{
    public string Name {get;set;}
    public MyProp Value {get;set;}

    private MyType()
    {
        this.Name = string.Empty;
        this.Value = new MyProp { Id = Guid.Empty, Target = string.Empty };
    }
    public MyType(Guid id) : this()
    {
        this.Value.Id = id;
        // Fill rest of data through magic
    }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(this.Value.Id == Guid.Empty)
            yield return new ValidationResult("You must fill the property");
    }
}

This model shows up in forms (through its own EditorTemplate) as a textbox with a button which allows for selection from a list (the backing data is a Dynamics CRM 2011 Environment, and this model is actually aimed to represent a lookup attribute).

public class MyModel
{
    // Many props

    [Required] // This one is enforced correctly
    public string MyString {get;set;}

    [Required] // This one isn't
    public MyType MyData {get;set;}

    public MyModel() { this.MyData = new MyType(); }
}

The resulting view shows the field (empty, of course). User can only input data by clicking the field and choosing from a list (a jquery dialog takes care of this, and it already works).

The IValidatableObject interface sounds promising but the code doesn't seem to be ever invoked.

In the controller, I'm simply doing

[HttpPost]
public ActionResult MyAction(FormCollection data)
{
    if (!ModelState.IsValid) return View();
    // magic: handle data
}

What am I missing ? I probably misunderstood the IValidatableObject interface usage ?

Upvotes: 0

Views: 197

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038950

Your controller action should take the view model as parameter instead of weakly typed FormCollection which has absolutely no relation to your model (and its validation rules):

[HttpPost]
public ActionResult MyAction(MyModel model)
{
    if (!ModelState.IsValid) 
    {
        return View();
    }

    // magic: handle model
}

Now the default model binder is going to be invoked in order to bind the view model from the request and evaluate any validation logic you might have in this model.

How do you expect from your code, ASP.NET MVC, to ever know that you are working with this MyModel class? You absolutely never used it in your POST action, so you cannot expect to have any validation on it.

Once you start using view models you should forget about weakly typed collections such as FormCollection and start working with those view models.

Upvotes: 1

Related Questions