javascript - 脚本无法从Node.js应用程序中的EJS文件运行

标签 javascript html node.js chart.js ejs

我试图使用nodejs、express、mysql和ejs使网页显示图表,但是我显然不了解ejs/javascript等的工作原理。我需要一个脚本来设置一个图表(来自chart.js模块),但它没有输出任何类型的图表。
我试过的:
在脚本中放置console.log()消息,查看脚本中的代码是否有问题。没有输出到控制台,所以我确信脚本本身没有运行
将段落标记放在脚本上方的同一文件中,该脚本显示
正常情况下,显示文件仍在被访问,并且只有脚本不起作用
以下脚本将不运行:

<canvas id="myChart" width="50%" height="100px"></canvas>

<script scr="map-access-data.js" type="text/javascript"></script>
<script id ="map-popularity" type="text/javascript">
    var Chart = require('chart');
    var ctx = document.getElementById("myChart").getContext("2d");
    var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: getMapAccessNames(),
            datasets:[{
                label: "Map Selection Popularity",
                data: getMapAccessData(),
                backgroundColor: getMapColors(),
                borderColor: getMapColors(),
                borderWidth: 1
            }]
        },
        options: {
            scales: {
                yAxes: [{
                    ticks: {
                        beginAtZero:true
                    }
                }]
            }
        }
    });
</script>

在该文件的第一个脚本中引用的map-access-data.js文件:
var mysql = require('mysql');

var connection = mysql.createConnection({
    host     : 'localhost',
    user     : 'admin',
    password : 'pass',
    database : 'db'
});

connection.connect();

function getNumMaps(){
    connection.query("SELECT NAME, COUNT(*) FROM map_access GROUP BY NAME ORDER BY NAME", function(err, rows){
        return rows.length();
    });
}

function getMapAccessData(){
    var arr = [];
    connection.query("SELECT NAME, COUNT(*) FROM map_access GROUP BY NAME ORDER BY NAME", function(err, rows){
        for(i in rows){
            arr.push(i.count);
        }
    });
}

function getMapAccessNames(){
    var arr = [];
    connection.query("SELECT NAME, COUNT(*) FROM map_access GROUP BY NAME ORDER BY NAME", function(err, rows){
        for(i in rows){
            arr.push(i.name);
        }
    });
    return arr;
}

function getMapColors(){
    var arr = [];
    for(var i = 0; i < getNumMaps(); i++){
        arr.push('rgba(255,99,132,1)');
    }
    return arr;

此代码呈现的实际文件:
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <% include header.ejs %>
    <h1><%= title %></h1>
    <p>See how people are using our app <br/></p>
    <% include map-access-chart %>
  </body>
</html>

最佳答案

这里有很多误解:HTML文件将提供给您的客户机,客户机将看到<script>标记,从web服务器请求它们,并执行代码。您不能期望web浏览器在您的服务器上运行SQL查询,因此显然这不是您希望执行此JS代码的方式。
这么多是错误的,这将是一个长期的答案。以下是你的两个主要误解:
您尝试使客户端执行服务器代码
运行异步代码并认为它将同步返回
那么你的许多小错误:
长度是一个属性,而不是一个方法
必须导出一个方法,才能使它在节点中的外部可用
你使用for (i in rows)所以我是项目索引(我猜你是因为把它命名为i而得到的),然后你使用i.count
在您的SQL中输入SELECT COUNT(*) FROM然后使用.count属性,如果没有AS count属性,我不确定它是否能工作
在这一点上,我只能猜测您的SQL和图表使用情况并没有更好,对不起:(不过,我会尽量给您指出正确的方向)。
标识客户端和服务器
因此,首先,需要从Node.JS服务器执行此JS代码。通常的步骤是:
启动Express应用程序
配置EJS呈现
在您的路线上:
运行sql查询,获取一堆数据(您仍然是服务器端的)
呈现HTML,传递一些数据
现在在模板中,只需将所需的内容从服务器注入到客户端
利润
下一步的示例数据结构:

/
+- app.js
+- lib/
   +- map-access-data.js
+- views/
   +- index.ejs
   +- map-access-chart.ejs
+- stylesheets/
   +- styles.css

服务器
所需的服务器依赖项:npm install express ejs mysql,其余为客户端(如chart
// app.js
const express = require('express')
const app = express()

// Serve public static assets, like stylesheets and client-side JS
app.use(app.static('public'))

// Configure EJS template engine
app.set('view engine', 'ejs')

// Your route
app.get('/', (req, res) => {
  // Your route handler
  // req is client's request
  // res is server's response
})

// Start web server on http://localhost:8000
app.listen(8000)

好的,这里是服务器端,您可以使用MySQL和类似的系统。但首先我们需要解决另一个问题。
异步代码
异步是节点的一个非常重要的部分,真的,但是我们不能在这里解决所有问题。你会有关键词,我让你做你的研究驯服那部分。我将使用async/await以便您在阅读代码时不会受到太多干扰,并使用util.promisify来转换方法。你必须明白的是:
connection.query将查询远程服务器,在节点中它将异步完成,这意味着您不会立即得到任何结果,但您的代码也不会停止(或者它将被阻塞,这很糟糕)
要表示异步操作,基本上有两种方法:
将回调函数传递给异步函数,此回调将在结果可用时立即调用;使用回调时,不能返回任何感兴趣的内容
返回一个对象(称为promise),它只是一个空包装器;然后,这个对象将突然包含结果,并能够调用传递给它的then方法的函数;当使用promises时,必须返回那些对象,它们是未来数据的表示,是访问它的唯一方法
当您使用promise时,有一个称为async的特定语法允许您为promised数据wait,但是您的异步函数仍然是异步的,这意味着它返回一个包装器,而不是实际的结果,除非您也为它wait
以下是您的错误:
getNumMaps中,您的return位于回调中。这个回调是在函数返回自己的结果后调用的,因此它只返回未定义的
getMapAccessData中,您甚至没有返回任何内容,仍然没有定义
getMapAccessNames中,您终于返回了一些内容!但是由于connection.query是异步的,所以在funtion已经返回arr之后,您将数据推送到数组中,因此它总是返回[]
我将添加您执行三次相同的请求,听起来很浪费;)因此,您知道您希望最终将所有这些都包含在图表实例中,让我们不要将其拆分为4个执行相同查询的函数,而是使用适当的格式生成单个结果。
// lib/map-access-data.js
const mysql = require('mysql')
const connection = mysql.createConnection(/* your config here */)

// get promises instead of using callbacks
const util = require('util')
const queryCallback = connection.query.bind(connection) // or we'll have issues with "this"
const queryPromise = util.promisify(queryCallback) // now this returns a promise we can "await"

// our asynchronous method, use "async" keyword so Node knows we can await for promises in there
async function getChartData () {
  const rows = await queryPromise("SELECT name, COUNT(*) AS count FROM map_access GROUP BY name ORDER BY name")

  // Array#map allows to create a new array by transforming each value
  const counts = rows.map(row => row.count)
  const names = rows.map(row => row.name)
  const colors = rows.map(row => 'rgba(255,99,132,1)')

  // Return an object with chart's useful data
  return {
    labels: names,
    data: counts,
    colors: colors,
  }
}

模块
好的,现在你有了一个功能,只提供服务器端,这给了你所需要的。
现在您需要能够从app.js调用它,这意味着您需要:
导出它:
// lib/map-access-data.js
…

// Export your function as default
module.exports = getChartData

然后导入并在路由处理程序中使用它:
// app.js
const getChartData = require('./lib/map-access-data)

这叫做CommonJS模块
现在,在路由处理程序中,您可以简单地调用异步函数,并等待其结果:
// app.js
…

app.get('/', async (req, res) => {
  // Your route handler
  const data = await getChartData()
})

生成HTML
现在您的数据可用了,您仍然是服务器端的,您现在必须为您的客户端生成有效的HTML,它当前看起来像:
<!DOCTYPE html>
<html>
  … a bunch of HTML …

    <p>See how people are using our app <br/></p>
    <canvas id="myChart" width="50%" height="100px"></canvas>

    <!-- NO! it's not a client JS, it's server JS, client CANNOT download it -->
    <script scr="map-access-data.js" type="text/javascript"></script>

    <script id ="map-popularity" type="text/javascript">
        var Chart = require('chart'); // NO! you can't *require* from client
        var ctx = document.getElementById("myChart").getContext("2d");
        var myChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: getMapAccessNames(), // NO! You can't call server methods from client
                datasets:[{
…

显然我们需要解决一些问题:
删除对map-access-data.js的引用,这毫无意义
添加浏览器的方式,就像从CDN
在客户端JS中注入数据
在这里,我认为您可以使用Ajax请求,而不是直接将真实数据注入HTML,但是我不知道Chart,所以我将让您完成这一部分。一个提供JSON数据的Express应用程序非常简单,只需chart.js,然后在客户端执行一些Ajax。让我们看看您将数据直接注入HTML以打破所有障碍的版本:
服务器端你运行你的SQL,它给你一些数据
将这些数据传递到EJS模板,该模板(仍然是服务器端)生成HTML
在这个HTML中,您将注入服务器数据的字符串表示(使用res.send(data)
服务器最终将生成的HTML发送到客户端
客户端接收到这个格式良好的HTML,并在JSON.stringify中运行一些JS,每个人都很高兴
<!-- views/map-access-chart.ejs -->
<canvas id="myChart" width="50%" height="100px"></canvas>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<script id ="map-popularity" type="text/javascript">
    var ctx = document.getElementById("myChart").getContext("2d");
    var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: <%- JSON.stringify(data.labels) %>,
            datasets:[{
                label: "Map Selection Popularity",
                data: <%- JSON.stringify(data.data) %>,
                backgroundColor: <%- JSON.stringify(data.colors) %>,
                borderColor: <%- JSON.stringify(data.colors) %>,
                borderWidth: 1
…

// app.js
…

// Your route handler
const data = await getChartData()
// Render 'index.ejs' with variables 'title' and 'data' available
res.render('index', {
  title: 'Page title',
  data: data,
})

现在,当您从终端运行<script>并转到http://localhost:8000时,您应该会看到结果。我想还有很多错误,但那将是一个更好的开始:)

关于javascript - 脚本无法从Node.js应用程序中的EJS文件运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49343185/

相关文章:

javascript - Gulps gulp.watch 不会为新文件或已删除文件触发?

javascript - 不理解 Mongoose 模式数组语法

node.js - Electron Node -Gyp 库未加载 : dylib

javascript - 如何在 JavaScript 中将图像转换为 base64

javascript - 获取光标附近文本的父元素-插入符

javascript - 使用 jQuery 检测未选中的复选框

javascript - JQuery - 检查每个动态添加的按钮是否被点击

javascript - 当复选框悬停时显示工具提示文本

javascript - Internet Explorer Javascript 的 AppendChild 问题

javascript - GWT:隐藏移动设备上的地址栏