1

I am using MVC to display a simple form in a view:

ViewModel:

public class CreateSaleViewModel
{
    public string OrderId { get; set; }

    public decimal TotalAmount { get; set; }

    public bool ShowInstoreConfirmDetails { get; set; }
}

Controller action:

[HttpGet]
public IActionResult CreateSale()
{
    return View(new CreateSaleViewModel());
}

View:

@model CreateSaleViewModel
<form asp-controller="Sales" asp-action="CreateSale" method="post">

  <input asp-for="OrderId" />
  <input asp-for="TotalAmount" />

  <button type="submit" name="CreateSale" id="CreateSale">
    button
  </button>

</form>

I then post to a new view, where the same details need to be entered. To do this I store the old values in hidden inputs and provide another form to re-enter the details.

ViewModel:

public class ConfirmDetailsViewModel
{
    public string OrderId { get; set; }

    public decimal TotalAmount { get; set; }

    public string ConfirmOrderId { get; set; }

    public decimal ConfirmTotalAmount { get; set; }
}

Controller:

[HttpPost("Confirmdetails")]
public IActionResult ConfirmDetails(CreateSaleViewModel model)
{
 var viewModel = new ConfirmDetailsViewModel
 {
    ConfirmOrderId = model.OrderId,
    ConfirmTotalAmount = model.TotalAmount,
    OrderId = string.Empty,
    TotalAmount = 0.0m
  };

  return View("ConfirmDetails", viewModel);
}

View:

@model ConfirmDetailsViewModel

<form asp-controller="Sales" asp-action="Summary" method="post">
  <input type="hidden" value="@Model.ConfirmOrderId" id="OrderIdConfirm" />

  <input type="hidden" value="@Model.ConfirmTotalAmount" id="TotalAmountConfirm" />

  <input type="hidden" value="@Model.OrderId" id="banana" />


  <input asp-for="OrderId" />

  <input asp-for="TotalAmount" />

  <button  type="submit" name="CreateSale" id="CreateSale">
    button
  </button>
</form>

My problem is on the confirmdetails view orderId and TotalAmount retain the values that were posted from the previous page.

I have debugged the controller and can see the ConfirmOrderId and ConfirmTotalAmount properties have the correct values, and also OrderId and TotalAmount are empty strign and 0 respectively.

Even stranger is that

<input type="hidden" value="@Model.OrderId" id="banana" />

Has the correct value of "".

Does anyone know what is causing this issue?

Fran
  • 6,440
  • 1
  • 23
  • 35
DavidB
  • 2,566
  • 3
  • 33
  • 63
  • 3
    your inputs that use the asp-for are creating input elements with the correct name attributes for submission. All of you hand written hidden inputs do not have a name attribute. form submission is based on the name attribute, not the id. Add a name attribute that matches the property name you are trying to post. or use the correct tag helper – Fran Jul 03 '18 at 15:19
  • Thanks, that's a good point. I'm not really too concerned about the submission of that form at this point, it is the rendering of the view and the unexpected binding of the wrong values that I need a solution for, – DavidB Jul 03 '18 at 15:29
  • The issue is related to `ModelState` (refer [this answer](https://stackoverflow.com/questions/26654862/textboxfor-displaying-initial-value-not-the-value-updated-from-code/26664111#26664111) for an explanation). But are generating a new view, so do not return the view - follow the PRG pattern and redirect to a method that displays your new view –  Jul 03 '18 at 22:37

1 Answers1

2

MVC stores the posted back values in ModelState.

These values are used by default in @Html helpers - as a convenience. This allows the values of hidden form fields to be preserved through postbacks, even if they don't have properties in the view-model.

Unfortunately what is usually a convenience turns into a headache, if you try to modify the model's properties within the action. Helpers take their values from ModelState, ignoring the updated view-model.

To solve this, call ModelState.Clear()

  • removes all the posted back values from ModelState
  • the helpers will now use the values from the view-model.

Controller:

    [HttpPost]
    public IActionResult ConfirmDetails(CreateSaleViewModel model)
    {
        var viewModel = new ConfirmDetailsViewModel
        {
            ConfirmOrderId = model.OrderId,
            ...
        };

        ModelState.Clear();        // force asp-helpers to use the updated model's values

        return View("ConfirmDetails", viewModel);
    }