1

I have a very simple example I am trying to create. On a button click, I am trying to increment a number in the view model, that's it. The entirety of the code is here:

View Model

public class CustomerOverallViewModel
{
    public int currentState { get; set; }
}

Controller

public class WorkflowController : Controller
{
    public ActionResult Index()
    {
        CustomerOverallViewModel viewModel = new CustomerOverallViewModel();
        viewModel.currentState = 0;

        return View(viewModel);
    }

    [HttpPost]
    public ActionResult Index(CustomerOverallViewModel viewModel)
    {
        viewModel.currentState++;

        return View(viewModel);
    }
}

View

@model AgentWebsite.ViewModels.CustomerOverallViewModel

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>

@using (Html.BeginForm())
{
    <div>
        called from the WorkflowController -> Index method

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

        <input type="submit" class="btn btn-success" value="Back" />
        <input type="submit" class="btn btn-success" value="Next" />
    </div>
}

When I click the buttons, currentState is always being reset, no matter what. What am I missing? How can I use variables that are not on the page?

Carrie Kendall
  • 11,124
  • 5
  • 61
  • 81
James F
  • 535
  • 9
  • 26
  • 1
    Html helpers use the value from `ModelState` (not from the model properties) so when you post, the current value of `currentState` is added to model state. Incrementing its value does not change the value of `ModelState`. The correct appeoach is to follow the PRG pattern, but you can use `ModelState.Clear()` before incremeting the value. [Refer this answer](http://stackoverflow.com/questions/26654862/textboxfor-displaying-initial-value-not-the-value-updated-from-code/26664111#26664111) for an explanation of the behavior –  Apr 30 '15 at 23:10

1 Answers1

0

Here you go:

Controller:

public class WorkflowController : Controller
{
    public ActionResult Index()
    {
        var viewModel = new CustomerOverallViewModel();
        
        return View(viewModel);
    }

    
    [HttpPost]
    public ActionResult Index(CustomerOverallViewModel viewModel)
    {
        viewModel.currentState++;

        //Per feedback
        ModelState.Clear();

        return View(viewModel);
    }
}

View:

@model AgentWebsite.ViewModels.CustomerOverallViewModel

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>

@using (Html.BeginForm())
{
    <div>
        called from the WorkflowController -> Index method

        @Html.Raw(Model.currentState)
        @Html.HiddenFor(m => m.currentState)

        <input type="submit" class="btn btn-success" value="Back" />
        <input type="submit" class="btn btn-success" value="Next" />
    </div>
}

I personally would use JavaScript and make some Ajax calls to your action.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Rogala
  • 2,679
  • 25
  • 27
  • Never pass a model to a GET method! (a) If the model contains properties that are complex objects or collections, binding will fail and the properties will be null. (b) You can easily exceed the query sting limit and throw an exception. (c) It creates a really ugly query string. The correct approach is `[HttpGet]public ActionResult Index(int currentState)` where the POST method passes the incremeted value of `currentState` to the GET method and a new instance of `CustomerOverallViewModel` is initialized with the value of the parameter. –  May 01 '15 at 01:15
  • I completely agree with you, but in this case it is a very small model that is well defined. Honestly, I believe the best approach is to utilize Ajax and never leave the view. Another option is to cache the state, in the POST action, and get the value from the cache. This way, you could keep the state for a specified period of time. – Rogala May 01 '15 at 03:38
  • I suspect OP has only shown part of the model and the controller (as its shown it would be completely pointless - just one hidden field in a form!) And whether ajax is used or not is irrelevant to the question - which is why the changed property value is not being updated in the view. –  May 01 '15 at 03:48
  • I edited to clear the modelstate. I don't think passing the currentState to the GET method is a good approach either. To your points, his model is probably more complex. – Rogala May 01 '15 at 03:55
  • What makes you think passing the value of property `currentState` is not a good approach? You either do that or you save the model and pass its ID to the GET method and retrieve the model from the repository again. That's how the PRG pattern works. –  May 01 '15 at 04:00
  • To your point, it all comes back to complex objects. So the currentState of his model is propA = {"C", "A"}, propB = 2, propC = "text", etc. If someone wants to keep the state of all the properties for a complex model, then we are back to the initial approach, which you pointed out would cause binding issues. CurrentState was never stated as an ID, so passing currentState doesn't imply that the ID was passed or the state was stored in the repository. Caching could be a good option: http://www.c-sharpcorner.com/UploadFile/ff2f08/caching-in-mvc-application-with-entity-framework-using-query/ – Rogala May 01 '15 at 04:26
  • I'm referring to OP model which has a property named `currentState` (not the state of the model!) –  May 01 '15 at 04:31
  • You are correct in that I would be looking to make the model more complex, including have a complex object as part of it, something like `class ViewModel { string state; ClassA classA; ClassB classB; }` – James F May 01 '15 at 11:42