Search code examples
asp.net-mvcasp.net-coremodel-bindingtag-helpersasp.net-core-tag-helpers

ASP.NET Core TagHelper input asp-for rendering value attribute sticks with original value in request model


I build up a simple web page with asp.net core 3.1 for reproducing problem. There are 2 action in same HomeController.cs.

one for rendering a html form, another for receiving form post data then rendering form again.

public class HomeController : Controller
{
        // rendering form
        [HttpGet]
        public IActionResult Index()
        {
            var myForm = new MyForm();
            myForm.Id = 100;
            return View("index", myForm);
        }

        // after form post, rendering form with updated value.
        [HttpPost]
        public IActionResult Index(MyForm m)
        {
            m.Id = 200;
            return View("index", m);
        }
}

I use asp.net core tag helper in index.cshtml

@model MyForm

<div>
    <form asp-controller="Home" asp-action="Index" method="post">
        <span>@Model.Id</span>
        <input type="text" asp-for="@Model.Id"/>
        <button type="submit">Submit</button>
    </form>
</div>

When I GET /index, everything is fine, both @Model.Id in span and input value are 100

Http Response for Get /index

<form method="post" action="/">
        <span>100</span>
        <input type="text" data-val="true" 
               data-val-required="The Id field is required."
               id="Id" name="Id" value="100">
        <button type="submit">Submit</button>
</form>

But When I post form, I expected both value in span and input should be 200, but it is 200 in span and 100 in form

Http Response for POST /index with 100 in input

<form method="post" action="/">
        <span>200</span>
        <input type="text" data-val="true" 
               data-val-required="The Id field is required."
               id="Id" name="Id" value="100">
        <button type="submit">Submit</button>
</form>

I guess "asp-for" choose to read data from original ModelState, which is built while modelbinding. Instead of read ViewModel passed for view Razor rendering.

Does Anyone know why tag helper asp-for behavior like this? How should I avoid this behavior and make asp-for use the Model I passed in View("index", m)


Solution

  • I guess "asp-for" choose to read data from original ModelState

    Yes, this is by design. Default TagHelper display ModelState value not Model.

    If you want asp-for display the updated model value, you could add ModelState.Clear(); in your post action.

    public IActionResult Index(MyForm m)
    {
        ModelState.Clear();
        m.Id = 200;
        return View("index", m);
    }