3

I have a peculiar problem - I have a ViewModel with a List in it used to display a list of images:

public List<int> TrackerKeys { get; set; }

This is used in two places in the page:

@for (int i = 0; i < Model.TrackerKeys.Count(); i++)
{ 
    @Html.GenerateImage(PageModes.Http, Model.TrackerKeys[i])
}

And also

@for (int i = 0; i < Model.TrackerKeys.Count(); i++)
{ 
    @Html.HiddenFor(model => model.TrackerKeys[i])
}

This is sat inside a form - when the form is submitted, if a validation error occurs, the TrackerKeys property is updated with a new random set of numbers. I am passing the previous list back to ensure the user does not see the same image again.

Tracker keys is correctly set and passed back to the View.

My problem is:

The images correctly display the new images based on the new values in the list

However

The values of the hidden fields are not updated to the new values - they retain the original values.

Any ideas? Is this a bug with MVC3? any input would be greatly appreciated. Thank you.

Edit

Html before submission:

<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/26.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/33.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/8.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/30.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/6.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/18.gif" width="35" />  

And

<input id="TrackerKeys_0_" name="TrackerKeys[0]" type="hidden" value="26" />
<input id="TrackerKeys_1_" name="TrackerKeys[1]" type="hidden" value="33" />
<input id="TrackerKeys_2_" name="TrackerKeys[2]" type="hidden" value="8" />
<input id="TrackerKeys_3_" name="TrackerKeys[3]" type="hidden" value="30" />
<input id="TrackerKeys_4_" name="TrackerKeys[4]" type="hidden" value="6" />
<input id="TrackerKeys_5_" name="TrackerKeys[5]" type="hidden" value="18" />

And after the post: correct new values here...

<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/16.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/20.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/11.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/19.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/26.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/15.gif" width="35" /> 

and... still retaining old values...

<input id="TrackerKeys_0_" name="TrackerKeys[0]" type="hidden" value="26" />
<input id="TrackerKeys_1_" name="TrackerKeys[1]" type="hidden" value="33" />
<input id="TrackerKeys_2_" name="TrackerKeys[2]" type="hidden" value="8" />
<input id="TrackerKeys_3_" name="TrackerKeys[3]" type="hidden" value="30" />
<input id="TrackerKeys_4_" name="TrackerKeys[4]" type="hidden" value="6" />
<input id="TrackerKeys_5_" name="TrackerKeys[5]" type="hidden" value="18" />

controller action

[HttpPost]
    public ActionResult EmployerRegistration(EmployerViewModel Model)
    {
        if (ModelState.IsValid)
        {
            //do bits here
        }

        Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys); 
        return View(Model);
    }
Carl
  • 33
  • 1
  • 4

1 Answers1

13

Is this a bug with MVC3?

Oh no, that's by design and it is how all HTML helpers work in ASP.NET MVC. HTML helpers (such as Html.TextBoxFor, Html.HiddenFor, ...) first look at the ModelState when binding their values and after that they look at the Model. So if you intend to modify some property of your model inside the POST controller action and this property was part of the POST body, you will have to remove the old values from the ModelState first:

[HttpPost]
public ActionResult EmployerRegistration(EmployerViewModel Model)
{
    if (ModelState.IsValid)
    {
        //do bits here
    }

    // we clear all values from the modelstate => this will ensure that
    // the helpers will use the values from the model and not those that
    // were part of the initial POST.
    ModelState.Clear();
    Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys); 
    return View(Model);
}

or if you don't want to clear the entire ModelState (as this will remove all values and any corresponding modelstate errors that might be associated to them) you could remove only those keys that you actually modify:

[HttpPost]
public ActionResult EmployerRegistration(EmployerViewModel Model)
{
    if (ModelState.IsValid)
    {
        //do bits here
    }

    for (var i = 0; i < Model.TrackerKeys.Count; i++)
    {
        ModelState.Remove(string.Format("TrackerKeys[{0}]", i));
    }
    Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys); 
    return View(Model);
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928