49

I have a multi-step file import process. I have a hidden form input in my view that I am trying to populate with the "CurrentStep" from the view model.

<% = Html.HiddenFor(model => model.CurrentStep) %>

CurrentStep is an Enum and I always get the default value rather than the one I provided to the view model. on the other hand this gets me the correct value:

<p><% = Model.CurrentStep %></p>

I realise I could just hand code the hidden input but I want to know: what am I doing wrong? Is there a better way to keep track of the current step between POSTs?

Kiquenet
  • 14,494
  • 35
  • 148
  • 243
user427875
  • 1,245
  • 2
  • 11
  • 16

3 Answers3

77

What you are doing wrong is that you are trying to modify the value of a POSTed variable in your controller action. So I suppose you are trying to do this:

[HttpPost]
public ActionResult Foo(SomeModel model)
{
    model.CurrentStep = Steps.SomeNewValue;
    return View(model);
}

and html helpers such as HiddenFor will always first use the POSTed value and after that the value in the model.

So you have a couple of possibilities:

  1. Remove the value from the modelstate:

    [HttpPost]
    public ActionResult Foo(SomeModel model)
    {
        ModelState.Remove("CurrentStep");            
        model.CurrentStep = Steps.SomeNewValue;
        return View(model);
    }
    
  2. Manually generate the hidden field

    <input type="hidden" name="NextStep" value="<%= Model.CurrentStep %>" />
    
  3. Write a custom helper which will use the value of your model and not the one that's being POSTed

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 2
    Thanks. I thought the whole point of a view model was to pass complex (more complex than should be contained in ViewData at least) information to the View. So the View isn't using the View Model I'm passing to it? It's just getting whatever was in the POST vars? That doesn't make much sense to me. – user427875 Jan 29 '11 at 23:38
  • 4
    Thanks, great answer. For (1) you can also use ModelState.Clear() to remove all values. – tkerwood Jun 29 '11 at 04:03
  • 2
    @Darin Dimitrov: we had a similar problem, and your post helped us a lot. But what we do not get, and maybe you have the answer, is WHY it does this. Why does it take the value from the POST, even though you seemingly override it with the value from the model in the .cshtml. – Garth Marenghi Aug 02 '12 at 08:56
  • 2
    Because that's the behavior a user will want in 99.99% of the cases: he will want to see the same values that he entered in the form after postback, not some new value that you assigned in the POST action. Just imagine that a user types some value into a textbox, then some model error occurs and you modify this value and redisplay the form. The user might not pay attention that the value he entered was modified. That's the reason why this is the default behavior: because you are not supposed to modify what the user sent you to the POST action in most cases. – Darin Dimitrov Aug 02 '12 at 15:43
  • 11
    @DarinDimitrov. Surely this is not for MS to decide. If I change the posted value, I am doing so deliberately. Why does MVC need to override my preference? What benefit does this feature have? Apologies if I'm overlooking something really obvious! – Paul Fleming Aug 25 '12 at 14:56
  • @flem, I already explained the reasoning in my answer: users will want to see the same values they entered when they filled-in the form after the postback. – Darin Dimitrov Aug 25 '12 at 22:30
  • @DarinDimitrov can you provide a link of the documentation of MVC where this is discribed. Thanks in advance – Zinov Dec 15 '16 at 17:24
  • What is the rationale behind this? There is surely an explanation – yonexbat Jun 23 '17 at 10:32
  • The OP asked about Html.HiddenFor. A hidden html field. Why/How are users seeing this data? I often "hide' Id's or other data that is not essential to the user, but makes my life easier. – t.durden Jul 13 '18 at 17:10
  • Use ModelState.Remove for the specific field, again not recommended. https://stackoverflow.com/questions/30757343/mvc-hidden-field-via-html-helper-in-form-post-issue?noredirect=1&lq=1 – Kiquenet Nov 23 '18 at 05:18
  • @t.durden Users are not seeing the data, the application is. And after the first post, the application modifies the value of that hidden field and sends it back in the same view. Posting the form again sends the originally posted value, not the updated value. – Suncat2000 Apr 22 '21 at 15:58
9

My solution was to use Darin's second option, because option 1 (clearing from the model state) means hard coding a string (and the naming convention can be tricky with complex models), and wanted to avoid option 3 because I already have so many custom helpers.

<input type="hidden" name="@Html.NameFor(x => Model.SomeId)" value="@Model.SomeId" />

Just a reminder that you can use Html.NameFor to keep things clean.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Zac
  • 1,722
  • 1
  • 19
  • 22
2

Make sure you model property has a "set" operator.

This won't get updated on post-back:

@Html.HiddenFor( m => m.NoSeq)

public Class MyModel
{
    int _NoSeq;
    public NoSeq
    {
        get { return _NoSeq };
    }
}
gharel
  • 413
  • 4
  • 13
  • This was the answer that worked for me! My viewmodel had "private set" on several properties, and they were showing up as null on the httppost. (I am setting them inside the parameterized constructor before rendering the view). Just changing from {get; private set;} to {get; set;} did the job. – Krishna Gupta Sep 11 '19 at 10:51