【问题标题】:Allowing to click on a link after script has finished running允许在脚本完成运行后单击链接
【发布时间】:2018-04-04 23:35:31
【问题描述】:

我试图找到一种方法,让用户只有在特定功能完成运行后才能点击链接。所以在我的例子中,我有 ejs 模板,其中段落中有两个链接。单击第一个(“运行 python”)会激活一个需要一些时间才能完成的脚本。然后只有在它完成之后(所以有 “finished” - 来自控制台上打印的行:console.log('finished')下一个链接(“See the table”)将是可点击的(或取消隐藏或类似的东西)。

<h1>Search for a movie</h1>

<form action="results" method="GET">
    <input type="text" placeholder="search term" name="search">
    <input type="submit">
</form>

<p><a href="/run"> Run python </a></p>
<p><a href="/data"> See the table </a></p>

这里是 app.js 代码

var express = require("express")
var app = express()
var request = require("request")
var PythonShell = require('python-shell');
app.set("view engine", "ejs")
app.engine('html', require('ejs').renderFile);

var thelist =[]

app.get("/", function(req, res){
    res.render("search")
})

var jsondata = ""


app.get("/results", function(req, res){
    var query = req.query.search
    var url = "http://www.omdbapi.com/?s=" + query + "&type=series&apikey=thewdb"

    request(url, function(error, response, body){
        if(!error && response.statusCode == 200){
            var data = JSON.parse(body)
            res.render("results", {data: data})
        }    
    })
})

app.get('/data', function(req, res) {
    //viewname can include or omit the filename extension
    res.render(__dirname + '/well.html'); 
});


app.get("/show/:id", function (req, res) {
    var id = req.params.id;
    thelist.push(id)
    console.log(thelist);
    res.redirect('/')
});

app.get("/run", function(req, res) {
            var pyshell = new PythonShell('script2.py');
            pyshell.send(JSON.stringify(thelist))
            pyshell.on('message', function (message) {
    // received a message sent from the Python script (a simple "print" statement)
            jsondata += message
            });

// end the input stream and allow the process to exit
            pyshell.end(function (err) {
             if (err){
               throw err;
                };

            console.log('finished');
            });
            res.redirect('/')
            thelist = []
});

app.listen(process.env.PORT, process.env.IP, function(){
    console.log("Movie App has started!!!");
})

【问题讨论】:

  • 假设您最初隐藏(或禁用)该链接(“查看表格”),然后在console.log('finished') 之后的pyshell.end 函数中为其添加内联样式,这行得通吗? 例如: var linkToEnable = document.querySelector(".link-to-enable); linkToEnable.style.display = "block";(只是一个例子 - 这里的重点是确定您是否可以在预期的时刻添加内联样式:相关函数完成后)
  • 嗯,我不确定我是否正确地这样做,但我收到错误,即文档未定义。这是服务器端 node.js 代码,它似乎不允许 dom 操作。

标签: javascript html node.js express ejs


【解决方案1】:

正如 emil 在他的回答中建议的那样,您可能可以使用 ajax 来做到这一点, 但是既然你用的是ejs模板引擎,为什么不用呢?
(只需将.html模板文件扩展名改为.ejs即可。

另外,我认为您最好的选择是不使用res.redirect
最好使用res.render,给视图传一个参数,默认设置为false

一个基本的例子:

server.js

// ...
app.get("/", function (req, res) {
  res.render("search", { active: false });
})

// ...
app.get("/run", function (req, res) {
  // ...
  pyshell.end(function (err) {
    if (err) throw err;
    console.log('finished');
    res.render("search", { active: true });
  });
});

搜索.ejs

<p><a href="/run">Run python</a></p>
<p>
  <%if (active) { %>
    <a href="/data">See the table</a>
  <% } else { %>
    See the table
  <% } %>
</p>

现在See the table 链接只有在 python 脚本完成后才能点击。

【讨论】:

    【解决方案2】:

    总结

    应该通过使用 AJAX 调用来完成。除了在/run 中使用重定向,您可以让它返回一些指示作业成功/错误状态的 JSON 数据。

    点击Run python,它将运行AJAX调用而不是重定向页面到/

    OnSuccess的AJAX调用上,可以启用查看表格。

    代码

    HTML

    <p><a id="run" href="/run"> Run python </a></p>
    <p><a id="show-data" href="/data" style="pointer-events:none;"> See the table </a></p>
    

    前端 Javascript

    $(function() {
      $('#run').click(function(event) {
         event.preventDefault();
         $.get('/run', function() {
           $('#show-data').css('pointer-events', 'all');
         });
      });
    });
    

    NodeJs

    app.get("/run", function(req, res) {
      ...
      // res.redirect('/');
      res.json({ success: true });
    });
    

    【讨论】:

    • 我在你发布的时候试过这个,在我点击运行 python 后“查看表格”已经可以点击了,但是脚本还没有完成。为什么会这样?
    • 换句话说,它应该在 app.get 中的整个代码运行后发送 res.json({success: true}) - 因此执行了 console.log。
    • 当然可以。 res.json 应该在 app.get 完成所有工作后调用,这意味着它应该在 Promise 链的末尾或在最后一次触发的回调中。
    • 但这就是我现在正在做的事情。我在 console.log('finished') 行下方插入了 res.json。但是它仍然在打印 console.log 之前运行
    • 你是否删除了运行函数中的res.redirect('/')
    【解决方案3】:

    你可以使用阻止函数Jquery来阻止链接

     $("a").click(function() {
            if (status == "1") {
               return true;
    
            } else {
               return false;
               e.preventDefault();
            }
         });
    

    这是我用来阻止用户在不运行函数的情况下导航到其他页面的函数。

    我用 value = 0 初始化了一个名为 status 的变量

    然后,一旦函数成功,我就会增加变量。

    因此,如果状态为 0,则 URL 将不起作用,完成该功能后,状态将递增为 1,因此如果条件为真,它将返回 true 语句。如果不是,它将返回 false 语句,并且将使用 e.preventDefault() 阻止它

    【讨论】:

    • 这是问题的简单部分。但另一部分将如何设置状态;)?
    • 你必须在你的功能成功完成后增加状态值,
    • 您介意将这部分代码添加到您的解决方案中吗?
    【解决方案4】:

    你为什么不试试socket.io? 这是简化的代码,但功能齐全...

    (注意:“msg”对象仅作为示例,供进一步使用)

    index.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Title</title>
    </head>
    
    <body>
      <h1>Search for a movie</h1>
      <p><a href="#" onclick="runPython()"> Run python </a></p>
      <p><a id="table-data"> See the table </a></p>
    
      <script src="/socket.io/socket.io.js"></script>
      <script>
        var msg = {
          msg: "TestMsgSend",
          iParam: 100,
          sParam: "Test Param 1",
          aParam: [1, 2, 3]
        }
        var socket = io();
    
        socket.on("server:finished", function (msg) {
          // msg = "TestMsgReceived", for further usage
          console.log("\nId: " + socket.id + "\nmsg: " + msg.msg + "\niParam: " + msg.iParam + "\nsParam: " + msg.sParam + "\naParam: " + msg.aParam);
    
          document.getElementById("table-data").setAttribute("href", "/data");
        });
    
        function runPython() {
          socket.emit("client:run", msg);
        }
      </script>
    </body>
    </html>
    

    app.js

    var express = require("express");
    var app = express();
    var http = require("http").Server(app);
    var io = require("socket.io")(http);
    
    app.use("/", express.static(__dirname + "/"));
    
    // ***************************************************************************
    // ***************************************************************************
    // ***** Your code
    // ***************************************************************************
    // ***************************************************************************
    
    io.on("connection", function (socket) {
      console.log("New connection with id: " + socket.id);
    
      socket.on("client:run", function (msg) {
        // msg = "TestMsgSend", for further usage
        console.log("\nId: " + socket.id + "\nmsg: " + msg.msg + "\niParam: " + msg.iParam + "\nsParam: " + msg.sParam + "\naParam: " + msg.aParam);
    
        // ***************************************************************************
        // ***************************************************************************
        // ***** Your code
        // ***************************************************************************
        // ***************************************************************************
    
        msg.msg = "TestMsgReceived";
        msg.iParam++;
        msg.sParam = "Test Param 2";
        msg.aParam.push(4, 5, 6)
        io.emit("server:finished", msg);
      });
    });
    
    // ***************************************************************************
    // ***************************************************************************
    // ***** Your code
    // ***************************************************************************
    // ***************************************************************************
    
    http.listen(80, function () {
      console.log("listening on *:80");
    });
    

    package.json

    {
      "name": "test",
      "version": "1.0.0",
      "description": "",
      "main": "app.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "express": "^4.16.2",
        "socket.io": "^2.0.4"
      }
    }
    

    node app.js 开始并享受

    【讨论】:

    • 为这个用例实现套接字是多余的。您不是在制作实时应用程序。它只是禁用一个链接!
    【解决方案5】:

    为什么不使用简单的保护变量之类的东西呢?在您的代码中:

    var ready = false; // 1) the guard
    
    app.get('/data', function(req, res) {
        if (ready) { // 2) view execution blocked until guard is ready
            res.render(__dirname + '/well.html');
        }
    });
    
    app.get("/run", function(req, res) {
        var pyshell = new PythonShell('script2.py');
        pyshell.send(JSON.stringify(thelist))
        pyshell.on('message', function (message) {
            // received a message sent from the Python script (a simple "print" statement)
            jsondata += message
        });
    
        // end the input stream and allow the process to exit
        pyshell.end(function (err) {
            if (err) {
                throw err;
            };
            ready = true; // 3) if evetything is OK the guard is now ready
            console.log('finished');
        });
        res.redirect('/')
        thelist = []
    });
    

    【讨论】:

    • 1- 为单线程服务器使用全局变量将永远无法正确处理多个用户。 2- 这没有回答禁用链接的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    • 2018-11-17
    相关资源
    最近更新 更多