Struggling to get callback to work express.js - node.js

Trying to make sure that request has completed before rendering page
Overview of app - submit for with code, make request, populate results page
//Index.js
var response = require('./requestMapping')
// home search page
exports.index = function(req, res){
res.render('index', { stationDisplay: response.station_id, test: 'rob' });
};
**//post from form to call METAR service
exports.post =('/', function(req, res, next) {
response.getMETAR(req.body.query,function(){
res.render('results', {
stationDisplay: response.station_id,
test: 'rob'
});
});
})**
//Index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= stationDisplay %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>Enter ICAO code to get the latest METAR</h1>
<form method="post" action="/">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>
Module to call webservice - requestMapping.js
/**
* #author robertbrock
*/
//Webservice XML
function getMETAR(ICAO){
var request = require('request');
request('http://weather.aero/dataserver_current/httpparam?datasource=metars&requestType=retrieve&format=xml&mostRecentForEachStation=constraint&hoursBeforeNow=24&stationString='+ICAO, function(error, response, body){
if (!error && response.statusCode == 200) {
var XmlDocument = require('xmldoc').XmlDocument;
var results = new XmlDocument(body);
console.log(body)
console.log(ICAO)
exports.station_id = results.valueWithPath("data.METAR.station_id");
//etc.
}
})
}
exports.getMETAR =getMETAR;

I can't see that your getMETAR function actually takes a callback function? I would expect it to be:
function getMETAR(ICAO, callback) {
// Do your work
var station_id = results.valueWithPath("data.METAR.station_id");
callback(null, station_id); // It's common to use the first arg of the callback if an error has occurred
}
Then the code calling this function could use it like this:
app.post('/', function(req, res) {
response.getMETAR(req.body.query, function(err, station_id) {
res.render('results', {stationDisplay: station_id, test: 'rob'};
});
});
It takes some time getting used to async programming and how the callbacks work, but once you've done it a few times, you'll get the hang of it.

Without seeing the rest of your code it hard to guess, but your code should look more like:
app.post('/', function(req, res, next) {
response.getMETAR(req.body.query,function(){
res.render('results', {
stationDisplay: response.station_id,
test: 'rob'
});
});
});

Related

express force refresh client webpage

Im trying to refresh a clients webpage (using a router) and everywhere I look I see something along the lines of using res.redirect(same page link of some sort), however for some reason this isnt working for me, do you know of any alternatives?
my code looks something like this
router.post('/sendSnippet', function (req, res) {
req.on('data', function(data) {
User.findOne({email: req.user.email}).then((userToEdit) =>{
if(userToEdit){
var newSnippet = {
"types":[],
"code": data.toString()
}
userToEdit.snippets.push(newSnippet)
userToEdit.save().then(()=>{
//refresh here
res.redirect('/profile/');
})
}
})
})
});
thanks for any help in advance
Assuming you are trying to force-reload a client's browser tab for your website, I don't think you can do that server-side.
You can use meta http-equiv or the Refresh HTTP header to tell the client's browser to refresh the page after some time or use client javascript to refresh the page:
Router:
router.post("/example", (req, res) => {
res.header("Refresh", "10"); // tells the browser to refresh the page after 10 seconds
res.send("your data");
});
Meta:
<head>
<!-- Refresh after 10 seconds -->
<meta http-equiv="refresh" content="10">
</head>
Javascript + html:
<html>
<body>
<script>
// reloads after 10 seconds
setTimeout(() => {
location.reload();
}, 10000);
// or you could have some kind of API to tell when to refresh the page
function check() {
const x = new XMLHttpRequest();
x.open("GET", "some path");
x.send();
x.onload = function() {
if (x.response === "done") {
location.reload();
} else {
setTimeout(check, 1000);
}
}
}
check();
</script>
</body>
</html>

AngularJS, special character from Json via server

I'm trying to create a small multi-language project using NodeJS version 10.15.1, AngularJS version 1.5.8 and UTF8 encoded html. I should proceed with my own function instead of using other modules.
I created 2 different json files containing 2 different languages. The json is loaded via server using a $http call and the answer is stored inside a $scope variable.
$http.post(apihost + '/languages/language_frontend', {page: "home"}).then(function(language) {
$scope.language = language.json;
});
I pass the parameter page to filter with part of the json the function should retrieve.
router.post('/language_frontend', function(req, res, next) {
return new Promise(function(resolve,reject) {
if(config.language == 'it') return res.json({status: 'ok', json: italian_frontend[req.body.page]});
else if(config.language == 'en') return res.json({status: 'ok', json: english_frontend[req.body.page]});
});
});
This is (part) of one of the json
{
"home": {
"planning": "Pianificazione",
"activities_planning": "Pianificazione Attività"
},
"login": {
"test_one": "italiano uno",
"test_one": "italiano due"
}
}
And this is the html that displays the information
<div class="panel-heading">
<div class="row">
<div class="col-xs-3"><i class="fa fa-mobile-phone fa-5x"></i></div>
<div class="col-xs-9 text-right">
<div class="huge ng-binding">{{language.activities_planning}}</div>
</div>
</div>
</div>
The problem is that the displaying of activities_planning comes with an accented character and, coming from server side call, I don't know how to display it correctly. I'd like a general solution to implement everywhere, so I don't have to worry about few exceptions with special characters.
This is the result without a solution: Pianificazione Attivit�
Any suggestion?
So, here it is https://glitch.com/edit/#!/angularjs-specialchars. I tried to set up the same thing with you :
In my app.js on the backend, I get the content of JSON file and expose it in /language route :
const path = require("path");
const express = require("express");
const app = express();
const language = require("./test.json");
app.use('/', express.static(path.join(__dirname, 'public')));
app.get('/language', (req, res) => res.json({ status: "ok", json: language }));
app.listen(5000, function() {console.log("Server is running on port 5000")});
In my index.js on the client-side, I send a request to the server to get the JSON file :
angular.module("app", []).controller("MyController", ["$scope", "$http",
function ($scope, $http) {
// send request to get the json
$http.get('/language').then(function (resp) {
const language = resp.data.json;
console.log(language); // I've checked on the console.log, the text is OK
$scope.text = language.test; // bind to screen
});
}
]);
And in my index.html I just use it :
<body ng-app="app">
<div ng-controller="MyController">
<h1>Hello {{text}}!</h1>
</div>
</body>
What I have :

My simple code doesn't work with express-validator

I am studying about express-validator.
'router.post' passes without the validation
I made form and password input in html.
<body>
<h1>Hello Guys!</h1>
<p>Fill in your id number.</p>
<form action="/yournumber/sub" method="POST">
<input type="password" name="password">
<button type="submit">Summit</button>
</form>
</body>
And that password call router.post
This is my router code.
but this post runs redirect regardless of the length of password.
How could I make correctly?
router.post('/yournumber/sub', (req, res, next) => {
req.check('password').isLength({min: 4});
var errors = req.validationErrors();
if (errors) {
req.session.errors = errors;
req.session.success = false;
console.log('error');
} else {
req.session.success = true;
console.log('suc')
}
res.redirect('/ab');
});
I don't know the req.check()-notation and can't find it in the official documentation, but:
The way I use this module is as an express middleware. You can check the snipped on the linked documentation. Here is your adapted code:
const validation = [
check('password').isLength({min: 4})
];
router.post('/yournumber/sub', validation, (req, res, next) => {
var errors = validationResult(req);
if (errors.isEmpty()) {
// valid
} else {
// not valid
}
});
As you can see, the validation-array is passed in as another handler for the route. It works the same way as your handler:
Check if the parameter is valid, as specified to the check()-function
If not, add information to the req-object
Call next() to get to either the next validation or your handler.
Then, when you want to check the results, use validationResult() (which you spelled wrong and called as a method of req instead of passing it to the method).

Nunjucks Async Rendering with Keystone View

In an Express-based app, I'm running an async function to fetch image source from a database and render them into a Nunjucks view engine
Nunjucks is rendering the following in the DOM
<img src="[object Promise]">
I have already enabled Nunjucks's async rendering with web: { async: true } and enabled the nunjucks async api with a callback like so
// controller.js (KeystoneJS app)
view.render('index', function (err, res) {
console.log('err at index render', err); // undefined
return res;
});
How can i get the resolved value of my async function?
As I understand it, Nunjucks doesn't support asynchronous render directly. You can use asynchronous filters to get it. Maybe I'm wrong.
Imho, use feature with Be Careful! mark is a not good idea.
// template.njk
Hello {{user_id | find | attr('name') }}!
// app.js
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
// Async filter
env.addFilter('find', function(a, cb) {
setTimeout(function () {
cb(null, {
name: 'Smith'
});
}, 10);
}, true)
// Sync filter
env.addFilter('attr', function(obj, attr) {
return obj && attr && obj[attr];
});
env.render('template.njk',
{user_id: 1}, // pass sync vars
function(err, res) {
if (err)
return;
console.log(res);
return res
}
);
I don't know about nunjucks, but you can implement async functionality regardless of the view engine being used. To show the idea, I tried to reproduce your situation. I created an HTML file named index.html with an img tag without any src attribute:
<html>
<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>Reproduce</title>
</head>
<body>
<img id='bg'></img>
<script src='./so.js'></script>
</body>
</html>
I have a <script> tag in my HTML which links to my so.js file shown below by which an image is requested by sending a HTTP request to NodeJS/Express server:
getImage();
function getImage(){
// Get an image by its name as URL parameter
fetch('/bg/background.jpg',{
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then(result=>{
return result.blob()
}).then(result=>{
console.log('result -> ', result)
document.querySelector('#bg').src=URL.createObjectURL(result)
document.querySelector('#bg').style.width='200px'
}).catch(err=>{
console.log('err -> ', err)
})
}
Here is my NodeJS/ExpressJS code inside a file name server.js:
express=require('express')
bodyParser=require('body-parser')
path=require('path')
fetch=require('node-fetch')
server=express()
//Body-parser middleware
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({extended:false}))
server.use(bodyParser.raw())
//Set static path
server.use(express.static(path.join(__dirname,'public')))
server.get('/',(req,res)=>{
res.render('index.html')
})
// Pick a file on hard disk
// and send it as "Blob" to the browser
server.get('/bg/:name',(req,res)=>{
var options = {
root: __dirname + '/public/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
var fileName = req.params.name;
res.sendFile(fileName, options, function (err) {
if (err) {
next(err);
} else {
console.log('Sent:', fileName);
}
});
})
server.listen('8000',()=>{
console.log('Server listening on port 8000...')
})
As can be seen, I'm doing my async communication between browser and server by implementing fetch API and without even touching the view engine.
I just wanted to provide an alternative idea to use fetch API and do the async HTTP communications with it regardless of any view rendering engine that is being used.

Delete a user with Mongoose, Express

I am new to Express and MongoDB. I created a small web app in Node.js and am using Express.js and Mongoose. I can succesfully create a user and have a user sign in but I am having trouble with a user being able to delete their account.
I have a user.js file in my routes folder which is where I am writing the code to signup, signin, delete, etc. Here is a link to the project on GitHub ( https://github.com/NicholasGati/shopping-cart-2 ). The button to delete a user's account is in views/user/edit.hbs. I put the button in a form. When I click the button, the user is not deleted and I am redirected to '/' for some reason. Note: '/:id' in my routes/user.js file becomes '/user/:id'.
Here is the code in the routes/user.js file for the delete method:
router.delete('/:id', isLoggedIn, (req, res, next) => {
User.findOneAndRemove({_id: req.params.id}, (err) => {
if (err) {
req.flash("error", err);
return res.redirect("/user/edit");
}
req.flash("success", "Your account has been deleted.");
req.logout();
return res.redirect("/shop/coffee");
});
});
Here is the form in views/user/edit.hbs:
<form action="/user/{{user.id}}" method="delete">
<div class="form-group">
<button type="submit" class="btn btn-danger">Delete Account</button>
</div>
</form>
Also, here is the isLoggedIn function:
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/");
}
Since you are new I think I should lead you to find the problem yourself:)
Make sure about form methods.
Make sure the route for user deletion is called.
If the markup doesn't seem right I am sorry cas I am using my phone to post this answer.
I had this exact same issue. I used an XMLHttpRequest from the client side in order to do this. I'm sorry I'm not experienced enough to explain why it worked this way and not from the node end, but it may have to do with form data being inherently designed to pass information, not delete information. In any case, try this solution.
In your client side code:
Your button code (form action shouldn't matter, and for that matter, the tag shouldn't either, since the logic is handled in the JS, but this is what I used):
<button id = "del-btn" class="btn btn-danger">Delete</button>
Script to send HTTP request from the button click, this code should go in the same file as your button above, or as an include JS file that the HTML page has imported:
<script>
var del_btn = document.getElementById("del-btn");
del_btn.addEventListener("click", function(e) {
var user = <%- JSON.stringify(user) %>;
var xhr = new XMLHttpRequest();
xhr.open("DELETE", "/user/" + user._id);
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
window.location.href = "/users";
}
};
xhr.send();
//make XMLHttpRequest to delete the poll here
}, false);
</script>
in your server side route, note how the response is just a success code. It's the XMLHTTP Request from the client side that does the redirection:
app.delete('/user/:id', isLoggedIn, function(req,res){
User.remove({
_id: req.params.id,
ownerID: req.user._id
}, function (err, user) {
if (err)
return console.error(err);
console.log('User successfully removed from polls collection!');
res.status(200).send();
});
});

Resources