MongoDB _id returns undefined on frontend (VueJS) - javascript

I created a simple fullstack webapp using NodeJS & Express + MongoDB to build the backend API and Vue.JS for the frontend.
All the Write Read and Delete API worked perfectly (I tested it using Postman). Everything works perfectly as well on the frontend, except when I tried to iterate (v-for) on an array of objects to get the _id, it doesn't work.
The array called posts has the attributes of 'text' and 'createdAt'. The v-for works perfectly and output the 2 attributes as expected. However, I tried to output _id (default id from MongoDB) but it returned "undefined".
This causes a problem because if I can't get _id, it wouldn't be possible for me to delete a specific post using the existing backend delete API.
From my understanding, in the backend side, the _id needs to be converted into ObjectId first before it can be used for db querying. But on the frontend (vue) I am not sure on how can turn _id into ObjectId. Am I getting into the right direction here?
<div class="post" v-for="(post,i) in posts " :key="post._id" :index="i" :item="post" #dblclick="deletePost(post._id)">
{{post.createdAt.getDate()}}/{{post.createdAt.getMonth() + 1}}/{{post.createdAt.getFullYear()}}
<p class="text">{{post.text}}</p>
</div>
...
methods: {
async deletePost(id){
console.log(id) //this returns undefined
await PostService.deletePost(id);
this.posts = await PostService.getPosts();
}
},
...
//del post in PostService.js
static deletePost(id){
return axios.delete(url+id)
}
//backend delete api
router.delete('/:id',async (req,res)=>{
const posts = await loadPostCollection()
console.log(ObjectID(req.params.id))
await posts.deleteOne({_id: ObjectID(req.params.id)});
res.status(200).send()
})
expected output: _id of each 'post', e.g:5cfa8c29f74c65ae485a6d93
actual output: undefined
no error message(s).

You need to add the property reference post, like this:
<div class="post" v-for="(post,i) in posts " :key="post._id" :post="post" #dblclick="deletePost(post._id)">
{{post.createdAt.getDate()}}/{{post.createdAt.getMonth() + 1}}/{{post.createdAt.getFullYear()}}
<p class="text">{{post.text}}</p>
</div>

You don't have to convert _id to Mongo ObjectID on the FrontEND.
Your code looks normal, send all post object to function like that and debug it.
#dblclick="deletePost(post)"
Probably your backend return _id as an object.

Related

Node SQL Server IN query not working with request params

I want to run a query in Node SQL Server which is using IN clause. This is the string used for querying 'a','b','c'. This code works fine, but user is passing data so, I can't use it. May lead to attacks:
const dbResult = await request.query(`
SELECT OrderID, ParentSKURefNum, SKURefNum, OrderCompleteTime
FROM ${tables.ORDERS}
WHERE OrderID IN (${idsWithQuotes})
`);
I want to use request.input('OrderIDs', ids) and then code will be like this:
request.input('OrderIDs', ids);
const dbResult = await request.query(`
SELECT OrderID, ParentSKURefNum, SKURefNum, OrderCompleteTime
FROM ${tables.ORDERS}
WHERE OrderID IN (#OrderIDs)
`);
But the code above always shows: No data found. What am I doing wrong? In second situation I also tried removing first and last quote from the string assuming request automatically adds it.
Thanks for your help!
I'm using SQL Server 2012 which doesn't support STRING_SPLIT function to split CSV into some sort of table which then IN operator operates on.
I found it on stack overflow that we can split the values using XML which I didn't really understand but did the trick.
SELECT OrderID, ParentSKURefNum, SKURefNum, OrderCompleteTime
FROM ${tables.ORDERS}
WHERE OrderID IN (
SELECT Split.a.value('.', 'NVARCHAR(MAX)') DATA
FROM
(
SELECT CAST('<X>'+REPLACE(#OrderIDs, ',', '</X><X>')+'</X>' AS xml) AS STRING
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
)

JSON Property Undefined even thought the variable containing the Data is populated with a JSON object

I am trying to receive a MongoDB document from my database through a get request. I have tested the URL via Postman and is returning the document as expected. This has made me aware that the issue is in the frontend and not the Server.
When I do the same via my frontend, it retrieves the data, and is stored in the variable res and assigned to this.englishteam so it can be accessed outside the function scope of GetSingleEnglishTeam.
public teamenglish!:EnglishTeam;
GetSingleEnglishTeam(_id: string) {
this.englishTeamService.GetSingleEnglishTeam(_id).subscribe((res)=>{
console.log("The team: "+ res);
this.teamenglish = res;
// console.log(this.teamenglish);
})
}
However, when I try to access the ClubName stored in teamenglish I get an undefined error
ERROR TypeError: Cannot read property 'ClubName' of undefined
at ClubHonoursComponent_Template (club-honours.component.html:9)
Club-honours.component.html
<div class="row">
<div class="column">
<div class="PremierLeagues" >
{{teamenglish.ClubName}}
</div>
</div>
Get Request to the Server
GetSingleEnglishTeam(_id: string){
console.log( "ID is "+_id);
return this.http.get<EnglishTeam>('http://localhost:3000/api/getTeamEn/' + `${_id}`);
}
When I console log the variable containing the JSON object I get [Object Object].
What I want to do is be able to send a getOne request to the server and the server to return the single document. Can anyone see where I have gone wrong?
The problem is that the template is initialised before teamenglish variable could be set in callback of subscribe.
You can solve this by 3 methods -
1.) In your template file use Safe navigation operator (?) i.e. -
{{teamenglish?.ClubName}}
2.) Use *ngIf on container div to only render itself if teamenglish is available to use (which will be available to use once your observable callback sets it to teamenglish i.e. -
<div *ngIf="teamenglish" class="PremierLeagues" >
{{teamenglish.ClubName}}
</div>
3.) Use async pipe in your template to automatically handle the subscription and value resolution i.e.
In your ts file, assign the observale to a variable like -
const obs = this.englishTeamService.GetSingleEnglishTeam(_id)
Then in your template file -
{{obs | async | json}}

Display data from database (using mongodb) in hbs/html file Node.Js

I started studying node.js, and now I'm trying to do a "Todo-App".
I'm trying to find the best way to transfer data from my database (using mongodb) into my hbs files, so I could display it.
From the server.js -> server to the hbs -> client (correct to me if I'm wrong please, by assuming that server.js is the server of course and the hbs file is the client)
So, I succeeded to do it by passing an array.
but when I'm trying to display in html desing, it just looking bad.
The code:
app.get('/allTasks',(req,res)=>{ //get (go to) the allTasks (hbs file)
Todo.find().then((todos) => {
console.log(todos);
var arrayOfTodos = [];
todos.forEach(function(element){
console.log("\n\n\n\n\n elemnt details: ",element.text + "\n",element.completed+"\n");
arrayOfTodos.push(element.text,element.completed);
});
res.render("allTasks.hbs", {
pageTitle: "Your tasks: ",
todos: arrayOfTodos
});
});
});
The result is:
You can see a picture
As you can see, its just looking bad... cause it just display an array,
an I want to display each task seperately.
Any tips?
Thanks a lot,
Sagiv
Instead of using push just do:
Todo.find().toArray(function(err, result){
arrayOfTodos = result;
})
Once you have your array, the design got nothing to do with mongodb. You will need to learn how to use your render technology. You need to touch your html template, so you should start by posting that.
The problem solved.
I just had to learn how to handle the data in the hbs side.
so the code is: (in hbs)
{{#each todos}}
{{missionNumber}} <br>
{{text}}<br>
completed = {{completed}}<br><br>
{{/each}}
as you can see, the each is a loop , that pass on the todos parameter (my array)
and i just have to display the data in the way i want it to be displayed.
thanks for your help.

How to query Firebase data after using .push() to add data?

Here is the code for when I'm pushing the data to Firebase:
firebase.database().ref(`booklogs/${uid}/${book_id}`).push(page_id)
booklogs :
{HUMjSHxVKAPfVXzOId9zCBkGOgv1:{
book28917: {
-KYp4FdYYODDZG1FX-Pb: 1
}
}
}
My problem is when I query the data, the child node of the ${book_id} includes the push key, but I only want to get the value which is 1 and not the push key.
The code I use to query is:
var booklogs = db.ref(`booklogs/${uid}/${project}`);
booklogs.once('value')
.then((snapshot) => {
console.log(`pages viewed are ${snapshot.key}: ${snapshot.val()}`);
console.dir(snapshot.val());
}).catch((error) => {
console.log(`Error : ${error}`);
});
The data returned in the console is:
pages viewed are 2634651: [object Object]
{ '-KYp4FdYYODDZG1FX-Pb': 1 }
Any input would be much appreciated. Thanks!
If you only want the '1' and not the push key, try using .set()
firebase.database().ref(`booklogs/${uid}/${book_id}`).set(page_id)
That will get rid of the object and just give you the value that you wanted. Push automatically generates a key for every value you add, so you will always get an object back. From the Firebase docs - "For basic write operations, you can use set() to save data to a specified reference, replacing any existing data at that path."
https://firebase.google.com/docs/database/web/read-and-write

Mongo check if a document already exists

In the MEAN app I'm currently building, the client-side makes a $http POST request to my API with a JSON array of soundcloud track data specific to that user. What I now want to achieve is for those tracks to be saved to my app database under a 'tracks' table. That way I'm then able to load tracks for that user from the database and also have the ability to create unique client URLs (/tracks/:track)
Some example data:
{
artist: "Nicole Moudaber"
artwork: "https://i1.sndcdn.com/artworks-000087731284-gevxfm-large.jpg?e76cf77"
source: "soundcloud"
stream: "https://api.soundcloud.com/tracks/162626499/stream.mp3?client_id=7d7e31b7e9ae5dc73586fcd143574550"
title: "In The MOOD - Episode 14"
}
This data is then passed to the API like so:
app.post('/tracks/add/new', function (req, res) {
var newTrack;
for (var i = 0; i < req.body.length; i++) {
newTrack = new tracksTable({
for_user: req.user._id,
title: req.body[i].title,
artist: req.body[i].artist,
artwork: req.body[i].artwork,
source: req.body[i].source,
stream: req.body[i].stream
});
tracksTable.find({'for_user': req.user._id, stream: req.body[i].stream}, function (err, trackTableData) {
if (err)
console.log('MongoDB Error: ' + err);
// stuck here - read below
});
}
});
The point at which I'm stuck, as marked above is this: I need to check if that track already exists in the database for that user, if it doesn't then save it. Then, once the loop has finished and all tracks have either been saved or ignored, a 200 response needs to be sent back to my client.
I've tried several methods so far and nothing seems to work, I've really hit a wall and so help/advice on this would be greatly appreciated.
Create a compound index and make it unique.
Using the index mentioned above will ensure that there are no documents which have the same for_user and stream.
trackSchema.ensureIndex( {for_user:1, stream:1}, {unique, true} )
Now use the mongoDB batch operation to insert multiple documents.
//docs is the array of tracks you are going to insert.
trackTable.collection.insert(docs, options, function(err,savedDocs){
//savedDocs is the array of docs saved.
//By checking savedDocs you can see how many tracks were actually inserted
})
Make sure to validate your objects as by using .collection we are bypassing mongoose.
Make a unique _id based on user and track. In mongo you can pass in the _id that you want to use.
Example {_id : "NicoleMoudaber InTheMOODEpisode14",
artist: "Nicole Moudaber"
artwork: "https://i1.sndcdn.com/artworks-000087731284-gevxfm-large.jpg?e76cf77"
source: "soundcloud"
stream: "https://api.soundcloud.com/tracks/162626499/stream.mp3? client_id=7d7e31b7e9ae5dc73586fcd143574550"
title: "In The MOOD - Episode 14"}
_id must be unique and won't let you insert another document with the same _id. You could also use this to find the record later db.collection.find({_id : NicoleMoudaber InTheMOODEpisode14})
or you could find all tracks for db.collection.find({_id : /^NicoleMoudaber/}) and it will still use the index.
There is another method to this that I can explain if you dont' like this one.
Both options will work in a sharded environment as well as a single replica set. "Unique" indexes do not work in a sharded environment.
Soundcloud API provides a track id, just use it.
then before inserting datas you make a
tracks.find({id_soundcloud : 25645456}).exec(function(err,track){
if(track.length){ console.log("do nothing")}else {//insert}
});

Categories

Resources