下拉框OnChange提交后视图未绑定更新模型值问题排查
问题描述
我做了一个展示邮件模板的简单视图,用下拉列表选择要操作的模板。初始选第一个模板时绑定正常,但下拉框变更触发表单提交后,虽然能命中控制器断点、获取到选中的模板并返回视图,可视图还是显示初始的数值(切换到Template2后截图显示的还是Template1的内容)。应用里多处有类似情况,显然我对机制理解有误,为什么return View(t)时不显示更新后的模型值?我期望切换后显示Template2的对应内容。
相关代码
模型代码
namespace WebApplication1.Models { public enum TemplateList { Template1, Template2, Template3 } public class EmailTemplate { public TemplateList TemplateId { get; set; } public string Subject { get; set; } public string Body { get; set; } public EmailTemplate() { } public static List<EmailTemplate> GetTemplates() { List<EmailTemplate> templates = new List<EmailTemplate>(); templates.Add( new EmailTemplate { TemplateId = TemplateList.Template1, Body = "This is template #1", Subject = "Template 1" }); templates.Add( new EmailTemplate { TemplateId = TemplateList.Template2, Body = "This is template #2", Subject = "Template 2" }); templates.Add( new EmailTemplate { TemplateId = TemplateList.Template3, Body = "This is template #3", Subject = "Template 3" }); return templates; } } }
视图代码
@model EmailTemplate @{ ViewData["Title"] = "Email Edit"; } <div class="container"> <h5 class="text-centered">Edit Email Template</h5> @using (Html.BeginForm("Email", "Home", FormMethod.Post)) { <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="row"> <label class="control-label col-sm-1">Template</label> @Html.DropDownListFor(m => m.TemplateId, new SelectList(Enum.GetNames(typeof(TemplateList))), "", new { @class = "form-control col-sm-2", @onchange = "this.form.submit();"}) <span asp-validation-for="TemplateId" class="text-danger"></span> </div> <div class="row"> <label class="control-label col-sm-1">Subject</label> @Html.TextBoxFor(m => m.Subject, new { @class = "form-control col-sm-10"}) <span asp-validation-for="Subject" class="text-danger"></span> </div> <div class="row"> <label class="control-label col-sm-1">Body</label> @Html.TextAreaFor(m => m.Body, 20, 70, new { @class = "form-control col-sm-10"}) <span asp-validation-for="Body" class="text-danger"></span> </div> <div class="container, text-center"> <input type="submit" asp-action="Update" value="Update" class="btn btn-primary" /> <input type="submit" asp-action="Index" value="Cancel" class="btn btn" /> </div> } </div>
控制器代码
public IActionResult Email() { List<EmailTemplate> templates = EmailTemplate.GetTemplates(); EmailTemplate t = templates.ElementAt(0); return View(t); } [HttpPost] public IActionResult Email(EmailTemplate template) { List<EmailTemplate> templates = EmailTemplate.GetTemplates(); EmailTemplate t = templates.Find(m => m.TemplateId == template.TemplateId); return View(t); }
原因分析
ASP.NET MVC/Razor的视图辅助方法(比如TextBoxFor、TextAreaFor)优先从ModelState中读取值,而不是你传入的模型对象。当你提交表单时,表单里的旧值(比如初始的Subject和Body)会被自动存入ModelState,即使你在控制器里获取了新的模板对象并传入视图,视图还是会显示ModelState里的旧值,导致看起来没有更新。
解决方案
有两种可行的解决方式:
方式一:清除ModelState(直接简单)
在返回视图前,调用ModelState.Clear()清空旧的状态,这样视图就会从你传入的新模型对象中读取值:
[HttpPost] public IActionResult Email(EmailTemplate template) { List<EmailTemplate> templates = EmailTemplate.GetTemplates(); EmailTemplate t = templates.Find(m => m.TemplateId == template.TemplateId); ModelState.Clear(); // 清除旧的ModelState值 return View(t); }
方式二:使用PRG模式(更规范)
PRG(Post/Redirect/Get)是避免重复提交、解决状态残留的标准模式。提交后重定向到Get方法,传入选中的模板ID,让Get方法重新加载对应模板并返回视图:
[HttpPost] public IActionResult Email(EmailTemplate template) { // 重定向到Get版本的Email方法,传入选中的TemplateId return RedirectToAction(nameof(Email), new { templateId = template.TemplateId }); } // 修改Get方法,支持接收TemplateId参数 public IActionResult Email(TemplateList? templateId = null) { List<EmailTemplate> templates = EmailTemplate.GetTemplates(); EmailTemplate t; if (templateId.HasValue) { t = templates.Find(m => m.TemplateId == templateId.Value); } else { t = templates.ElementAt(0); } return View(t); }
验证
修改后重新运行,切换下拉框时,视图就会正确显示选中模板的Subject和Body内容了。
内容的提问来源于stack exchange,提问作者Dave Alessi
相关产品推荐
相关产品推荐

