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

如何从Express路由将数据传入页面的script标签?

如何在Handlebars模板的Script标签中使用Express传递的数组数据?

首先得说,你遇到的问题其实分两部分:异步操作导致数据还没准备好就渲染模板,以及Handlebars模板中直接在script里使用数组的正确方式,咱们一步步解决。

第一步:先修复异步查询的顺序问题

看你的路由代码,三个数据库查询(User.findOne、Tasks.findAndCountAll、Tasks.findAll)是并行执行的,但你直接在Tasks.findAll的回调里调用了res.render——这时候前两个异步操作大概率还没完成,所以countList和categoryList还是空数组,传去模板自然没用。

解决办法是用Promise.all等待所有异步操作完成,再统一渲染:

app.get('/:username/tasks', function (req, res) {
  if (req.session.user === req.params.username) {
    // 把每个数据库查询包装成Promise
    const getUserPhoto = db.User.findOne({
      where: { username: req.session.user }
    });
    const getTaskStats = db.Tasks.findAndCountAll({
      // 修正attributes配置,正确拿到每个分类的数量
      attributes: ['category', [db.fn('count', 'category'), 'taskCount']],
      where: { UserUsername: req.session.user },
      group: 'category'
    });
    const getAllTasks = db.Tasks.findAll({
      where: { UserUsername: req.params.username }
    });

    // 等待所有Promise执行完毕
    Promise.all([getUserPhoto, getTaskStats, getAllTasks])
      .then(([userInfo, statsResult, tasksData]) => {
        // 整理需要的数据
        const photo = userInfo.dataValues.picURL;
        const categoryList = statsResult.rows.map(row => row.dataValues.category);
        const countList = statsResult.rows.map(row => row.dataValues.taskCount);

        console.log(categoryList); // 应该能拿到['Health', 'Other', ...]
        console.log(countList); // 对应[5,1,...]

        // 所有数据准备就绪,渲染模板
        res.render('index', {
          data: tasksData,
          photo: photo,
          countList: countList,
          categoryList: categoryList
        });
      })
      .catch(err => {
        console.error('查询出错:', err);
        res.redirect('/');
      });
  } else {
    res.redirect('/');
  }
});

这里修正了findAndCountAll的attributes配置,因为默认分组后不会自动返回每个组的数量,需要手动用db.fn('count')统计,这样拿到的数据才是正确的。

第二步:在Script标签中正确使用Handlebars传递的数据

现在数据已经正确传到模板了,但直接写labels: [categoryList]没用,因为Handlebars默认会把数组转成字符串(比如变成"Health,Other,Recreational,Work"),而Chart.js需要的是JS数组。

有两种简单的解决方式:

方式一:直接在Script中输出JSON格式的数组

用Handlebars的三重大括号{{{}}}(避免HTML转义)配合JSON.stringify,直接把数组转成JS能识别的格式:

<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: {{{JSON.stringify categoryList}}},
    datasets: [{
      label: '# of Tasks',
      data: {{{JSON.stringify countList}}},
      backgroundColor: [
        'rgba(255, 99, 132, 0.2)',
        'rgba(54, 162, 235, 0.2)',
        'rgba(255, 206, 86, 0.2)',
        'rgba(75, 192, 192, 0.2)',
      ],
      borderColor: [
        'rgba(255,99,132,1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
      ],
      borderWidth: 1
    }]
  },
});
</script>

三重大括号会直接输出JSON.stringify后的结果(比如["Health","Other",...]),这样JS就能直接把它当作数组使用。

方式二:通过HTML的data属性传递数据

如果你不想在script里直接写Handlebars表达式,可以把数据存在隐藏元素的data属性里,再在JS中读取解析:

<!-- 在HTML中添加隐藏元素存储数据 -->
<div id="chart-data" style="display:none" 
     data-categories='{{{JSON.stringify categoryList}}}' 
     data-counts='{{{JSON.stringify countList}}}'></div>

<script>
// 读取并解析数据
const chartDataEl = document.getElementById('chart-data');
const categoryList = JSON.parse(chartDataEl.dataset.categories);
const countList = JSON.parse(chartDataEl.dataset.counts);

// 初始化Chart
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: categoryList,
    datasets: [{
      label: '# of Tasks',
      data: countList,
      // 其他配置不变
    }]
  },
});
</script>

这种方式更灵活,适合数据量较大或者需要在多个地方复用数据的场景。

最后验证

做完上面两步,重启Express服务,访问页面后打开浏览器控制台,确认categoryList和countList已经正确输出为数组,Chart.js就能正常生成饼图了。

内容的提问来源于stack exchange,提问作者Joshua Wood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:29:18