Subscribe returns undefined variable - javascript

So in general, the shopping cart details are stored in local storage, being parsed back and assinged to an array, and then should be subscribed and posted to MongoDB, but returns "undefined":
checkout.ts
confirmOrder(){
let orderDetails: any = {};
orderDetails.firstName = this.checkOutForm.controls['firstName'].value;
orderDetails.lastName = this.checkOutForm.controls['lastName'].value;
orderDetails.phone = this.checkOutForm.controls['phone'].value;
orderDetails.address = this.checkOutForm.controls['address'].value;
orderDetails.country = this.checkOutForm.controls['country'].value;
orderDetails.city = this.checkOutForm.controls['city'].value;
this.orderItem=[];
for (let i in this.productAddedToCart) {
this.orderItem.push({
product_Name:this.productAddedToCart[i].product_Name,
id:this.productAddedToCart[i].id,
product_Price:this.productAddedToCart[i].product_Price,
product_Quantity:this.productAddedToCart[i].product_Price,
type:this.productAddedToCart[i].type,
product_Img: this.productAddedToCart[i].product_Img,
product_Description:this.productAddedToCart[i].product_Description,
_id:this.productAddedToCart[i]._id
});
}
orderDetails.product= this.orderItem
debugger
console.log("orderDetails:", orderDetails.product)
this.connectionService.sendReceipt(this.checkOutForm.value).subscribe((res) =>{
debugger
console.log("res is:", res);
});
this.orderService.CreateOrder(orderDetails).subscribe((data:OrderDetails) => {
debugger
console.log("data is:", data) // prints nothing
debugger
this.globalResponse = data
console.log("data is:", this.globalResponse) //prints nothing
debugger
});
alert('Your order has been received.');
this.checkOutForm.reset();
this.disabledSubmitButton = true;
this.router.navigate(['/']);
localStorage.removeItem('product');
localStorage.removeItem('cartItemCount');
};
this.globalResonse is "undefined".
Why is it not possible to console.log(data)?
Doesnt it get subscribed?
order.service.ts:
CreateOrder(orderDetails:OrderDetails){
return this.http.post(`${this.uri}/order`, orderDetails,{
observe:'body'})
}
My backend if needed(works):
//create order
orderRoute.route('/').post((req, res) =>{
Product.findById(req.body.productId)
.populate('product')
.then(product => {
if(!product){
return res.status(404).json({
message: "product not found"
});
}
const order = new OrderDetails({
_id: new mongoose.Types.ObjectId(),
product: req.body.productId,
email: req.body.email,
firstName:req.body.firstName,
lastName: req.body.lastName,
phone: req.body.phone,
address: req.body.address,
});
return order.save()
})
.then(result => {
console.log(result);
return res.status(200).json({
message: "Order was Created",
order: {
order_id: result._id,
product: result.product,
firstName:result.firstName,
lastName: result.lastName,
email: result.email,
phone: result.phone,
address: result.address,
createdAt: new Date,
},
request:{
type:"GET",
order_url: "http://localhost:5000/order/" + result._id
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error:err
});
});
});
I also receive 2 errors:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
(node:17548) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Much Appreciated!

I think the reason for not to receive any data from executing CreateOrder() because it get an interesting HTTP error. From following this document I got this content and it says,
Error [ERR_HTTP_HEADERS_SENT] is an interesting error that is fired up when a server tries to send more than one response to a client. What this means is that for a given client request the server previously sent a response (either a success response with the resource requested or error response for a bad request) back to the client and now is unexpectedly trying to send another response
So your backend tries to send the response twice. that's the issue here. If you could open the network tab in the browser console you can inspect if it's true.
Hope this helps.

Related

unable to access 200 code status in angular server response

Goal: If the server response with 200 code, I want to perform some changes on the record locally.
issue: I am not able to access the response code or the 'message' attribute
this is how the server response from this http call::
// MongooseModelSchema: this is the model that uses moongose
MongooseModelSchema.updateOne({ _id: req.params.id, creator: req.userData.userId }, channel)
.then(result => {
if (result.n > 0) {
res.status(200).json({ message: "Update successful!" }); // this is what I want to capture
} else {
res.status(401).json({ message: "Not authorized!" });
}
})
.catch(error => {
console.log(error);
res.status(500).json({
message: "Couldn't udpate channel!"
});
});
I am able to hit the API no problem
I have the following http snip in my angular code::
this.http
.put(BACKEND_URL+'setState/' + id, channelData)
.subscribe(response => {
console.log('response',response);
console.log('check if response has Update successful!: ',JSON.stringify(response).search('Update successful!'));
console.log('response.message',response.message);
this.router.navigate(["/editChannel", id]);
})
this is the console.log image:
issue image: as you can see, i dont have access to the return code. I also cant access the 'message' property UNTIL i run the code, then it works.... super weird.
How do I check for the 200 code?
That makes sense. Message object is not typed, so compiler is telling you that message doesn't exist on response. Instead what you should do is the following:
myresponse.ts:
interface MyResponse {
message: string
}
this.http
.put<MyResponse>(BACKEND_URL+'setState/' + id, channelData)
.subscribe(response => {
console.log('response',response);
console.log('check if response has Update successful!: ',JSON.stringify(response).search('Update successful!'));
console.log('response.message',response.message);
this.router.navigate(["/editChannel", id]);
})
now angular will grab the response and map it to the MyResponse interface giving you ability to access the message property. Alternatively you could keep it as any or unknown not sure what the type on response by default is, but if its any just do response['message']
Hope that helps.

Not able to log a fetch's result

I'm currently working with nodejs, I created a server side function that returns and prints data from a database.
app.get('/renderMainDashboard', (req,res)=>{ //DASHBOARD DATA
con.connect(err => {
if (!err){
con.query("SELECT * FROM owners", (err, data, fields) =>{
console.log(data); //IT LOGS THE DATA INTO DE VS TERMINAL
return data;
})
}
});
});
I need this function to be called from the client side, so there is a class that makes the fetch inside the constructor:
export default class{
constructor() {
this.title = "Dashboard";
fetch('http://localhost:5600/renderMainDashboard') //DEFAULT GET ()
.then(response => response.json())
.then(finalResponse => {console.log('Datos recibidos desde el server', finalResponse);});
//DOESN'T LOG 'Datos recibidos...' TO WEB CONSOLE
//.then(console.log('Response from then statement'); //IT DOES THE LOG
}
//----
}
The function actually works, when I try to do the fetch it is still working but I need to log the response. As you may see, there is a then statement with a console.log('Datos recibidos...') but it is not working. Any idea of what I may be doing wrong?
Actual output of the DB:
[
TextRow {
id: 1,
firstName: 'Andres',
lastName: 'Gonzalez',
email: 'androsogt#gmail.com',
personalKey: 'androso+1234-',
phoneNumber: '35006115'
},
TextRow {
id: 2,
firstName: 'Pedro',
lastName: 'Contreras',
email: 'sirpedro#gmail.com',
personalKey: 'holamundo',
phoneNumber: '41508886'
},
TextRow {
id: 3,
firstName: 'Yuhana',
lastName: 'Melgar',
email: 'melgar.keyla#gmail.com',
personalKey: 'COD2002',
phoneNumber: '37578639'
}
]
You don't end your request by sending a response. See this example:
app.get('/', function (req, res) {
res.send('hello world')
})
You should use res.send() method to end your request and send back data.
Why it doesn't log? Because response.json() returns a rejected promise as there's no response (timeout) and hence, second .then doesn't get called.
Your express get /renderMainDashboard handler is not sending anything back to the client.
Try replacing:
return data;
with:
res.status(200).json(data); // provided that data is a valid JSON object

Promise chaining with mongoDB (and mongoose). How to use .save() after .then() and correctly breakout of a promise chain if a response has been sent?

I have the following code for signing up a user. Where I first validate the user input. Secondly I check to see if the user already exists, if yes it should return with response 400. If not go to step 3 and add the new user. Finally in step 4 return the newly created entry. Logically, it works and adds data to database correctly, however it always responds back with 'User already exists' on postman (from step 2) even if it's a new user which has correctly added the user to the db. Which makes me think the third step is being done before a response in step 2 can be sent, which would mean I have not chained the promise correctly. Also the new user is never sent back as response, which I think is because I have not used Promise.then() together with user.save() correctly. I also get the following error (posted after the code), which I understand means I am trying to send a second response after a first has already been sent. I can solve this problem with async and await but want to learn how to do it this way. Thanks, any help is appreciated.
const { User, validateUser } = require('../models/userModel');
const mongoose = require('mongoose');
const express = require('express');
const router = express.Router();
router.post('/', (req, res) => {
return Promise.resolve()
.then(() => {
//Step 1: validae the user input and if there is an error, send 400 res and error message
console.log('My user post body req::', req.body);
const { error } = validateUser(req.body); //this is using Joi.validate() which has a error property if errors are found
if (error) {
return res.status(400).send(error.details[0].message);
}
})
.then(() => {
//step 2: check if user already exists, if yes send res 400
let user = User.findOne({ email: req.body.email });
if (user) {
return res.status(400).send('User already exists');
}
})
.then(() => {
//Step 3: enter new user into the database
user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
return user.save();
})
.then((result) => {
//step 4: return the newly added user
return res.status(200).send(result);
})
.catch((error) => {
console.log('Error Adding new User', error);
});
});
module.exports = router;
I get the following error message from the catch. Even though I am I am returning with every response
Error Adding new User Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:494:11)
at ServerResponse.header (/home/ssaquif/WebDevProjects/movie-reviews-backend/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/home/ssaquif/WebDevProjects/movie-reviews-backend/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/home/ssaquif/WebDevProjects/movie-reviews-backend/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/home/ssaquif/WebDevProjects/movie-reviews-backend/node_modules/express/lib/response.js:158:21)
at /home/ssaquif/WebDevProjects/movie-reviews-backend/routes/users.js:35:27
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
code: 'ERR_HTTP_HEADERS_SENT'
You don't need to use Promise.resolve in your route.
You just need a chain of then blocks, in which you need to return a value to the next one.
I refactored your code like this:
router.post("/", (req, res) => {
//Step 1: validate the user input and if there is an error, send 400 res and error message
console.log("My user post body req::", req.body);
const { error } = validateUser(req.body);
if (error) {
return res.status(400).send(error.details[0].message);
}
//step 2: check if user already exists, if yes send res 400
User.findOne({ email: req.body.email })
.then(user => {
if (user) {
return res.status(400).send("User already exists");
}
return;
})
.then(() => {
//Step 3: enter new user into the database
let user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
return user.save();
})
.then(result => {
//step 4: return the newly added user
return res.status(200).send(result);
})
.catch(error => {
console.log("Error Adding new User", error);
res.status(500).send("Error");
});
});
You will have a result like this when a user successfully registers:
{
"_id": "5dd65df52f7f615d8067150d",
"name": "ssaquif",
"email": "test#test.com",
"password": "123123",
"__v": 0
}
And when an existing email is used, the response will be like this with statusCode 400.
User already exists
You could solve this somehow by chaining promises correctly in a more complicated way, or you use async / await and get rid of all those problems:
router.post('/', async (req, res) => {
try {
//Step 1: validae the user input and if there is an error, send 400 res and error message
console.log('My user post body req::', req.body);
const { error } = validateUser(req.body); //this is using Joi.validate() which has a error property if errors are found
if (error) {
return res.status(400).send(error.details[0].message);
}
//step 2: check if user already exists, if yes send res 400
let user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).send('User already exists');
}
//Step 3: enter new user into the database
user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
await user.save();
//step 4: return the newly added user
return res.status(200).send(user);
} catch(error) {
// Report error internally
return res.status(500).send("Something bad happened");
}
});
The main problem with your code is that returning from a .then callback will continue executing the next .then callback. Therefore you try to set the headers status multiple times (but that's your smallest problem).
If you look at the error message "Cannot set headers after they are sent to the client" it means you are trying to send something over the response object twice which are not possible. Try log something right before each time you send something as a response and see which two are being called.
Instead of returning the res.status(400).send promise, try call it normally and then return a rejected promise or throw an error instead.

What is the appropriate way of handling user (action) related errors?

I'm scratching my head trying to figure out the best way to handle errors from specific user actions. I'm using Express as my web server and even though it works, for the most part, I am getting not-so-useful, generic error messages. For instance, in the code below, I get the Request failed with status code 400 error message on the client side for the first two conditions/exceptions in the try block.
How do I approach this in the following example?
Express Server-side Controller
async function voteInPoll (req, res) {
const { category, pollId } = req.params;
const { name, choiceId, voterId } = req.body;
try {
const poll = await Poll.findById(pollId);
// Check if user has already voted in poll
const hasVoted = poll.votedBy.some(voter => voter.equals(voterId));
if (!voterId) { // Check if user is authenticated
res
.sendStatus(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res
.sendStatus(400)
.json({ message: 'Sorry, you can only vote once' });
} else {
const choice = await poll.choices.id(choiceId);
const votedChoice = { name, votes: choice.votes + 1 };
await choice.set(votedChoice);
await poll.votedBy.push(voterId);
poll.save();
res
.sendStatus(200)
.json({
message: 'Thank you for voting. Find other polls at: ',
poll,
});
}
} catch (error) {
throw new Error(error);
}
}
React/Redux Action
export const voteInPoll = (category, pollId, votedItem, voterId) => async dispatch => {
try {
const response = await axios.post(
`http://localhost:3050/polls/${category}/${pollId}/vote`,
{
...votedItem,
voterId,
}
);
dispatch({ type: store.polls.VOTE_SUCCESS, payload: response.data.poll });
} catch (error) {
console.log(error);
dispatch({ type: store.polls.VOTE_FAILURE, payload: error.message });
}
};
Edit
What I find rather bizarre is I get the expected error response sent, as seen below under the Network tab of Chrome's Developer tools.
You should not be using res.sendStatus(statusCode) because of the following as defined in the docs here:
Sets the response HTTP status code to statusCode and send its string representation as the response body.
The key thing about the above is:
and send its string representation as the response body.
So doing: res.sendStatus(400).json({ message: 'Oops 400!'}) will not give you a JSON response which is what you're expecting, but simply display:
Bad Request
Which is the string representation of the 400 HTTP status code: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors
What you need to do is replace all of your res.sendStatus(..).json(..) with res.status(...).json(...) like so:
if (!voterId) { // Check if user is authenticated
res
.status(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res
.status(400)
.json({ message: 'Sorry, you can only vote once' });
} else {
// ...
}
and so on.

Performing update operations on Meteor.users collection within catching a rejected promise (using fcm-push+Meteor)

I am utilizing an NPM package called fcm-push (https://www.npmjs.com/package/fcm-push) in order to send FCM notifications to various mobile devices based on a generated message. There's no issue when the FCM message successfully sends, however if the FCM message sending fails because the FCM token associated with the message is "NotRegistered," then I would like to remove the FCM token associated with the user.
However, whenever the FCM message fails to send, the token never gets removed from the user's profile, even though it triggers the call back on calling Meteor.users.update. If there is any way for me to modify the database operation so I can successfully perform the update operation on the profile, some guidance would be appreciated.
[INFO] -- 10:59:23 | "Error" | Data: {
"data": "NotRegistered",
"haltDate": "2017-03-31T10:59:23.660Z"
} | User: cHkDSqQBMVc:APA91bFXCwp1-nxi2xxVEZARAMHs48kLm6FN0tbgmjv1lP1-LsBty_6gCFqGqDxGV9JrpCDG9pVFIxUz-77-6QxbIMa2OWmG4xoN2-E_8UoD_xe8MVoDb-DZY_KSZcMh4Bg_5F18ltg0
return fcm.send(fcmMessage).then((data) => {
var processEndDate = new Date();
console.log("Response Data "+data+" ------ "+startDate+" --> "+processEndDate);
loggerA.info("Response", {data: data, startDate: startDate, endDate: processEndDate}, token);
return {
method: 'SendMessage',
status: JobberServer.Status.SUCCESS,
dateEnd: processEndDate,
response: data
};
}).catch((err) => {
loggerA.info("Error", {data: err, haltDate: startDate}, token);
Meteor.users.update({_id: targetId}, {$pull: {"profile.fcmTokens": {id: token}}}, {multi: true}, function (err, docsModified) {
loggerA.info("Deregister Op", {token: token, res: err, noOfDereggedTokens: docsModified}, "NAN");
});
return {
method: 'SendMessage',
status: JobberServer.Status.FAIL,
dateEnd: null,
response: err
}
});
Ended up fixing it myself - simply just had to remove the update operation from the method itself, wrap the promise using Promise.await(...) and then return the return value of that so it can be consumed by the Meteor.call(...) callback. The resulting code in the method looks like this:
return Promise.await(fcm.send(fcmMessage).then((data) => {
var processEndDate = new Date();
console.log("Response Data "+data+" ------ "+startDate+" --> "+processEndDate);
loggerA.info("Response", {data: data, startDate: startDate, endDate: processEndDate}, token);
return {
status: JobberServer.Status.SUCCESS,
response: data
};
}).catch((err) => {
loggerA.info("Error", {data: err, haltDate: startDate}, token);
return {
status: JobberServer.Status.FAIL,
response: err
};
}));
This allows me to get status and response from the response parameter of the method's call back since await waits for the promise to resolve first before returning the value. Afterwards, I can perform the necessary operations without any issue based on the response.

Categories