21

The Model:

class Address
{
    public string City { get; set; }
    public string Zip { get; set; }
}

The Controller:

[HttpPost]
public ActionResult GetAddress(Address model)
{
    if (!String.IsNullOrEmpty(model.Zip))
    {
        model.City = GetCityByZip(model.Zip);
    }
    return View(model);
}

The View:

<div class="formrow">
    @Html.LabelFor(model => model.City)
    @Html.TextBoxFor(model => model.City) 
    @Html.ValidationMessageFor(model => model.City)
</div>
<div class="formrow">
    @Html.LabelFor(model => model.Zip)
    @Html.TextBoxFor(model => model.Zip) 
    @Html.ValidationMessageFor(model => model.Zip)
</div>

The problem is whenever the city is being modified, it never gets reflected on the view. During debugging, the model.City contains the correct value but it doesn't show up on view. Even something as simple as @Html.TextBoxFor(model => model.City) doesn't display the correct model.City value.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
xar
  • 1,429
  • 2
  • 17
  • 29
  • 1
    the model you have posted and the one you are passing as a parameter are not the same. Have you referenced the wrong model? – Tommy Aug 07 '12 at 02:07
  • @MarkOreta : Updated the question, added the view – xar Aug 07 '12 at 02:14
  • @Tommy My bad. The class name was a mistake. I just updated the question. I was renaming the class just for questioning purposes. – xar Aug 07 '12 at 02:22

2 Answers2

50

HtmlHelpers get the model values from the model state and not the model when you update and return the model. In order to update and return the model, add this line of code in your post method:

ModelState.Clear();

or you could set the value of city in the ModelState itself:

ModelState["City"].Value = GetCityByZip(model.Zip);
Tommy
  • 39,592
  • 10
  • 90
  • 121
  • That makes sense. Is this the case on all version of MVC? – xar Aug 07 '12 at 02:53
  • As far as I know, yes. The modelstate is what tells the view about valid/invalid properties, etc. – Tommy Aug 07 '12 at 03:46
  • 6
    This must be implemented on Aprils Fools day... Thanks. (Just looked 2 hours on my server side and client side code. I should have starting googling earlier...) – Christian Gollhardt Sep 18 '17 at 04:27
  • 3
    @ChristianGollhardt - been there man. This was one of those "features" that I ran into and shall never forget because of the lost time :) – Tommy Sep 18 '17 at 10:52
  • Finally found this after almost two days of thinking I was going crazy. Thanks so much! – Zach Jan 18 '21 at 15:24
  • Also found [this post](https://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes) from Rick Strahl which was also helpful. – Zach Jan 18 '21 at 16:00
4

As Tommy noted, this is, somewhat counterintuitively, the correct behavior since form data submitted on post gets first priority when binding the data to the returned view. This makes some sense as the user is likely to have made a validation error when re-returning the same view and gets to resume their form entry as is without the problems of losing form input when restoring a page

One other option is to manually insert the value for the input

So instead of this:

@Html.TextBoxFor(model => model.City)

Do this instead:

<input type="text" name="City" value="@Model.City" />

* which will grab the value directly off the model

Or even better:

<input type="text" value="@Model.City"
       name="@Html.NameFor(model => model.City)"
       id="@Html.IdFor(model => model.City)" />

*Note: this won't bring in data-val attributes. If you're using them on this property for client side validation, you'll need to build list of data validation attributes for a given element

Additional Resources

KyleMit
  • 30,350
  • 66
  • 462
  • 664