动态PartialView在浏览器进退及表单验证后消失的解决方案求助
首先得明确这两个问题的核心原因:动态创建的DOM元素既没有被服务器端的Model追踪,也没有被浏览器的历史状态记录,所以无论是服务器返回验证视图,还是浏览器前进后退,都会回到初始页面状态。下面分步骤给你解决:
一、解决表单验证返回后PartialView丢失的问题
这个问题的关键是让服务器知道你动态添加的Partial对应的 data,这样返回视图时能重新渲染它们。
1. 调整Model结构
给你的FarSighted模型加一个集合属性,用来存储所有动态创建的Bill数据:
public class FarSightedModel { // 保留你原来的Share、PackFactor、LenseCover等属性 public bool Share { get; set; } public decimal? PackFactor { get; set; } public decimal? LenseCover { get; set; } // 新增集合,存储所有动态添加的Bill public List<FarSightedBillModel> Bills { get; set; } = new List<FarSightedBillModel>(); }
这里假设你的PartialView用的Model是FarSightedBillModel,根据实际情况替换即可。
2. 服务器端预渲染已有的Bills
在Create.cshtml的FarSightedBillsSection里,除了留空给JS添加,还要遍历Model里的Bills集合,预先渲染已有的Partial:
<div id="FarSightedBillsSection"> @* 先渲染已经存在的Bills *@ @for (int i = 0; i < Model.FarSighted.Bills.Count; i++) { <div id='farSightedBill@i'> <a onclick="RemovePartial('far', @i)" style='cursor: pointer'> <img src='/images/Minus.png' width='25' height='25' /> </a> <h4> <a onclick="$('farSighted@i').collapse('toggle')" style='cursor: pointer' data-target='#farSighted@i' id='farBillTitle@i'> عینک دائمی @(i+1) </a> </h4> <br /> <div class='collapse show' id='farSighted@i'> <partial name="RecipeFarSightedBill.cshtml" Model="Model.FarSighted.Bills[i]" /> </div> </div> } </div>
同时更新JS里的初始索引,让它从已有的Bills数量开始:
var farIndex = @Model.FarSighted.Bills.Count; var farRealCount = @Model.FarSighted.Bills.Count;
3. 确保表单提交时数据能正确绑定
你的PartialView里的表单元素需要生成正确的name属性,让ASP.NET Core能把数据绑定到集合里。可以在返回Partial的Action里设置字段前缀:
public IActionResult DisplayFarBill(int index, decimal? packFactor, decimal? lenseCover) { var billModel = new FarSightedBillModel(); // 设置字段前缀,确保Partial里的asp-for生成正确的name:FarSighted.Bills[index].XXX ViewData.TemplateInfo.HtmlFieldPrefix = $"FarSighted.Bills[{index}]"; return PartialView("RecipeFarSightedBill", billModel); }
这样用户提交表单时,所有动态添加的Bill数据都会被绑定到Model.FarSighted.Bills集合里,服务器验证失败返回视图时,会重新渲染这些Bills。
4. 处理删除逻辑
删除Partial时,除了移除DOM,还可以在服务器端过滤掉空的Bill(防止无效数据提交):
[HttpPost] public IActionResult Create(Recipe model) { if (!ModelState.IsValid) { // 过滤掉空的Bill,避免返回视图时渲染空的Partial model.FarSighted.Bills = model.FarSighted.Bills .Where(b => !string.IsNullOrEmpty(b.SomeKeyProperty) || /* 其他判断条件 */) .ToList(); return View(model); } // 正常处理逻辑 return RedirectToAction("Index"); }
二、解决浏览器前进后退后PartialView丢失的问题
这个问题是因为浏览器历史记录只保存了初始页面状态,我们可以用SessionStorage或浏览器的History API来保存动态状态,页面加载时恢复。
方案1:用SessionStorage保存状态
每次添加/删除Partial时,把当前的Bill数量存在SessionStorage里,页面加载时读取并恢复:
// 修改AddBill函数,添加保存逻辑 function AddBill(type) { // 保留你原来的tag生成逻辑 $.get('/Glasses/DisplayFarBill?index=' + farIndex + '&packFactor=' + '@(Model.FarSighted?.PackFactor)' + '&lenseCover=' + '@(Model.FarSighted?.LenseCover)', function (partial) { $('#FarSightedBillsSection').append(tag); $('#farSighted' + farIndex).append(partial); $('#farSighted' + farIndex).collapse('show'); farIndex++; farRealCount++; // 保存当前Bill数量到SessionStorage sessionStorage.setItem('farBillsCount', farRealCount); }); } // 修改RemovePartial函数,更新保存的状态 function RemovePartial(type, index) { $('#farSightedBill' + index).remove(); farRealCount--; sessionStorage.setItem('farBillsCount', farRealCount); } // 页面加载时恢复状态 $(document).ready(function() { const savedCount = sessionStorage.getItem('farBillsCount'); if (savedCount) { const needToAdd = parseInt(savedCount) - @Model.FarSighted.Bills.Count; // 循环调用AddBill,恢复到之前的数量 for (let i = 0; i < needToAdd; i++) { AddBill('far'); } } });
方案2:用History API绑定浏览器历史
每次操作后把状态推到历史记录,前进后退时触发恢复:
// AddBill里添加pushState function AddBill(type) { // 原有代码... $.get(/* ... */, function (partial) { // 原有代码... farIndex++; farRealCount++; // 把当前状态推到历史记录 history.pushState({ farBillsCount: farRealCount }, '', window.location.href); }); } // 监听前进后退事件 window.addEventListener('popstate', function(event) { if (event.state?.farBillsCount) { // 先清空现有动态Partial $('#FarSightedBillsSection').find('[id^="farSightedBill"]').remove(); farIndex = @Model.FarSighted.Bills.Count; farRealCount = @Model.FarSighted.Bills.Count; // 恢复到保存的数量 const needToAdd = event.state.farBillsCount - @Model.FarSighted.Bills.Count; for (let i = 0; i < needToAdd; i++) { AddBill('far'); } } });
这个方案的好处是状态和浏览器历史绑定,前进后退时会自动触发恢复。
内容的提问来源于stack exchange,提问作者Maryam

