Adding mongoose object to array in nodejs - javascript

I have an array of images as this.
var newImageParams = [
{
image_string: "hello",
_type: "NORMAL"
},
{
image_string: "hello",
_type: "NORMAL"
}
]
I am using mongo db and In nodejs, I am using mongoose, Everything is fine, I Can add data to mongoose table, but after adding each image, I am trying to save the resulting object into an array, Which in console.log is happening and right as well, but when I return that array, there is nothing in it and it empty
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
// allImages[i]
var newImage = new Image(Object.assign(newImageParams[i], {bike: bike._id}));
newImage.save(function(err, image){
console.log(image);
allImages.push(image);
console.log(allImages);
});
}
}
This is what I am doing, everything is right and good in console but when I finally return,
res.json({bike: bike, images: allImages });
Images array is totally empty as it was when declared? why this is happening, please help

I bet you send response before your callbacks are done.
The easy and fast fix:
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
const image = Object.assign(newImageParams[i], {bike: bike._id});
var newImage = new Image(image);
allImages.push(image);
newImage.save(function(err, image){
console.log(image);
console.log(allImages);
});
}
}
I will add correct approach with async/await, the older versions would use Promise.all or async.parallel:
async function doJob() {
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
const image = Object.assign(newImageParams[i], {bike: bike._id});
const newImage = new Image(image);
await newImage.save();
allImages.push(image);
}
}
}

allImages.push(image); -> that line of code is async. It's will go to event queue and your console log will call immediately.
So console.log(allImages); will return empty.
You can read more in here. It will help you in future.

Related

Trouble with Addition Assignment in Array from Firebase

I have a scenario where i need to query multiple collections at once and retrieve the values based on the collection name. I use Promise.all to do so and it works accordingly like so
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("collection1").where("user_id", "==", uid).get(),
admin.firestore().collection("collection2").where("user_id", "==", uid).get(),
admin.firestore().collection("collection3").where("user_id", "==", uid).get(),
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "collection1") {
qs.docs.map((doc) => {
valuesArr1.push(doc.data().arr);
});
} else if (qs.query._queryOptions.collectionId == "Collection2") {
qs.docs.map((doc) => {
valuesArr2.push(doc.data());
});
} else if (qs.query._queryOptions.collectionId == "collection3") {
qs.docs.map((doc) => {
valuesArr3.push(doc.data());
});
}
} else {
return
}
});
for (var i=0; i < valuesArr1.length; i++) {
if (valuesArr1[i].desiredData) {
console.log('datas from for loop on datas array', valuesArr1[i].desiredData)
globalVariable += `<img src="${valuesArr1[i].desiredData}">`;
}
}
Once I do this I map the query snapshot I get and am able to retrieve the values up to this point like so
From the first collection I retrieve an array from a firestore document and then the following collections i just retrieve all documents from the collections. This all 'works' in that when I console.log into the functions console the data shows up exactly as expected. It's only when I want to iterate over the data and assign the results to a global variable to use elsewhere that strange behavior occurs.
The console.log shows the desired data in the functions console with no issues, but the output when I interpolate that data into the html and send it off in nodemailer I get the following result
undefined is always the first in the response when i use the += addition assignment operator, but if i just use the = assignment operator there's no undefined but I obviously don't get all the data I'm expecting.
There are no undefined values or documents in the collections that I'm retrieving, I've checked thoroughly and even deleted documents to make sure of it. After days of researching I've come to the conclusion it has to do with the asynchronous nature of the promise I'm working with and the data not being immediately ready when I iterate it.
Can someone help me understand what I'm doing wrong and how to fix it in node?
I figured out a solution to my problem and would like to share it in hopes it saves a future viewer some time.
Before, I was storing the results of the array from Firebase inside a global variable. To save some head scratching I'll post the code again below.
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
globalVariableArray.map(gv => {
globalVariable += `<p>gv.desiredData</p>` // <--- Right here is where the problem area was
})
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'Almost but not quite',
html: `${globalVariable}`
};
The above code give the expected output, but the output would always have undefined first before the data showed. This happened no matter how the array from Firebase was iterated over.
After strengthening my Google-Fu, I worked out the following solution
var globalVariableArray = []
var globalVariable
var dbPromises = [];
dbPromises.push(
admin.firestore().collection("DataCollection").where("user_id", "==", uid).get()
);
const promiseConst = await Promise.all(dbPromises);
promiseConst.forEach((qs) => {
if (qs.size > 0) {
if (qs.query._queryOptions.collectionId == "DataCollection") {
Promise.all(
qs.docs.map(doc => {
globalVariableArray = doc.data().arrayWithDesiredData;
})
);
}
else {
return
}
});
var mailOptions = {
from: foo#blurdybloop.com,
to: 'bar#blurdybloop.com
subject: 'It works!!',
html: `${globalVariableArray.map(dataIWantedAllAlong => <p>dataIWantedAllAlong.desiredData</p> )}` <--- Here I simply loop through the array inside the interpolation blocks and voila! no more undefined showing up in the results
};
I perform the loop inside the brackets where I interpolate the dynamic data and am no longer getting that pesky undefined showing up in my emails.
Safe travels and happy coding to you all!

how to set default value in case of error

I'm learning React and I'm trying to render 10 pieces of data from a specific API.
This is a function I wrote for iterating through the fetched data and taking the title and the image:
for (let i = 0; i < 10; i++) {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs.src,
});
}
I don't know why but one of the images gives me that error:
index.js:1 TypeError: Cannot read property 'attribs' of undefined
Which stops the whole rendering.
I wanted to try to put a temporary placeholder image so the rest of the images can be loaded without any errors.
I thought about adding conditions into the image line inside the loop but it didn't work for me. What's the right way to do that?
If I wasn't clear enough please comment what wasn't clear and I'll try to describe it better.
If someMethod is synchronous (based on your code sample it should be) then you can wrap your code in try...catch block like so:
for (let i = 0; i < 10; i++) {
try {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs.src,
});
} catch (err) {
data.push({
title: 'placeholder',
image: 'placeholder',
});
continue;
}
}
Here's more cleaner version of the above:
for (let i = 0; i < 10; i++) {
let title = '';
let image = '';
try {
title = someMethod(allData)[i].children[0].data;
image = someMethod(allData)[i].attribs.src;
} catch (err) {
image = 'failed to load or something';
}
data.push({title, image});
}
for (let i = 0; i < 10; i++) {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs?.src ?? defaultSrc,
});
}
docs: Optional_chaining and Nullish_coalescing_operator
You can solve the null pointer exceptions in 2 ways.
Manually check for truthy values using ? or &&
Create a simple utility function
Point 1 will solve your problem, but however if you start using it everywhere throughout your code it may not look tidy. I'd recommend to go by creating a utility function as below.
export const getSafe = (fn, defaultValue=null) => {
try {
return fn()
} catch (e) {
return defaultValue
}
}
You can then use it anywhere like..
var result = getSafe(() => object1.data.key)
You can use it for accessing any type of values to any depth. You can also pass a default value as 2nd argument if required. by default it is set as null
var resultArray = getSafe(() => array1.data.values, [])
Just remember, the 1st value should be passed as a function.
So in your case...
for (let i = 0; i < 10; i++) {
data.push({
title: getSafe(() => someMethod(allData)[i].children[0].data, "Default Title"),
image: getSafe(() => someMethod(allData)[i].attribs.src, "placehoder_img"),
});
}
Implement the map function.
The for loop will give an error at your component
In react if you need to render elements don't implement the for loop
datas.map(......)

JSON Property undefined in JavaScript with p5.js

I'm just beginning learning to code and this is my first post here so please forgive me if I violate any rules here....
So here is my question, it seems quite simple but I can just not figure out what is the problem. I have a JSON file like this:
{"imgj":
[
{
"id":"1",
"user_id":"1",
"tag":"siteplan",
"imageurl":"images/cb.jpg"
},
{
"id":"2",
"user_id":"2",
"tag":"floorplan",
"imageurl":"images/cbb.jpg"
},
{
"id":"3",
"user_id":"1",
"tag":"section",
"imageurl":"images/postit.png"
},
{
"id":"4",
"user_id":"2",
"tag":"siteplan",
"imageurl":"images/avatar_default.jpg"
}
]
}
and in my .js file, I'm trying to get data of img from the JSON file, but always failed.
p.preload=function(){
var url ='imglist.json';
imglist = p.loadJSON(url);
for(var i=0; i<4; i++) {
imgurl = imglist.imgj[i].imageurl;
img[i]=p.loadImage("imgurl");
}
};
The result of the console is shown as below:
console.log(typeof imglist == 'object'); //return true
console.log(imglist); //return {} imgj:(4)[{...},{...},{...},{...}]
console.log(imglist.imgj[1]); //return undefined
it seems that my JSON file is successfully read as an object, but the property of it cannot be read. It's really weird.
This should work however it's only fetch workaround. I don't know how to access this not own property made by loadJSON.
p.preload= function(){
var url ='imglist.json';
fetch(url)
.then(data=>data.json())
.then(imglist=>{
//imglist = p.loadJSON(url);
console.log(imglist.imgj[0]); // Object { id: "1", user_id: "1", tag: "siteplan", imageurl: "images/cb.jpg" }
for(let i = 0;i< imglist.imgj.length;i++) {
imgurl = imglist.imgj[i].imageurl;
img[i]=p.loadImage(imgurl);
}});
};
After a bunch of fiddling I believe loadJSON is actually asynchronous behind the scenes, so your for-loop continues to execute before the file is done loading.
You could store the imglist in a global/classwide variable and do the for-loop inside the setup or draw function instead.
var imglist;
var imgs;
p.preload = function() {
var url = 'imglist';
imglist = loadJSON(url);
}
p.setup = function() {
imgs = imglist.map(function(imglistitem) {
return loadImage(imglistitem.imageurl);
});
}
I am not quite sure how p5.js is wired behind the scenes, but the example from the reference uses the draw() function to pick apart the json:
https://p5js.org/reference/#/p5/loadJSON

code is working while debug, but without not - Javascript / Jquery

I have an issue that I don't understand and I have no idea how to fix it, where to look for the cause. When I'm debugin (chrome) every thing is working, but during normal use it dosen't. For me is some kind of Science-Fiction, it would be better for me if it's more Science than Fiction :)
for (var i = 0; i < filteredAddedFiles.length; i++) {
if ((/\.(png|jpeg|jpg|gif)$/i).test(filteredAddedFiles[i].name)) {
(function (file) {
var reader = new FileReader();
var blob = b64toBlob(file.base64.replace('data:image/jpeg;base64,', ''), file.type);
reader.addEventListener("load", function () {
console.log(this);
var image = new Image();
image.src = window.URL.createObjectURL(blob);
image.onload = function () {
preview.innerHTML += drawHtml(image, file)
};
//I tried:
//(function (b) {
// var image = new Image();
// image.addEventListener("load", function () {
// preview.innerHTML += drawHtml(this, file);
// //window.URL.revokeObjectURL(image.src);
// });
// image.src = window.URL.createObjectURL(b);
//})(blob);
});
reader.readAsDataURL(blob);
})(filteredAddedFiles[i]);
} else {
errors += filteredAddedFiles[i].name + " Unsupported Image extension\n";
}
}
here I attached a short movie that shows how its working
link to movie
not working - I mean - it looks like the all thing inside for dosen't executed
EDIT: 1
#Teemu - I turn on logs in chrome console and all console.log's appear
#Katie.Sun - now the above for - console.log(filteredAddedFiles.length); is 0 - but when I'm debuging code the same console.log(filteredAddedFiles.length); have values !
EDIT: 2
#Matti Price
filteredAddedFiles - is the result of custom logic of page, filtering,
validation etc.
Everything starts here:
addedFiles = added(files); // files - FileList from input this is a read only array of obj
function added(from) {
var out = [];
for (var i = 0; i < from.length; i++) {
(function (obj) {
var readerBase64 = new FileReader();
readerBase64.addEventListener("load", function () {
var fileBase64 = readerBase64.result;
var row = { name: obj.name, size: obj.size, type: obj.type, base64: fileBase64 }
out.push(row);
});
readerBase64.readAsDataURL(obj);
})(from[i]);
}
return out;
}
then addedFiles - do something farther and transform into filteredAddedFiles later. what I found in added function? during debug there is an length value witch is correct, but when I opened the __proto__: Array(0) I found length property = 0.
Should this length value be equal to the value from above length?
The second thing:
I have to admit that I don't have enough knowledge aboute addEventListener. Are there any queues here or some thread etc?
EDIT: 3
After last #Teemu comment I made some changes (I had to read a lot aboute promisses etc:)), but output is the same console.log("out resolve", out); shows a array of object, but console.log("out.length then", out.length); shows 0 and the small blue i-icon show msg - Value below evaluated just now
var out = [];
for (var i = 0; i < files.length; i++) {
fillArray(files[i], out);
}
console.log("out resolve", out);
console.log("out.length then", out.length);
function fillArray(obj, out) {
return new Promise(function (resolve, reject) {
var readerBase64 = new FileReader();
readerBase64.addEventListener("load", function () {
var fileBase64 = readerBase64.result;
var row = { name: obj.name, size: obj.size, type: obj.type, out.push(row);
resolve(out);
});
readerBase64.readAsDataURL(obj);
});
}
After I posted the edit above I relized that I just create promise, I forgot to call `promise
, but this is not important, because 90% of my code has been changed because of this topic and the answer of the Golden Person #Kaiido
URL.createObjectURL() - is synchronous. You don't need your Promise wrapping event handlers hell, all can be done in a single loop.
In my case, this is a better solution than a filereader, I have to upload only images, and with some restrictions thanks to which I don't have to worry about freeze the Internet browser, because of the synchronous nature of createObjectURL()
Thank you for your help and commitment

Array.push is not working with objects in Nodejs

How can I add an object as an entry to an array, like I'm trying to do here...
displayAccounts(function(data){
var index;
var accs = new Array();
for (index = 0; index < data.length; ++index) {
rclient.get(data.account, function (info) {
accs.push({
account: data.account,
info: info
});
});
}
console.log(accs);
});
Output:
accs = []
Desired solution:
accs = [{account: 'jak', info: 0},{account: 'jil', info: 1}]
The problem is almost certainly that rclient.get is asynchronous, so examining the array after your loop is examining it too soon (the gets have been started, but they haven't finished yet); it will get filled in by callbacks that occur asynchronously. Wait until the last callback has occurred, e.g.:
displayAccounts(function(data){
var index;
var accs = []; // [] is a better way to write new Array()
for (index = 0; index < data.length; ++index)
rclient.get(data.account, function (info) {
accs.push({
account: data.account,
info: info
});
if (accs.length === data.length) {
// We're done, *now* look at / use the array
console.log(accs);
}
});
}
});
Note that depending on how rclient.get works, the callbacks may or may not occur in the same order as the requests.
Side note: That rclient.get(data.account, ... looks suspicious, you're repeatedly requesting the same information. Perhaps rclient.get(data[index].account, ...?

Categories

Resources