You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态PartialView在浏览器进退及表单验证后消失的解决方案求助

解决动态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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 11:57:40