Jason
Jason

Reputation: 2915

Combining client-side jQuery and server - side Express

I'm learning Express / Node. I'm trying to build a simple HTML form which I validate client - side (e.g, with jQuery / AJAX) but I also serve from the server - side (with Express.js). To be clear, the app is now simple enough that I could do everything client - side, but I want to build server - side behavior to learn the entire pipeline.

Here's the form. Note the button of type button with name change-color; this is a button that I use for debugging purposes, explained later. I am also including jquery v.3.6.0 and my own client.js file.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>A MWE</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <h1>A simple form.</h1>
    <form action="/" method="post">
      <input type="text" name="num1" placeholder = "First Number" value=""> <br>
      <input type="text" name="num2" placeholder = "Second Number" value=""> <br>
      <button type="submit" name="submit">Calculate</button>
      <button type="button" name="change-color">Change color</button>
    </form>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="client.js" charset="utf-8"></script>
  </body>
</html>

In the form, the user is supposed to give me two numbers, which the server will then sum and return. Server code is inside a file called server.js, outlined below:

const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({extended:true}));

app.listen(3000, () => {
  console.log("Server spun on port 3000.");
});

app.get("/", (req, res) => {
  console.log("Serving a GET request at root route.");
  res.sendFile(absoluteDirPath() + "index.html");
});

app.post("/", (req, res) => {
  console.log("Serving a POST request at root route.");
  const num1 = Number(req.body.num1);
  const num2 = Number(req.body.num2);
  res.send("<h3>Result: " + (num1 + num2) + "</h3>");
});

function absoluteDirPath() {
  const path = require('path');
  return __dirname + path.sep;
}

When we first access the root endpoint (GET call), the server uses res.sendFile() to send the above HTML file to the user. When the user inputs two numbers on the form and clicks on the button of type submit inside the form, the app.post() of server.js method calculates and send()s the requested sum.

Clearly I need to do some validation before I apply the summation; what if I don't get a number? Based on the answers to this post, to avoid complex and sudden redirections, I seem to have to do this on the client side, e.g, in a separate .js file. In the same post, I have been advised to author something like the following function and assign it to an event listener:

function submitFormWithoutReloading() {
   $.ajax({
      type: "POST",
      url: "/",
      data: {
         num1: $('#idOfYourFirstInput').val(),
         num2: $('#idOfYourSecondInput').val()
      },
      success: function(res) {
         alert(res); // This is what the server returns
      }
   });
}

However, I seem to have stumbled upon a bigger issue here. I am having some trouble running all my jQuery when I serve the user with the HTML file from the app.get() method! It seems as if every time I access http://localhost:3000/, the HTML file is sent to me, but the linked client.js file is not read (and I am guessing that the same thing holds for jQuery).

To ensure that this is what my problem is, I temporarily forgot about the validation routine, made the change-color HTML button mentioned above and added some dummy code to client.js:

$(document).ready(function() {
  alert("Hi!");
});

$('button[name="change-color"]').click(function() {
  $("body").toggleClass("colored-background");
});

So the change-color button is used just to toggle the body element to have yellow as its background as per this CSS rule:

.colored-background {
  background-color: yellow;
}

If I open index.html from my filesystem, it all works just fine; jQuery and my client.js file are loaded, the alert() comes just fine, and the button works as intended. But when I access localhost:3000 and the GET endpoint serves the client with the HTML file, it's as if the DOM scanning does not read client.js and I can't do any client - side stuff, including my much desired validation!

Any ideas of what I'm doing wrong? I know I could do everything on the client side here, but I want to learn how to split the concerns on client side and server side. Server side might want to access a DB, the client can't do everything.

// Edit: If it helps, the Chrome console says some interesting things when I load localhost:3000:

Output of Chrome console when I access localhost:3000.

Upvotes: 0

Views: 549

Answers (1)

code
code

Reputation: 6319

You didn't do anything wrong. This is just a common problem that everyone using node.js (including me! Cant access other files in Node.js).

You have to tell express which files can be viewed by the client. Let's create a folder called public. Inside your folder, put you .js and .css files, as well as other pictures or other assets you want accessible by the client side.

This folder, public, on your website will be replaced with your domain. Consider these files/folders:

index.js (server)
/ views
   - index.html
/ public
   - style.css
   - client.js
   - jquery.min.js

The client can only access the files inside public, like this: http://localhost:3000/style.css, or http://localhost:3000/client.js, or http://localhost:3000/jquery.min.js. However, we will not be able to access the other files (not in the public folder), unless the server explicitly gets it:

app.get('/index.html', function(req, res) {
   res.sendFile(path.join(__dirname, '/views/index.html'));
});

Solution:

// ...
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, '/public')));
// You routes here

This way, the client will be able to access all the files under the public directory. Your problem should be solved.

Keep in mind: The server can still access all the files. This only restricts the client side.

Upvotes: 1

Related Questions