I wrote this small Express server to get data from an HTML form and query OpenWeatherMap with that data:
const { OpenWeatherAPI } = require("openweather-api-node");
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
const PORT = process.env.port || 3000;
app.use(bodyParser.urlencoded({ extended: true }));
app.listen(PORT, () => console.log(`Server listening on port ${PORT}.`));
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "/public/views/index.html"));
});
app.post("/weather/public", (req, res) => {
let city = Number(req.body.city);
let units = (req.body.units || "").trim().toUpperCase();
if (!["IMPERIAL, METRIC, STANDARD"].includes(units)) {
$("h1").innerText =
"Please input Imperial, Metric or Standard for the units.";
return;
}
let weather = new OpenWeatherAPI({
key: MY_OPENWEATHERMAP_API_KEY,
locationName: city,
units: units,
});
weather.getCurrent().then((data) => {
res.send(
`<p>Current temperature in ${city} is: ${data.weather.temp.cur}</p>`
);
});
});
The HTML itself is very simplistic:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Weather query</title>
</head>
<body>
<h1>Input a city and your units!</h1>
<form action="/weather/public" method="post">
<input name="city" placeholder="Your city here" type="text" />
<input
name="units"
placeholder="Units of Measurement (imperial, metric, standard)"
type="text"
/>
<button name="submit" type="submit">Calculate!</button>
</form>
</body>
<script
crossorigin="anonymous"
integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ="
src="https://code.jquery.com/jquery-3.6.1.min.js"
></script>
<script charset="utf-8" src="../../app.js"></script>
</html>
When running the server and inputting a few items in the form, I get:
ReferenceError: $ is not defined
at C:\Users\jason\code\Web-Development\weather\app.js:19:5
at Layer.handle [as handle_request] (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\route.js:144:13)
at Route.dispatch (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\route.js:114:3)
at Layer.handle [as handle_request] (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\index.js:284:15
at Function.process_params (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\index.js:346:12)
at next (C:\Users\jason\code\Web-Development\weather\node_modules\express\lib\router\index.js:280:10)
at C:\Users\jason\code\Web-Development\weather\node_modules\body-parser\lib\read.js:137:5
at AsyncResource.runInAsyncScope (node:async_hooks:204:9)
Line 19 is the only location in the .js file where jquery is used:
$("h1").innerText = "Please input Imperial, Metric or Standard for the units.";
I changed the location of the <script> tag for jquery to be inside the <head> block but this did not help. I'm wondering whether it is even possible for me to use jquery inside Express' handlers or whether this is impossible and I should instead send an error HTML file as a response.
Answered by Pointy in the commments; effectively I can't be mixing up calls to client-side jquery from server-side Express code.
Related
I'm trying to build a login page for users and need to send the login info to my express server. I'm not too sure about how to call the api for my server and send the information across though.
Right now I get a 405 error method not allowed when I try to submit the form by clicking on the button.
index.html:
<!DOCTYPE html>
<html land="en">
<head>
<meta charset="UTF-8">
<title>Home page</title>
<script src="../app.js"></script>
</head>
<body>
To login
</body>
</html>
login.html:
<!DOCTYPE html>
<html land="en">
<head>
<meta charset="UTF-8">
<title>Login page</title><br><br>
<script src="../app.js"></script>
</head>
<body>
<form id="login-form" method="post" action="/login">
<input type="text" name="username" id="username-field" placeholder="Username" required><br><br>
<!-- TODO: Change type to "password" -->
<input type="text" name="password" id="password-field" placeholder="Password" required><br><br>
<input type="submit" value="Login" id="login-form-submit">
</form>
</body>
</html>
app.js:
const express = require('express')
var bodyParser = require('body-parser')
var port = process.env.PORT || 3000;
var app = express();
app.use(express.json());
app.use(express.urlencoded({"extended": true}));
app.get('/', function (req, res) {
res.render(__dirname + '/views/index.html');
});
app.post('/login', function (req, res) {
res.statusCode = 200;
res.send({ 'body': req.body });
});
// Listen on port 3000, IP defaults to 127.0.0.1
app.listen(port, () => {
console.log('Server listening at http://127.0.0.1:' + port + '/');
});
You can do it using javascript. Add this to your login.html file
<script type="text/javascript" >
async function login(){
// Get values from FORM
let username = document.getElementById("username-field")
let password = document.getElementById("password-field")
// Body payload
let body = { username, password }
// Fetch options
let option = {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json'
}
}
// Call /login as POST and wait for reply
const response = await fetch(`/login`, options)
const json = await response.json()
// Use json response
alert(json)
}
document.getElementById("login-form-submit").addEventListener("click", login);
</script>
In order for the server to be properly up, you need to make sure you run it as node app.js instead of adding it to the html file.
UPDATE:
Check out this repo for a working solution : https://github.com/rui-costa/stackoverflow-68238492
Please educate me about how am I going to connect the CSS to the server. I've followed two tutorials on youtube, one is using node.js and nodemailer. With this, I use a localhost to run my website but the CSS and js I made from the second tutorial (pop-up when button is clicked) won't work on the localhost but when I clicked the html file itself.
Is this because the tutorials are for different kinds of website? like static and dynamic?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewpoint" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet" type="text/css" />
<title>Document</title>
</head>
<body>
<h1>Welcome to my App</h1>
<form>
<div>
<label for="email">Sender's Email: </label>
<input type="email" id="email" placeholder="Your Email"> <br>
</div>
<div>
<label for="classNum">To whom: R</label>
<input type="number" id="classNum" placeholder="class#" min="1" max="31"> <br>
</div>
<div>
<label for="subject">Subject: </label>
<input type="text" id="subject" placeholder="Subject"> <br>
</div>
<div>
<label for="text">Letter: </label> <br>
<textarea name="text" id="text" cols="30" rows="10"></textarea> <br>
</div>
<input type="submit" value="Submit" class="modal-button" data-modal-target="#modal">
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">Letter Sent!</div>
</div>
<div class="modal-body">
Pressing this button will refresh the page.
<div><button data-close-button class="refresh-button">Send another letter</button></div>
</div>
</div>
<div id="overlay"></div>
</form>
<script src="script.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$('form').on('submit', (e) => {
e.preventDefault();
const email = $('#email').val().trim();
const subject = $('#subject').val().trim();
const text = $('#text').val().trim();
const classNum = $('#classNum').val().trim();
const data = {
email,
subject,
text,
classNum
};
$.post('/email', data, function(){
console.log('Server received our data')
});
});;
</script>
</body>
</html>
This is the server.js
const express = require('express');
const sendMail = require('./mail')
const log = console.log;
const app = express();
const path = require('path');
const PORT = 8080;
app.use(express.urlencoded({
extended: false
}));
app.use(express.json());
app.post('/email', (req, res) => {
const { subject, email, text, classNum} = req.body;
console.log('Data: ', req.body);
sendMail(email, subject, text, classNum, function(err, data){
if (err){
res.status(500).json({ message: 'Internal Error'});
}
else{
res.json({ message: 'Email sent!' });
}
});
// res.json({ message: 'Message received!' })
});
app.get('/', (req, res) =>{
res.sendFile(path.join(__dirname, 'views', 'index.html'));
});
app.listen(PORT, () => log('Server is starting on PORT: ', 8080));
And this one is for the pop-up, script.js
const openModalButtons = document.querySelectorAll('[data-modal-target]');
const closeModalButtons = document.querySelectorAll('[data-close-button]');
const overlay = document.getElementById('overlay');
var path = require('path') //from stackoverflow
app.use(express.static(path.join(__dirname, 'public')));
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = document.querySelector(button.dataset.modalTarget)
openModal(modal)
})
})
closeModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal')
closeModal(modal)
})
})
function openModal(modal) {
if (modal == null) return
modal.classList.add('active')
overlay.classList.add('active')
}
function closeModal(modal) {
if (modal == null) return
window.open("https://www.w3schools.com");
}
Please tell me if I need to include the CSS and the mail.js .
If you want to allow users or browsers to get files from your server, you need to add them to your server-side code. For example, you've added a stylesheet reference to your index.html, so the browser will try to get that file (/style.css) from the server. You haven't put any reference to this on the server side, so the server will respond with 404 Not Found or another error.
In order to make the server respond to a request for "/style.css", you need to add the following to your server-side index.js:
app.get("/style.css" /*name of file in index.html*/, (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'style.css')); //CHANGE THIS TO THE NAME OF THE FILE ON THE SERVER
});
The same needs to happen for your browser script, script.js:
app.get("/script.js" /*name of file in index.html*/, (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'script.js'));
});
app.get tells express to respond to a GET request to the first parameter: in the example, that's "/style.css". If you wanted to respond to a GET request to "/foobar", then you would write app.get("/foobar", (req, res) => {/*SOME CODE HERE*/}); . The reason why it wasn't working was becuase when the browser tried to find style.css and script.js, the server didn't know what to do because you hadn't included app.get for those files, and therefore responded with an error.
This might be confusing due to the architecture of how this works. Look at this diagram:
==== HOW A WEBSITE SENDS A FILE ====
______ ____ ______
/ . . \ GET /file [____] READ / |
| j | ======> [____] ======> | file |
\__===_/ <====== [____] <====== | |
user RESPONSE server |_______|
When I click the subscribe button it redirects my page to 127.0.0.1 and nothing appears in console.log. Why is that
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Abonelik</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="signin.css">
</head>
<body>
<div class="parent-wrapper">
<span class="close-btn glyphicon glyphicon-remove"></span>
<div class="subscribe-wrapper">
<h4>Abone Olun</h4>
<form action="/" method="POST">
<input type="text" name="fname" class="subscribe-input" placeholder="Adınız">
<input type="text" name="lname" class="subscribe-input" placeholder="Soyadınız">
<input type="email" name="email" class="subscribe-input" placeholder="Mail Adresiniz">
<button type="submit">Abone Ol</button>
</form>
</div>
</div>
</body>
</html>
Nodejs
//jshint esversion:6
const express = require("express");
const bodyParser = require("body-parser");
const request = require("request");
const https = require("https");
const app = express();
app.use(express.static("public"));
app.use(bodyParser.urlencoded({
extended: true
}));
app.get("subs", function(req, res) {
res.sendFile(__dirname + "/signup.html");
});
app.post("/", function(req, res) {
const mail = req.body.email;
const ilkad = req.body.fname;
const soyad = req.body.lname;
console.log(fname, lname, email);
const data = {
members: [{
email_address: mail,
status: "Subscribed",
merge_fields: {
FNAME: fname,
LNAME: lname,
}
}]
};
const jData = JSON.stringify(data);
const url = "https://usX.api.mailchimp.com/3.0/lists/";
const option = {
method: "post",
auth: ""
};
const request = https.request(url, options, function(response) {
response.on("data", function() {
console.log(JSON.parse(data));
});
});
request.write(jData);
request.end();
});
app.listen(3000, function() {
console.log("Server Çevrimiçi");
});
I just wanted to apply the subscribe button a function to send data to the hyper terminal when the user logs mail name surname etc. but when I click it redirects me to 127.0.0.1 which is local folder. and I am almost sure that there is something wrong between app.get and form direction but I don't know how to solve this problem. I know it is a long post sorry for that but I really appreciate some help. thank you.
You are printing the wrong variables. Since you do this:
const mail = req.body.email;
const ilkad = req.body.fname;
const soyad = req.body.lname;
you should print the data from request by:
console.log(ilkad, soyad, email);
Also, a good practice is using the object destructuring like this:
const {email, fname, lname} = req.body
and then use:
console.log(fname, lname, email);
I cannot access the supplied url param in my delete endpoint and i have no idea why. GET, POST and PUT works just fine.
const express = require("express");
const app = express();
app.use(express.json());
const courses = [
{id: 1, name: 'course1'},
{id: 2, name: 'course2'},
{id: 3, name: 'course3'},
];
app.delete('/api/courses/:id', (res, req) => {
console.log(req.params.id);
});
I do a DELETE in Postman supplying the url "localhost:5000/api/courses/1" and i get the response:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>TypeError: Cannot read property 'id' of undefined
<br> at app.delete (C:\Users\Ove\WebstormProjects\Vidley\index.js:84:28)
<br> at Layer.handle [as handle_request] (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\layer.js:95:5)
<br> at next (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\route.js:137:13)
<br> at Route.dispatch (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\route.js:112:3)
<br> at Layer.handle [as handle_request] (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\layer.js:95:5)
<br> at C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\index.js:281:22
<br> at param (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\index.js:354:14)
<br> at param (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\index.js:365:14)
<br> at Function.process_params (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\index.js:410:3)
<br> at next (C:\Users\Ove\WebstormProjects\Vidley\node_modules\express\lib\router\index.js:275:10)
</pre>
</body>
</html>
Could anyone please explain to me what is going on here? Only the DELETE endpoint acts like this, all others are fine
Express passes your handler (req, res), not (res, req), so your req is actually res and doesn't have a .params field.
I am trying to understand where to locate the logic to send an email via a contact form in my Angular App (using angular-fullstack MEAN stack from Yeoman).
I can add the logic to send an email in the app.js file on the server side using nodemailer and sendgrid and everything works and an email is sent every time I refresh the server, however I am a little fuzzy on where to place the logic so that it only gets sent once the form is submitted and it hits the server side.
This is what the create action looks like on the Express JS side...
exports.create = function(req, res) {
Contact.create(req.body, function(err, contact) {
if(err) { return handleError(res, err); }
return res.json(201, contact);
});
};
Here is the code in app.js that is working, but obviously not in the right place...
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var options = {
auth: {
api_user: 'username', // 'SENDGRID_USERNAME' - Recommended to store as evn variables
api_key: 'password', // 'SENDGRID_PASSWORD'
}
};
var mailer = nodemailer.createTransport(sgTransport(options));
var email = {
to: 'sendto#email.com',
from: 'sendfrom#email.com',
subject: 'Test Email',
text: 'Awesome Email',
html: '<b>Bold and Awesome Email</b>'
};
mailer.sendMail(email, function(err, res) {
if (err) {
console.log(err)
}
console.log(res);
});
Coming from a rails background my initial thought is to stick the logic in the create action so that if the object is created successfully the email gets sent. Is this a correct way of thinking about it in this scenario...I am new to the MEAN stack.
Thanks for any help...
You need to create a route on the server that you can post form values to from Angular using $http.post.
The following lets you enter details in an Angular form. The form is then posted to Node where the req.body values are extracted and added email object. The email is then send by SendGrid.
SERVER.JS
var express = require('express');
var http = require('http');
var bodyParser = require('body-parser');
var dotenv = require('dotenv');
dotenv.load(); //load environment variables from .env into ENV (process.env).
var sendgrid_username = process.env.SENDGRID_USERNAME;
var sendgrid_password = process.env.SENDGRID_PASSWORD;
var sendgrid = require('sendgrid')(sendgrid_username, sendgrid_password);
var email = new sendgrid.Email();
var app = express();
app.use(bodyParser.json()); //needed for req.body
app.set('port', process.env.PORT || 3000);
app.use(express.static(__dirname + '/public'));
app.post('/email', function(req, res) {
email.addTo(req.body.to);
email.setFrom(req.body.from);
email.setSubject(req.body.subject);
email.setText(req.body.text);
email.addHeader('X-Sent-Using', 'SendGrid-API');
email.addHeader('X-Transport', 'web');
sendgrid.send(email, function(err, json) {
if (err) {
return res.send("Problem Sending Email!!!!");
}
console.log(json);
res.send("Email Sent OK!!!!");
});
});
var server = http.createServer(app);
server.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port') ) ;
});
INDEX.HTML
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title></title>
<!-- CSS -->
</head>
<body ng-controller="MainCtrl">
<form name="emailForm">
<div class="group">
<input type="email" name="to" ng-model="email.to" ng-required="true">
<label>To</label>
</div>
<div>
<input type="email" name="from" ng-model="email.from" ng-required="true">
<label>From</label>
</div>
<div>
<input type="text" name="subject" ng-model="email.subject" ng-required="true">
<label>Subject</label>
</div>
<div>
<textarea ng-model="email.text" name="text" placeholder="Enter Text Here.."></textarea>
</div>
<button id="emailSubmitBn" type="submit" ng-click="submitEmail()">
Submit
</button>
</form>
<!-- JS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
CLIENT SIDE APP.JS
angular.module('myApp', [])
.controller('MainCtrl', function($scope, $http) {
$scope.submitEmail = function() {
console.log("TEST");
//Request
$http.post('/email', $scope.email)
.success(function(data, status) {
console.log("Sent ok");
})
.error(function(data, status) {
console.log("Error");
})
};
});