Question

I'm trying to get my head around why (data annotation) validation errors are triggering when the page first loads, prior to any Submit/Posts. But more importantly how to fix this.

Reading SO and the interwebs, the reason seems to be model binding triggers validation errors for the view model properties, prior to the view model properties having values. I'm not sure if this is true and what is actually happening, but it sounds legit.

And I've read two workarounds, which sound a bit hacky: 1. Use the ModelState.Clear in the controller action method on the inital page load, OR 2. Initialiase the view model properties in an empty view model constructor. (Yet to confirm this technique)

Both these techniques sound like work-arounds. I'd rather understand what is happening and design my code appropriately.

Have others come across this issue? And if so, what are you doing?

Code as requested. Below you can see the Data Annotation validation on Property1 which is being triggered on initial requests, i.e. first page load.

Controller action method (refactored for simplicity):

    public ActionResult Index([Bind(Include = Property1, Property2, Property3, vmclickedSearchButton)] IndexVM vm, string Submit)
    {
        bool searchButtonClicked = (Submit == "Search") ? true : false;
        if (searchButtonClicked)
        {
            PopulateUIData(vm); // Fetch data from database and pass them to VM

            if (ModelState.IsValid)
            {
                vm.clickedSearchButton = true; // Used in the vm to avoid logic execution duing initial requests

                DoWork(vm);
                }
            }

            return View(vm);
        }

        // Inital request
        IndexVM newVM = new IndexVM();
        PopulateUIData(newVM); // Fetch data from database and pass to VM

        return View(newVM);
    }

Design note: Ideally I would like to sepate the rendering and submiting logic into separate action methods. I.e. rendering within a [HttpGet]Index() action method, and submitting within a [HttpPost]Index() action method. But since I'm using ForMethod.Get in the View as this method is used for searching functionality, I can only use a [HttpGet] Index action method.

View Model (refactored for simplicity):

public class IndexVM
{
    // DropDownLists
    public IEnumerable<SelectListItem> DDLForProperty1 { get; set; }
    public IEnumerable<SelectListItem> DDLForProperty2 { get; set; }
    public IEnumerable<SelectListItem> DDLForProperty3 { get; set; }

    [Required]
    public int? Property1 { get; set; }       
    public int? Property2 { get; set; }
    public int? Property3 { get; set; }
    public bool vmclickedSearchButton { get; set; }
}

Note: The view model is very simple. It contains drop down lists, selected properties for the DDLs, and a validation rule on one of the properties.

Adding a constructor to the view model and initialising the property workaround:

public IndexVM()
{
  this.Property1 = 0;
}
Was it helpful?

Solution

The problem is that you are sending an invalid model to your view.

  1. The Property1 in your model is being required and is a nullable int. This does not explain why validation executes, but why the model is invalid.

  2. Your action method is executing the validation during initial load. Model binding will execute validation regardless of http method (get or post).

Since you are requiring Property1 (int?) to NOT be null, by definition your model becomes invalid when it is instantiated. There are several ways to handle this (not sure which is most appropriate though)

  1. Create separate methods for HttpGet and HttpPost in your controller. Do not implement binding for HttpGet.

  2. Use a default value (as you have done already)

  3. Modify the model so that Property1 is not nullable (i.e. int).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top