返回

.NET MVC 巧用 Bootstrap 模态框:实体模型数据传递

javascript

Bootstrap 模态框传值的那些事儿:.NET MVC 实体模型数据传递

你是不是也遇到过这样的问题:想把 .NET MVC 里的实体模型数据传到 Bootstrap 模态框里,却发现直接用 data-bs-whatever="@Model" 不好使? 别急,这篇博客就来帮你解决这个问题。

一、 问题:data-bs-whatever 的坑

按照 Bootstrap 的官方文档,咱们可以用 data-bs-whatever 属性来传值。 问题是,直接把整个实体模型 @ent 塞进去,JavaScript 里取出来的 recipient 只是一个字符串表示, 不是对象,更别提访问它的属性了(比如 recipient.Name 结果是 undefined)。

这是因为直接将一个 C# 对象输出到 HTML 特性时,它会被转换为字符串表示(默认是类的全名)。不是我们期望的那个包含各个字段数据的 JSON 对象.

二、 解决方法

针对上面所的原因,有几个办法解决这传值问题,优缺点都给你列出来。

1. 笨办法:用多个 data-* 属性

  • 原理: 把实体模型的每个字段都用一个单独的 data-* 属性存起来。
  • 代码:
<button class="btn btn-primary" 
        data-bs-toggle="modal" 
        data-bs-target="#exampleModal" 
        data-id="@ent.Id"
        data-name="@ent.Name"
        data-number="@ent.Number">Remove</button>
const exampleModal = document.getElementById('exampleModal')
exampleModal.addEventListener('show.bs.modal', event => {
    const button = event.relatedTarget
    const id = button.getAttribute('data-id')
    const name = button.getAttribute('data-name')
    const number = button.getAttribute('data-number')

    const modalTitle = exampleModal.querySelector('.modal-title')
    const modalBodyInput = exampleModal.querySelector('.modal-body p')

    modalTitle.textContent = `Warning! You're about to remove ${name}`
    modalBodyInput.textContent = `Product Number: ${number}`
    // ... 可以继续使用 id, name, number 这些变量
})
  • 优点: 简单粗暴,容易理解。
  • 缺点: 如果实体模型有很多字段,写起来太麻烦了,而且容易出错。

2. JSON 大法好:序列化成 JSON

  • 原理: 在服务器端把实体模型序列化成 JSON 字符串,再传给 data-bs-whatever。 前端取到之后, 用JSON.parse()解析成对象。
  • 代码:
// 在你的 Controller 或 View 里,引入 Newtonsoft.Json 库(如果你的项目里没有,需要先安装)
// using Newtonsoft.Json;
// 如果用.NET Core 内置的库 System.Text.Json
// using System.Text.Json;

//推荐在Controller中完成序列化,然后通过ViewBag/ViewData传递,以下只是展示如何view直接实现.
//  需要谨慎处理 Razor 视图中的字符串转义和安全性问题。
@using Newtonsoft.Json;

<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@JsonConvert.SerializeObject(ent)">Remove</button>
const exampleModal = document.getElementById('exampleModal')
exampleModal.addEventListener('show.bs.modal', event => {
    const button = event.relatedTarget
    const recipient = button.getAttribute('data-bs-whatever')
    const entity = JSON.parse(recipient) // 把 JSON 字符串转成 JavaScript 对象

    console.log(entity.Name) // 搞定!
    console.log(entity.Number)

    const modalTitle = exampleModal.querySelector('.modal-title')
    const modalBodyInput = exampleModal.querySelector('.modal-body p')

    modalTitle.textContent = `Warning! You're about to remove ${entity.Name}`
    modalBodyInput.textContent = `Product Number: ${entity.Number}`
})
  • 优点: 非常灵活,可以传递任意复杂的对象。
  • 缺点: 需要引入 JSON 序列化的库。 如果要考虑前后端统一,还需要处理特殊字符,单双引号转义等细节。

3. 改进版JSON方法: 使用 data- 存储 ID,然后通过 AJAX 获取详细数据。

  • 原理 : 只通过data-id 传递实体的主键(通常是 Id)。当模态框显示时,用 JavaScript 发起一个 AJAX 请求,根据这个 Id 去服务器端获取完整的实体数据(通常返回 JSON)。

  • 代码

    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-id="@ent.Id">Remove</button>
const exampleModal = document.getElementById('exampleModal');

exampleModal.addEventListener('show.bs.modal', event => {
    const button = event.relatedTarget;
    const entityId = button.getAttribute('data-id');

    // 发起 AJAX 请求
    fetch(`/YourController/GetEntityDetails?id=${entityId}`) // 假设你有一个 GetEntityDetails 的 Action
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(entity => {
            // 成功获取到实体数据
            const modalTitle = exampleModal.querySelector('.modal-title');
            const modalBodyInput = exampleModal.querySelector('.modal-body p');

            modalTitle.textContent = `Warning! You're about to remove ${entity.Name}`;
            modalBodyInput.textContent = `Product Number: ${entity.Number}`;
        })
        .catch(error => {
            console.error('There has been a problem with your fetch operation:', error);
            // 处理错误,比如显示一个错误提示
            const modalBodyInput = exampleModal.querySelector('.modal-body p');
            modalBodyInput.textContent = 'Failed to load product details.';

        });
});
// 对应的 Controller Action (YourController.cs)
public IActionResult GetEntityDetails(int id)
{
    Entity entity = _context.Entities.Find(id); // 从数据库或其他数据源获取实体

    if (entity == null)
    {
        return NotFound(); // 或者返回一个错误消息
    }
     // System.Text.Json
     return Json(entity);
     // or if you are use Newtonsoft.Json
     //return Json(entity, new Newtonsoft.Json.JsonSerializerSettings());
}

  • 优点 : 保持了数据传输的干净和安全。避免将所有实体数据暴露在 HTML 中,也更容易处理大型或复杂对象.
  • 缺点 : 需要编写额外的服务器端代码 (Controller Action), 而且增加了网络请求.

4. 进阶:把 JSON 数据直接内联到 JavaScript 里 (不推荐大数据量)

如果你的实体数据不是特别大,而且不想用 AJAX,可以考虑把 JSON 数据直接内联到 JavaScript 里。

  • 原理: 在 Razor 视图里把整个实体列表(或者部分数据)序列化成 JSON,然后赋值给一个 JavaScript 变量。在模态框的事件处理函数里,通过 ID 去这个 JavaScript 数组里查找对应的实体。

  • 代码:

@using Newtonsoft.Json;

<script>
    // 把实体列表序列化成 JSON,赋值给一个 JavaScript 变量
    var entities = @Html.Raw(JsonConvert.SerializeObject(Model.Entities));
</script>

<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-id="@ent.Id">Remove</button>


const exampleModal = document.getElementById('exampleModal')
exampleModal.addEventListener('show.bs.modal', event => {
    const button = event.relatedTarget
    const entityId = parseInt(button.getAttribute('data-id')) // ID 通常是数字,最好转一下

    // 在 entities 数组里查找对应 ID 的实体
    const entity = entities.find(e => e.Id === entityId)

    if (entity) {
        const modalTitle = exampleModal.querySelector('.modal-title')
        const modalBodyInput = exampleModal.querySelector('.modal-body p')

        modalTitle.textContent = `Warning! You're about to remove ${entity.Name}`
        modalBodyInput.textContent = `Product Number: ${entity.Number}`
    } else {
        // 没找到对应的实体,可能是数据不一致,或者 ID 有问题
        console.error("Entity not found for ID:", entityId)
    }
})
</script>

  • 优点 : 减少一次网络请求,比较直接.
  • 缺点 : 增加了页面大小。 数据量如果很大,页面加载会变慢,而且数据也暴露在 HTML源代码里面了.

三、安全建议

不论您用哪种, 特别是JSON序列化方法, 一定要注意数据安全 。 尽量不要在前端暴露敏感信息。 对于敏感数据,不要直接传递到前端。 只传递必要的 ID, 或者做一些数据脱敏.

如果直接在 HTML 属性里存储 JSON 数据,要小心 XSS(跨站脚本)攻击。 确保对数据进行正确的编码或转义。 可以考虑对数据做 HTML 编码 ( Razor 里可以用 @Html.Raw() 搭配一个编码函数).

方法3 (AJAX) 是相对比较安全的一种选择,因为它只传递了 ID,实际数据仍然在服务器端处理.

四、总结一下

好了,上面几个方法,你可以挑一个适合你项目的。 最常用的是 JSON 序列化的方法。如果数据比较简单,不想麻烦,就用多个 data-* 属性。数据安全敏感就用AJAX。根据需要,自己权衡一下。