So I'm making a simple forum app and I have 2 collections:
User
_id:60ccb13a21d65f0c7c4c0690
username: testuser
name: test
And Createpost
_id:60d80b1305dcc535c4bf111a
postTitle: "test post"
postText: "aaaaa"
postUsername: "testuser"
If I wanted to get data of testuser from User displayed on his forum post, what would be the best way to do that? This is what I've tried so far:
router.get('/forum', async (req,res)=>res.render('forum', {newPost: await Createpost.find().sort({ date: 'desc'}),
postUser: Createpost.aggregate([{$lookup: { from: "User", localField: "postUsername", foreignField: "username", as: "user"}}])}));
Then I want to get the name field from the newly joined documents in EJS like here:
<% newPost.forEach(newPost => { %>
Posted by: <%= newPost.postUsername %> - Name: <%= postUser.name %>
<%= newPost.postText %>
<% }%>
But when I do this, it doesn't say anything, or if I only put postUser, it says "[object Object]". Any help would be much appreciated.
You are missing await in your postUser query. So the return of this query can be a promise. I edited it a bit and just made 1 query to get all the data
you needed.
router.get('/forum', async (req, res) => {
const newPosts = await Createpost.aggregate([
{
$lookup: {
from: 'users',
localField: 'postUsername',
foreignField: 'username',
as: 'user'
}
},
{
$unwind: '$user'
}
]);
res.render('forum', {
newPosts
});
});
newPosts will have a value like this:
[{
"_id" : "60d80b1305dcc535c4bf111a",
"postTitle" : "test post",
"postText" : "aaaaa",
"postUsername" : "testuser",
"user" : {
"_id" : ObjectId("60ccb13a21d65f0c7c4c0690"),
"username" : "testuser",
"name" : "test"
}
},...]
Ejs file should be like here:
<% newPosts.forEach(newPost => { %>
Posted by: <%= newPost.postUsername %> - Name: <%= newPost.user.name %>
<%= newPost.postText %>
<% }%>
Related
I have got a nested array brought within a query from database, query brings the following:
{
_id: new ObjectId("623rf3f22275f399f88bb"),
first_name: 'Are',
last_name: 'You',
email: 'helping#stackoverflow.me',
password: '$2a$10$BSmezAjYkqU.234t65sT1pPOhg5jxosYrFzwjqXM3On3v.b7p46K1WS',
username: 'lcd1',
Messages: [
{
msgcontent: 'The user whatever said: whatsup.'
},
{
msgcontent: 'The user whatever said: whatsup.'
},
{
msgcontent: 'The user whatever said: whatsup.'
},
{
msgcontent: 'The user whatever said: whatsup.'
},
{
msgcontent: 'The user whatever said: whatsup.'
}
]
}
And I have a button for deleting in each of the brought iterations of a for loop on the page, how do I delete a specific message from the array using the button displayed from the iteration on the page?
I've got this displaying messages:
<% if (messages.length > 0) { %>
<% for (let nr = 1; nr <= messages.length; nr++) { %>
<div class="cardmsgpage">
<div class="displaymsg">Message <%= [nr] %> : <%= messages[nr-1].msgcontent %> </div>
<div class="sideways">
<form action="/deletemsg:id" method="POST">
<button class="msgpagebtn" type="submit"><img class="icon" src="/images/trash-can-solid.svg"></button>
</form>
</div>
</div>
<% } %>
and on the POST method I have:
app.post('/deletemsg', function (req, res) {
for (let nr = 0; nr <= messages.length; nr++) {
message = messages[nr];
}
users.findOne({}).then(result =>{
console.log(result);
users.deleteOne( { "Messages" : [messages[nr]] } );
console.log(result);
})
});
Can anyone tell me how I can delete only the message matching the button of the iteration that displayed it?
If I understood your concern correctly,
I think you are trying to say that you are displaying all the messages(as mentioned above in the problem) as Message i : <message content> and a delete button next to it, upon clicking which sends a POST request to the server to delete the message you've clicked on.
According to me, you can do one thing.....
When post request is made, it should be known to the handeler that which database record is to be modified(object id) and then inside of it which message is to be deleted.
To identify the message which should be deleted you can provide unique id to each of the message object inside your Messages property in database record or you can make a separate message schema and model and use it to store message in Messages array, then each message in Messages array will have unique id.
And then, we can use combination of the database record id(objectId) and the message id to delete the specific message from a specific database record.
Each message having unique id looks like...
{
_id: new ObjectId("623rf3f22275f399f88bb"),
first_name: 'Are',
last_name: 'You',
email: 'helping#stackoverflow.me',
password: '$2a$10$BSmezAjYkqU.234t65sT1pPOhg5jxosYrFzwjqXM3On3v.b7p46K1WS',
username: 'lcd1',
Messages: [
{
_id: <message-id>,
msgcontent: 'The user whatever said: whatsup.'
},
{
_id: <message-id>,
msgcontent: 'The user whatever said: whatsup.'
}
...........
]
}
Pass record's objectId and Messages array or whole object to the ejs file. Your action field of the form looks like /deletemsg/:record_id/:msg_id.
Inside your ejs file
// record_id is objectId of database record
<% if (messages.length > 0) { %>
<% for (let nr = 1; nr <= messages.length; nr++) { %>
<div class="cardmsgpage">
<div class="displaymsg">Message <%= [nr] %> : <%= messages[nr-1].msgcontent %> </div>
<div class="sideways">
<form action="/deletemsg/record_id/messages[nr-1]._id" method="POST">
<button class="msgpagebtn" type="submit"><img class="icon" src="/images/trash-can-solid.svg"></button>
</form>
</div>
</div>
<% } %>
Handle the POST request
app.post('/deletemsg/:record_id/:msg_id', function (req, res) {
users.findOne({_id: req.params.record_id }).then(result =>{
if(result){
console.log(result);
let messages = result.Messages;
// you have msg id, you have to delete that message
let idxToDelete = messages.findIndex(msg=>{
return msg._id === req.params.msg_id;
});
if(idxToDelete != -1)
messages.splice(idxToDelete, 1);
result.Messages = messages;
result.save().then(()=>{
console.log("Message deleted!!!");
}).catch(err=>{
console.log(err);
});
}
})
});
That's it......
Note:- you will have message and record schema like this below.....
const msg_schema = mongoose.Schema({
msgcontent: {type: String}
});
const record_schema = mongoose.Schema({
// everything same ......
// Messages array will have msg_schema types
Messages: [msg_schema]
});
I am having an issue here. I currently am making a school directory using node.js and MongoDB. I am in an app.post request and for some reason I can't get the name of the class being linked to the student to log to the console, but createdClass.name will print...
Here is the code...
app.post("/students/:id", function(req, res){
Student.findById(req.params.id, function(err, foundStudent){
if(err){
console.log(err);
} else {
Class.create(req.body.class, function(err, createdClass){
if(err){
console.log(err);
} else {
createdClass.student.id = foundStudent._id;
createdClass.student.name = foundStudent.name;
console.log(createdClass);
createdClass.save();
foundStudent.classes.push(createdClass);
console.log(foundStudent.classes[0].name);
foundStudent.save();
}
});
}
});
res.redirect("/students/" + req.params.id);
});
Also, here are my models...
STUDENT:
var mongoose = require("mongoose");
var studentSchema = new mongoose.Schema (
{
name: String,
classes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Class"
}
],
grades: Array
}
);
module.exports = mongoose.model("Student", studentSchema);
CLASS:
var mongoose = require("mongoose");
var classSchema = new mongoose.Schema (
{
name: String,
student:
{
id:
{
type: mongoose.Schema.Types.ObjectId,
ref: "Student"
},
name: String
}
}
);
module.exports = mongoose.model("Class", classSchema);
Thank you in advance and please do let me know if there is anything I can add to make this easier to read.
Here is the page making the post request...
<div>
<h1>Student Profile</h1>
<h2>Name: <%=student.name%></h2>
<div>
<h3>Classes:
<form action="/students/<%= student._id %>" method="POST">
<%if(student.classes.length === 0){%>
<p>No classes linked to profile, please add class..</p>
<input type="text" name="class[name]" placeholder="Class name">
<% } else { %>
<% student.classes.forEach(function(course){ %>
<li><%= course.name %></li>
<% }); %>
<% } %>
</form>
</h3>
</div>
</div>
Class is a reserved word and can't be used for a variable
Is it possible to perform Angular operations such as ng-repeat within the EJS templating engine for the purpose of TWO WAY DATA BINDING?
If so, can you provide an example of how to pass an array from express to EJS and perform ng-repeat.
My User Schema:
var mileSchema = mongoose.Schema({ miles: String });
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
},
userInfo : {
fullname : String,
region : String,
},
milesLog : [mileSchema]
});
module.exports = mongoose.model('User', userSchema);
My Express Route:
var User = require('../app/models/user');
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profile.ejs', {
user : req.user
});
});
My EJS template: ( this is only a section of the whole template )
<p ng-repeat="m in milesLog">{{m}}</p>
<% if (user.local.email) { %>
<p>
<strong>id</strong>: <%= user._id %><br>
<strong>email</strong>: <%= user.local.email%><br>
<strong>Region</strong>: <%= user.userInfo.region %><br>
<strong>username</strong>: <%= user.userInfo.fullname %><br>
</p>
<% } %>
the expense.id prints as undefined in the log
My Underscore template
<% _.each(expenses, function(expense) { %>
<tr>
<td><%= expense.get('name') %></td>
<td><%= expense.get('amount') %></td>
<td><%= expense.get('category') %></td>
<td><% console.log(expense.id) %></td>
</tr>
<% }); %>
My Backbone View
var ExpenseList = Backbone.View.extend({
el: '.page',
render : function(){
var expenses = new Expenses();
var that = this;
expenses.fetch({
success : function(expenses){
var template = _.template($('#expense-list-template').html(),
{expenses: expenses.models});
that.$el.html(template);
}
});
}
});
Server Response
[
{
"_id": "53c25827555953000091ab71",
"name": "Shell",
"amount": "$1000",
"category": "Internet"
},
{
"_id": "53c2bfee4bf93700006fb714",
"name": "FC",
"amount": "$432",
"category": "Game"
}
]
Your server is sending back _id attributes, not id attributes.
The easiest option would be to set idAttribute on your model:
var Expense = Backbone.Model.extend({
idAttribute: '_id',
//...
});
var Expenses = Backbone.Collection.extend({
model: Expense,
//...
});
That will give you expense.get('_id') and expense.id as the same thing and the models will send _id back to the server.
You could also add a parse method to your model to rename the attribute but that would cause problems when you tried to send data back to the server.
I have a simple model as follows:
module.exports = {
attributes: {
firstName: 'STRING',
lastName: 'STRING',
contact: 'STRING',
email: 'STRING'
}
};
I already have an index action that displays all the humans. This is the corresponding view:
<h1>List of all humans</h1>
<ul>
<% _.each(humans, function(model) { %>
<li><%= model.firstName %> /// <%= model.lastName %> /// <%= model.contact %> /// <%= model.email %> <button id="<%=model.firstName %>"type="button">Edit</button> </li>
<% }) %>
</ul>
What I want to accomplish is that every time someone clicks on the EDIT button, to display a view containing all the information of that specific model (localhost:1337/human/edit/:id). How can I write my controller? How can I tell my controller that I want THAT specific model to be displayed and route it properly?
Thank you!
You should point browser to localhost:1337/human/edit/:id url, where :id is your particular model's id. For example
<ul>
<% _.each(humans, function(model) { %>
<li><%= model.firstName %> <button id="<%=model.firstName %>" type="button">Edit</button>
</li>
<% }) %>
</ul>
This will automatically execute Human.edit controller with id param set to particular model's id. You don't have to write any custom routes, this is default behaviour.
Example of Human/edit controller action:
edit: function(req, res) {
Human.findById( req.param('id') )
.done(function(err, human) {
if (err) {
return res.send(err, 500);
} else {
if (human.length) {
res.json(human[0]);
} else {
res.send('Human not found', 500);
}
}
});
}
Here I return model encoded as json in response, for simplicity, but you can use your view.
In addition, firstName property is not the best value to use as buttons id attribute, because it should be unique.