返回

EJS & Plotly: 解决OHLC图表渲染及数据传递问题

javascript

解决EJS中Plotly OHLC图表渲染及数据传递问题

在Web开发中,动态生成图表是常见需求。使用 Plotly.js 这类强大的JavaScript图表库配合EJS这类模板引擎可以创建丰富的数据可视化效果。然而,在将数据从后端传递到前端的过程中,特别是在EJS模板中渲染 Plotly 图表时,可能会遇到一些阻碍,例如数据无法正确传递、图表不显示等等。 这个问题通常与EJS模板的数据传递和Plotly的初始化方式有关。下面针对此问题分析可能原因并提供有效的解决方案。

问题分析

上述代码的核心问题在于尝试直接在 chart.ejs 中使用EJS语法渲染一个 JavaScript对象 (变量 result) 并将其作为 Plotly 图表的数据源,或者引入一个含有 result 变量的chart.js脚本文件。 这会导致一系列的问题:

  1. EJS模板解析时机: EJS 是在服务端解析的, 而Plotly 图表绘制需要使用 JavaScript在浏览器中执行。服务端处理 chart.ejs 时会解析所有EJS标签, 渲染完成后将结果发到浏览器, 浏览器此时收到的并不是 JavaScript 对象本身,而是包含对象内容的字符串,Plotly不能直接识别它,导致数据无法正确传入。
  2. JavaScript 执行环境: 尝试直接在 chart.ejs 中的<script>标签中使用 result 变量,同样会失败,原因是:<script>中的 result 变量必须是一个浏览器中的 JavaScript 可用的对象,而不是服务端的 ejs 渲染数据。

解决方案

为了解决上述问题,核心思想是将 Plotly 绘图所需要的数据转化为前端可直接使用的 JavaScript 对象,在前端进行初始化绘图操作。

方案一:将数据以 JSON 形式内联到 HTML

这是最为常见和简便的做法: 在服务端,把要传递的数据对象result通过 EJS ${JSON.stringify(result)}转换成JSON字符串, 将其插入 HTML 的 <script> 标签中。这样一来, 浏览器端的 JavaScript 代码可以直接读取并解析该JSON字符串,获得 Plotly 可用的 JavaScript 数据对象。

操作步骤:

  1. 修改 chart.ejs: 将传递过来的 result 对象序列化为 JSON 字符串, 赋值给 window.resultData, 通过这个变量名将数据传递给 Plotly 的逻辑处理部分:
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
        <link rel="stylesheet" href="styles/main.css">
        <script src='https://cdn.plot.ly/plotly-2.35.2.min.js'></script>
    </head>
    <body>
        <h1>OHLC Chart</h1>
        <div id="ohlc-result-div"></div>

        <script>
            // Pass result from EJS template as a JavaScript object to the front end
            window.resultData = <%- JSON.stringify(result) %>;

            const trace = {
              x: window.resultData.x,
              open: window.resultData.open,
              high: window.resultData.high,
              low: window.resultData.low,
              close: window.resultData.close,
              type: "ohlc",
              increasing: { line: { color: "#17BECF" } },
              decreasing: { line: { color: "#7F7F7F" } },
            };

            const layout = {
              title: "OHLC Chart",
              dragmode: "zoom",
              xaxis: {
                  title: "Date",
                  type: "date",
              },
              yaxis: {
                  title: "Price",
                  type: "linear",
              },
            };

            Plotly.newPlot("ohlc-result-div", [trace], layout);
        </script>

    </body>
</html>
  • 代码分析: 使用 <%- %> 可以插入非转义 HTML 代码, 同时 JSON.stringify() 函数 将 result 转换成了字符串。 然后通过 window.resultData 来创建一个全局变量来存放这个字符串数据。在之后的 script 代码块中就可以用这个全局变量来拿到绘图需要的数据, 创建 tracelayout。 之后使用 Plotly.newPlot 就可以绘制图像。

说明:

  • 此方法直接将数据嵌入到页面 HTML 中, 简洁高效。对于较小的数据集非常合适。
  • 此方法避免了不必要的网络请求。
  • 将数据传递到 window 的同时也可以将它赋给某个自定义变量,根据开发需要进行调整。

方案二:使用 <script> 引入外部 JavaScript 文件进行绘制

如果想分离 chart.ejs 中的HTML和JavaScript, 或者 JavaScript 代码逻辑复杂, 可以使用外部JavaScript文件进行绘制,并在服务器端使用类似 JSON.stringify(result)的方式将数据传递到前端。 区别在于我们创建一个专门的 js 文件来处理数据并且用 JavaScript 全局变量来传递。

操作步骤:

  1. 修改chart.ejs: 在 chart.ejs中增加一段脚本,用来传递result 对象并声明 chart.js文件,确保它在 plotly.js 之后执行。
     <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
            <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
            <link rel="stylesheet" href="styles/main.css">
            <script src='https://cdn.plot.ly/plotly-2.35.2.min.js'></script>
        </head>
        <body>
            <h1>OHLC Chart</h1>
             <div id="ohlc-result-div"></div>
             <script>
                // Pass result from EJS template as a JavaScript object to the front end
               window.resultData = <%- JSON.stringify(result) %>;
             </script>
             <script src="/chart.js" defer></script>
        </body>
    </html>
    
    
  2. 修改chart.js :将之前直接使用的 result 改为 window.resultData
        const trace = {
         x: window.resultData.x,
         open: window.resultData.open,
         high: window.resultData.high,
         low: window.resultData.low,
         close: window.resultData.close,
         type: "ohlc",
         increasing: { line: { color: "#17BECF" } },
         decreasing: { line: { color: "#7F7F7F" } },
        };
    
        const layout = {
          title: "OHLC Chart",
           dragmode: "zoom",
          xaxis: {
              title: "Date",
              type: "date",
          },
          yaxis: {
              title: "Price",
               type: "linear",
           },
         };
     Plotly.newPlot("ohlc-result-div", [trace], layout);
    
  • 代码分析: 此方法需要增加 script 标签。首先, 用 window.resultData 将 EJS 传递的数据保存,保证 chart.js 可以获取到它。 注意保证引入的顺序, 需要在Plotly引入之后, chart.js 脚本文件在包含数据的 script 代码之后, 确保数据加载到全局作用域再使用。
    说明:

  • 将 Plotly 的逻辑独立到 chart.js,使代码结构更加清晰和模块化,易于维护。

  • 需要注意 script 标签的加载顺序。

安全提示

无论采用哪种方案,都应该注意以下安全事项:

  • 避免直接在模板中拼接 JavaScript 代码,以防止潜在的跨站点脚本(XSS)攻击。
  • 在将用户输入的数据渲染到模板前,应该进行充分的验证和转义。
  • 严格控制服务器端传递给前端的数据,只发送必需的数据。

掌握以上方案和技巧,能够解决 EJS 和 Plotly 协同工作时的各种问题,使 Web 开发流程更顺畅、代码结构更规范,并确保 Web 应用的安全性。