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

Razor页面多对多模型绑定与表单选中状态持久化问题

实现方案分步说明

一、修正模型间的关联关系

先通过EF Core正确配置多对多关联,明确中间表的角色:

1. 实体模型定义

// ChangeForm 主表模型
public class ChangeForm
{
    public int Id { get; set; }
    // 表单其他业务字段(如申请人、申请时间等)
    public ICollection<ChangeFormChangeType> ChangeFormChangeTypes { get; set; } = new List<ChangeFormChangeType>();
}

// ChangeType 变更类型字典表
public class ChangeType
{
    public int Id { get; set; }
    public string Name { get; set; } // 如新入职、重新入职
    public ICollection<ChangeFormChangeType> ChangeFormChangeTypes { get; set; } = new List<ChangeFormChangeType>();
}

// 中间关联表(若需额外字段可在此添加)
public class ChangeFormChangeType
{
    public int ChangeFormId { get; set; }
    public ChangeForm ChangeForm { get; set; }
    public int ChangeTypeId { get; set; }
    public ChangeType ChangeType { get; set; }
}

2. EF Core上下文配置

在DbContext的OnModelCreating方法中显式配置多对多关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // 配置中间表复合主键
    modelBuilder.Entity<ChangeFormChangeType>()
        .HasKey(cfct => new { cfct.ChangeFormId, cfct.ChangeTypeId });

    // 关联ChangeForm主表
    modelBuilder.Entity<ChangeFormChangeType>()
        .HasOne(cfct => cfct.ChangeForm)
        .WithMany(cf => cf.ChangeFormChangeTypes)
        .HasForeignKey(cfct => cfct.ChangeFormId);

    // 关联ChangeType字典表
    modelBuilder.Entity<ChangeFormChangeType>()
        .HasOne(cfct => cfct.ChangeType)
        .WithMany(ct => ct.ChangeFormChangeTypes)
        .HasForeignKey(cfct => cfct.ChangeTypeId);
}

二、Razor页面处理复选框与状态持久化

用LocalStorage保存选中状态,避免页面刷新或多次提交前丢失,仅在用户点击保存时统一提交:

1. 页面模型(PageModel)

public class ChangeFormPageModel : PageModel
{
    private readonly AppDbContext _dbContext;

    public ChangeFormPageModel(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    [BindProperty]
    public ChangeForm ChangeForm { get; set; }

    public List<ChangeType> AllChangeTypes { get; set; }

    // 接收提交时选中的变更类型ID集合
    [BindProperty]
    public List<int> SelectedChangeTypeIds { get; set; } = new List<int>();

    public async Task OnGetAsync(int? id)
    {
        // 加载所有变更类型选项
        AllChangeTypes = await _dbContext.ChangeTypes.ToListAsync();

        if (id.HasValue)
        {
            // 编辑场景:加载已有表单及关联的变更类型
            ChangeForm = await _dbContext.ChangeForms
                .Include(cf => cf.ChangeFormChangeTypes)
                .ThenInclude(cfct => cfct.ChangeType)
                .FirstOrDefaultAsync(cf => cf.Id == id);

            // 初始化已选中的ID列表
            if (ChangeForm != null)
            {
                SelectedChangeTypeIds = ChangeForm.ChangeFormChangeTypes.Select(cfct => cfct.ChangeTypeId).ToList();
            }
        }
        else
        {
            // 新增场景:初始化空表单
            ChangeForm = new ChangeForm();
        }
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            AllChangeTypes = await _dbContext.ChangeTypes.ToListAsync();
            return Page();
        }

        if (ChangeForm.Id == 0)
        {
            // 新增表单:先保存主表获取ID,再添加关联
            _dbContext.ChangeForms.Add(ChangeForm);
            await _dbContext.SaveChangesAsync();
        }
        else
        {
            // 编辑表单:先清空旧关联,避免重复数据
            var oldAssociations = await _dbContext.ChangeFormChangeTypes
                .Where(cfct => cfct.ChangeFormId == ChangeForm.Id)
                .ToListAsync();
            _dbContext.ChangeFormChangeTypes.RemoveRange(oldAssociations);
        }

        // 添加新的关联记录
        foreach (var typeId in SelectedChangeTypeIds)
        {
            _dbContext.ChangeFormChangeTypes.Add(new ChangeFormChangeType
            {
                ChangeFormId = ChangeForm.Id,
                ChangeTypeId = typeId
            });
        }

        await _dbContext.SaveChangesAsync();
        return RedirectToPage("./ChangeForm", new { id = ChangeForm.Id });
    }
}

2. Razor视图(.cshtml)

@page
@model YourNamespace.Pages.ChangeFormPageModel

<form method="post">
    <!-- 其他表单字段(如申请人、申请时间等) -->
    <div class="form-group">
        <label>选择变更类型</label>
        <div>
            @foreach (var type in Model.AllChangeTypes)
            {
                <div class="form-check">
                    <input type="checkbox" class="form-check-input change-type-checkbox"
                           id="type-@type.Id"
                           name="SelectedChangeTypeIds"
                           value="@type.Id"
                           @(Model.SelectedChangeTypeIds.Contains(type.Id) ? "checked" : "")>
                    <label class="form-check-label" for="type-@type.Id">@type.Name</label>
                </div>
            }
        </div>
    </div>

    <button type="submit" class="btn btn-primary">保存表单</button>
</form>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const checkboxes = document.querySelectorAll('.change-type-checkbox');
        // 用表单ID作为LocalStorage键,区分不同表单的选中状态
        const storageKey = `ChangeForm_SelectedTypes_@Model.ChangeForm.Id`;

        // 页面加载时从LocalStorage恢复选中状态
        const savedIds = localStorage.getItem(storageKey);
        if (savedIds) {
            const selectedIds = JSON.parse(savedIds);
            checkboxes.forEach(cb => {
                if (selectedIds.includes(parseInt(cb.value))) cb.checked = true;
            });
        }

        // 勾选/取消时同步保存到LocalStorage
        checkboxes.forEach(cb => {
            cb.addEventListener('change', function() {
                const selectedIds = Array.from(checkboxes)
                    .filter(item => item.checked)
                    .map(item => parseInt(item.value));
                localStorage.setItem(storageKey, JSON.stringify(selectedIds));
            });
        });
    });
</script>

三、关键细节说明

  • 关联稳定性:通过复合主键和显式外键配置,确保EF Core正确识别多对多关系,避免关联数据混乱。
  • 状态持久化:LocalStorage会在用户浏览器中保存选中状态,即使页面刷新、切换标签后返回,选中的选项也不会丢失,直到用户主动清除或提交表单。
  • 提交逻辑:编辑表单时先清空旧关联再添加新选中项,避免重复数据;新增表单需先保存主表获取ID,才能关联中间表。
  • 避免即时提交:移除checkbox的onchange提交逻辑,仅保留表单提交按钮,用户可自由勾选所有选项后统一提交。

内容的提问来源于stack exchange,提问作者Greg S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:35:15