views:

726

answers:

3

I have a controller with two actions:

[AcceptVerbs("GET")]
    public ActionResult Add()
    {
        PrepareViewDataForAddAction();
        return View();
    }


[AcceptVerbs("POST")]
    public ActionResult Add([GigBinderAttribute]Gig gig, FormCollection formCollection)
    {
        if (ViewData.ModelState.IsValid)
        {
            GigManager.Save(gig);
            return RedirectToAction("Index", gig.ID);
        }
        PrepareViewDataForAddAction();
        return View(gig);
    }

As you can see, when the form posts its data, the Add action uses a GigBinder (An implemenation of IModelBinder)

In this binder I have:

 if (int.TryParse(bindingContext.HttpContext.Request.Form["StartDate.Hour"], out hour))
        {
           gig.StartDate.Hour = hour;
        }
        else
        {
            bindingContext.ModelState.AddModelError("Doors", "You need to tell us when the doors open");
        }

The form contains a text box with id "StartDate.Hour".

As you can see above, the GigBinder tests to see that the user has typed in an integer into the textbox with id "StartDate.Hour". If not, a model error is added to the modelstate using AddModelError.

Since the gigs property gigs.StartDate.Hour is strongly typed, I cannot set its value to, for example, "TEST" if the user has typed this into the forms textbox.

Hence, I cant set the value of gigs.StartDate.Hour since the user has entered a string rather than an integer.

Since the Add Action returns the view and passes the model (return View(gig);) if the modelstate is invalid, when the form is re-displayed with validation mssages, the value "TEST" is not displayed in the textbox. Instead, it will be the default value of gig.StartDate.Hour.

How do I get round this problem? I really stuck!

+1  A: 

Could you do this in your PrepareViewDataForAddAction method?..

if (!ViewData.ModelState.IsValid)
    {
        ViewData["StartDate.Hour"] = "Error";
    }

The other fields on the form will still populate based on the properties of the Gig object.

Dominic Betts
+1  A: 

I think you need to do some basic client side validation first. don't allow it to post to the server.

Qun Wang
+1  A: 

I think the problem is that your ViewModel does not match closely enough with your View. It's really important in MVC that your ViewModel matches your View as closely as possible.

In your ViewModel you're assuming an integer, but in your View you're using a TextBox to render the property, which will allow any kind of text. There's a mismatch here and the difficulties you are experiencing trying to map them is a symptom of the mismatch.

I think you should either:
1. Change the type of the ViewModel property to string and then do validation in your controller to ensure the string entered is actually a number or:
2. Change the control that the View renders to a control that will only allow a number to be entered via a custom control or Javascript validation (as @Qun Wang recommends)

Personally, I'd recommend option 1. That way the ViewModel is not dependent on the View implementation.

Mac