EJS view will not render when triggered via fetch API - javascript

in my client javascript I am using the following function, which is triggered by an onclick event
function submitForm(event) {
const data = { name, image_url };
console.log(data);
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
}
The function above sends a post request to the expressJS backend of the app which triggers another function that is supposed to render one of two EJS views. here is the expressJS function
router.post('/', function (req, res) {
console.log('hitting router post');
var { name, imageURL } = req.body;
console.log(name, imageURL);
if (nicknameIsAvailable(name)) {
let user = {
nickname: name,
id: '',
image: imageURL,
};
console.log("new user connecting")
res.cookie('nickname', name, { signed: false });
res.render('chat', { nickname: name });
} else {
console.log('rejecting user ' + name + ' is already taken');
res.render('index', { message: userTakenErrorMessage });
}
});
problem is that res.render does not actually render the EJS view. I think what is happening is that it's sending back the HTML markup of the view to the client side javascript. The desired behavior would be for it to render the "chat" or "index" views with the given arguments. How can I achieve that?
If this approach does not work then what approach could I use to add some data to the body of a request without using a form and then having that request trigger rendering a view?

This is an ajax request, you will receive rendered HTML in the resolved promise of fetch function. To display the response content use:
fetch(...).then(res => res.text()).then(htmlStr => {
document.open();
document.write(htmlStr);
document.close();
})

Related

access a variable from callback function NodeJS

Here is a simplified code of my payment service in node.js:
async function pay({ package, via }) {
const options = {
api: 'test',
factorNumber: '123456789',
description: `package-${package}`,
redirect: 'http://localhost:4000/accounts/pay/callback', // this will hit router.get('/pay/callback', payCallback);
}
const response = await axios.post('https://pay.ir/pg/send', options, {
headers: {'content-type': 'text/json'}
});
return { redirect: `https://pay.ir/pg/${response.data.token}` };
}
// this function is executed when we redirect to http://localhost:4000/accounts/pay/callback as I explained above
async function payCallback(req, res) {
// how can I access 'via' here
}
As you see in the first function named pay I have access to via variable which is the email or phone number of the user who wants to pay, ok?
The payment API I'm using just allow the options in the pay function to be accessible from the payCallback (this one is a function which fires at successful payment).
But I need to know who paid and check the database to insert the new payment for the user right?
So I need to access via inside the payCallback...
How can I access via inside payCallback function?
I assume the required data would be sent by your payment API in the payCallback's request body. Check their documentation.
If that isn't actually the case, you could insert the pending payment into a database with a unique ID, then add that unique ID to your redirect url:
async function pay({ package, via }) {
const paymentId = insertIntoDatabase(...);
const options = {
api: 'test',
factorNumber: '123456789',
description: `package-${package}`,
redirect: `http://localhost:4000/accounts/pay/callback?paymentId=${paymentId}`,
}
const response = await axios.post('https://pay.ir/pg/send', options, {
headers: { 'content-type': 'text/json' }
});
return { redirect: `https://pay.ir/pg/${response.data.token}` };
}
async function payCallback(req, res) {
const { paymentId } = req.query;
if (!paymentId) {
// Unexpected, log an error or so. Tell customer to contact customer service
return;
}
const paymentInfo = getFromDatabase(paymentId);
if (!paymentInfo) {
// Also unexpected, so again log the error and tell the customer to contact you
return;
}
// Do whatever with paymentInfo
}
If strictly speaking you only need via, you could add that as a query parameter instead of working with a database. But when it comes to payments, having some logging is a good idea anyway.

Add parameters to post request using js on client side

I'm developing a "TODO" app using node.js and mongodb.
I'm trying to post a new task from the client but I didn't success to pass parameters to the server and from there to the database.
Client code:
<script>
function addData(item, url) {
var text = document.getElementById("myTask").value;
return fetch('/todos',{
method: 'post',
body: text
}).then(response => response.json())
.then(data => console.log(data));
}
</script>
Server code:
app.post('/todos',(req,res) =>{
console.log("\n\n req.body is: \n\n",req.body);
var todo = new Todo({
text: req.body.text});
todo.save().then((doc) =>{
res.send(doc);
console.log(JSON.stringify(doc,undefined,2));
},(err) =>{
res.status(400).send(err); //400 = unable to connect
console.log("Unable to save todo.\n\n\n" , err);
});
});
And the problem is that the client doesn't send the body to the server,
and the body is null on the server side:
See the logs here
(as you can see: req.body = {})
In the js code, I tried to pass the body parameter but I guess I did something wrong so I want to know the best way to pass parameters back to the server (not only the body but text, time and etc)
Thank in advance,
Sagiv
I think that you are missing something. Try to use name of param
body: JSON.stringify({data: text})
OR
read here Fetch: POST json data
I used this code:
(async () => {
const rawResponse = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 1, b: 'Textual content'})
});
const content = await rawResponse.json();
console.log(content);
})();
and now I succeeded to pass data to the request.
Thanks everybody

retrieve JSON response from Frisby POST in another function

i got a frisby function
createPOST = function () {
return frisby.post(url, {
body: qs.stringify({
username: data.user,
password: data.password
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then((resp) => {
let respJson = resp.json;
return respJson;
}, (error) => {
console.error("Error:: " + error);
throw error;
});
}
and second function
getRespJson = function () {
createToken().then(function (value) {
console.log("resp::"+value);
});
}
im trying to retrieve this json response in another function, but not able to using frisby. no log is even displaying
If your data coming in the body(that you are expecting) or anywhere, simply store into other variable and then by nested way you able to use it. if you have multiple nested body then in that situation you also able to use it.
I'm using the same with that workaround.
or try to use it by storing that into another file.
Thanks

Delay on file read on newly created file in node.js

I am writing a new json file when I run my results.html page and then using express.static to make the public folder files accessible in the browser.
I have my app working but am having to click the button multiple times in order to have the updated file be written and accessed.
How should I send the JSON info to the browser without writing a file? Or not experience this lag?
Send the data back in response
function setData(req, res) {
var data = '';
req.on('data', function(streamData) {
data += streamData;
});
req.on('end', function() {
fs.writeFile('data/data.json', data, 'utf8', function(err) {
if (!err) {
res.writeHead(201, {
'Content-Type': 'text/json'
});
res.end(data)
} else {
res.writeHead(400, {
'Content-Type': 'text/json'
});
res.end("File write error" + err)
}
})
})
}
access the data from front end
$.ajax({
url: '/set/data',
method: 'POST',
data: data
})
.done(function(responseData) {
// update your ui with responseData
})
.fail(function(error) {
console.log(error)
})

Saving to MongoDB when Socket.io event is emitted

I'm utilizing a MEAN stack and Socket.io to pull images from the real-time Instagram API. Everything is working great, but I now want to begin saving image data to a MongoDB database so I have a "history" of images from locations (rather than simply the most recent photos).
Below is the relevant (working) code I have so far:
Node server-side code to handle new photo updates from Instagram API and emit event to Angular controller:
// for each new post Instagram sends the data
app.post('/callback', function(req, res) {
var data = req.body;
// grab the object_id (as geo_id) of the subscription and send as an argument to the client side
data.forEach(function(data) {
var geo_id = data.object_id;
sendUpdate(geo_id);
});
res.end();
});
// send the url with the geo_id to the client side
// to do the ajax call
function sendUpdate(geo_id) {
io.sockets.emit('newImage', { geo_id: geo_id });
}
Angular controller code when 'newImage' event is received:
socket.on('newImage', function(geo_id) {
// pass geo_id into Instagram API call
Instagram.get(geo_id).success(function(response) {
instagramSuccess(response.geo_id, response);
});
// Instagram API callback
var instagramSuccess = function(scope,res) {
if (res.meta.code !== 200) {
scope.error = res.meta.error_type + ' | ' + res.meta.error_message;
return;
}
if (res.data.length > 0) {
$scope.items = res.data;
} else {
scope.error = "This location has returned no results";
}
};
});
Angular factory to handle calls to Instagram API:
angular.module('InstaFactory', []).factory('Instagram', function($http) {
var base = "https://api.instagram.com/v1";
var client_id = 'MY-CLIENT-ID';
return {
'get': function(geo_id) {
var request = '/geographies/' + geo_id.geo_id + '/media/recent?client_id=' + client_id;
var url = base + request;
var config = {
'params': {
'callback': 'JSON_CALLBACK'
}
};
return $http.jsonp(url, config);
}
};
});
I also have the following Angular Controller which currently GETS details of each location from my Stadia mongoDB model. This model also contains an (empty for now) 'photos' array that I want to PUSH photo details (url, username, user profile url, etc.) onto each time I receive them from Instagram:
angular.module('StadiaFactory', []).factory('Stadia', function($http) {
var base = "http://localhost:6060/api/stadia/";
return {
'get': function(id) {
var request = id;
var url = base + request;
var config = {
'params': {
'callback': 'JSON_CALLBACK'
}
};
return $http.jsonp(url, config);
}
};
});
This is where I get confused. Where do I fire off the PUT request to my Stadia API and does this Node route for my Stadia API look reasonable? Note: I omitted my GET route which works perfectly. PUT is just throwing me for a loop:
// add photos to stadium photos array
app.put('/api/stadia/:stadium_id', function(req, res) {
// use mongoose to get and update stadium
Stadium.findByIdAndUpdate(req.params.stadium_id,
{$push: {"photos": {img: ?, link: ?, username: ?, profile_picture: ?}}},
{safe: true, upsert: true},
function(err, stadium) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.jsonp(stadium); // return stadium in JSON format
});
});
Well there are a few problems with your current structure.
When your callback route is called, with a possibility of N objects in it, you're triggering your socket event and retrieving all the last photos of your geography each time. So let's say you will have 3 new objects, you will call 3 times the same thing to get the same data, which is a bit loss when you have the power of the sockets.
You can also have problems if you try to get the object data from the client-side and PUTing it to your server, since all your clients may receive the socket and you could end-up with duplicates, not to mention that this is a lot of traffic for not much, and this will burn your quota API limit, which is also not safe on the client-side since everyone can see your key.
To me, a good way to get something working (even if I don't really know what your :stadium_id param stands for) is to get the info you want directly on the server side in your callback using the request module.
You should only get the pictures, because you can retrieve a lot of things like users, tags or videos that you may don't want to get. So you will have to listen for the image objects, and nothing else.
You could have something like this:
var request = require('request');
var CLIENT_ID = 'yourId';
function newImage(data) {
io.sockets.emit('newImage', data);
}
app.post('/callback', function (req, res) {
//loop in all the new objects
req.body.forEach(function (data) {
if (data.type !== 'image') { return ; }
//BTW I think you should try with the id property instead of object_id
request('https://api.instagram.com/v1/media/' + data.object_id + '?access_token=' + CLIENT_ID,
function (error, response, body) {
if (error) { return ; }
//Here we have one JSON object with all the info about the image
var image = JSON.parse(body);
//Save the new object to your DB. (replace the STADIUM_ID)
Stadium.findByIdAndUpdate(STADIUM_ID, { $push: {'photos':
{ img: image.images.standard_resolution.url,
link: image.link,
username: image.user.username,
profile_picture: image.user.profile_picture
}}},
{ safe: true, upsert: true });
//Send a socket to your client with the new image
newImage({
id: image.id,
img: image.images.standard_resolution.url,
link: image.link,
username: image.user.username,
profile: image.user.profile_picture
});
}
});
res.end();
});
And then in your client, you will only have to push the new images received in the newImage socket event in the $scope.items.

Categories

Resources