David
David

Reputation: 15360

MVC Complex Model Binding

I would like to do complex validation on my form that contains a list of objects.

My form contains a list of, let's say, MyObjects. MyObject consists of a double amount and a MyDate which is just a wrapper around DateTime.

public class MyObject
{
    public MyDate Date { get; set; } //MyDate is wrapper around DateTime
    public double Price { get; set; }
}

The form...

<input type="text" name="myList[0].Date" value="05/11/2009" />
<input type="text" name="myList[0].Price" value="100,000,000" />

<input type="text" name="myList[1].Date" value="05/11/2009" />
<input type="text" name="myList[1].Price" value="2.23" />

Here is my Action

public ActionResult Index(IList<MyObject> myList)
{
   //stuff
}

I want to allow the user to enter in 100,000,000 for a Price and for the custom model binder to strip the ',' so it can convert to a double. Likewise, I need to convert the 05/11/2009 to a MyDate object. I thought about creating a MyObjectModelBinder but dont know what to do from there.

ModelBinders.Binders[typeof(MyObject)] = new MyObjectModelBinder();

Any help appreciated.

Upvotes: 0

Views: 2421

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038780

Here's a sample implementation of a custom model binder:

public class MyObjectModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // call the base method and let it bind whatever properties it can
        var myObject = (MyObject)base.BindModel(controllerContext, bindingContext);
        var prefix = bindingContext.ModelName;
        if (bindingContext.ValueProvider.ContainsKey(prefix + ".Price"))
        {
            string priceStr = bindingContext.ValueProvider[prefix + ".Price"].AttemptedValue;
            // priceStr = 100,000,000 or whatever the user entered
            // TODO: Perform transformations on priceStr so that parsing works
            // Note: Be carefull with cultures
            double price;
            if (double.TryParse(priceStr, out price))
            {
                myObject.Price = price;
            }
        }

        if (bindingContext.ValueProvider.ContainsKey(prefix + ".Date"))
        {
            string dateStr = bindingContext.ValueProvider[prefix + ".Date"].AttemptedValue;
            myObject.Date = new MyDate();
            // TODO: Perform transformations on dateStr and set the values 
            // of myObject.Date properties
        }

        return myObject;
    }
}

Upvotes: 2

Jarrett Meyer
Jarrett Meyer

Reputation: 19573

You're definitely going down the right path. When I did this, I made an intermediate view model that took Price as a string, because of the commas. I then converted from the view model (or presentation model) to a controller model. The controller model had a very simple constructor that accepted a view model and could Convert.ToDecimal("12,345,678.90") the price value.

Upvotes: 0

Related Questions