Ejs form, send array of data that can be converted into postgresql update queries - node.js

My app includes a scoresheet grid where each cell represents a student's score in one topic. The teacher can enter scores in each cell before clicking a submit button that sends them all at once.
Here is the ejs form that I have right now:
scoresheet.ejs
<tbody>
<% students.forEach((student, i) => { %>
<tr>
<td class="student-cell right">
<%= student.last_name %>, <%= student.first_name[0] %>
</td>
<% topics.forEach(topic=> { %>
<td class="score-cell center">
<input type="text" class="score-input" name="scores_<%= student.id %>_<%= topic.id %>">
</td>
<% }); %>
</tr>
<% }) %>
</tbody>
This form produces a req.body that looks something like this:
scores_1_2: '75',
scores_1_3: '92',
scores_1_4: '100',
scores_1_5: '100',
scores_1_6: '',
scores_2_1: '65',
scores_2_2: '60',
scores_2_3: '50',
scores_2_4: '35',
I'm trying to take this data and convert it into Postgresql query (or mutiple queries).
For example, the line scores_2_4: '35' would become
UPDATE scores SET points = 35 WHERE student_id = 2 AND topic_id = 4
The scores table is a many-to-many join table to connect students and topics.
I suspect that I still have a bit of work to do with my form. I'm probably not sending this data in an ideal way. This is my best solution so far to include a student_id and topic_id along with the teacher's score input.
If this approach is acceptable, then I also need a hint about how to convert all of this data into an update statement.
I'm using current versions of postgresql, nodejs, express, ejs and the node-postgres package.
Thank you in advance for any insight.

This is my best solution so far to include a student_id and topic_id along with the teacher's score input.
Yes, it's fine. You just have to parse the scores_${student_id}_${topic_id} format on the server back into the data structure you expect.
A more customary encoding is to use bracket notation instead of underscores though. Many parsers for application/x-www-form-urlencoded POST bodies can automatically transform this into a nested object, see e.g. Can not post the nested object json to node express body parser and How to get nested form data in express.js?.
<input type="text" class="score-input" name="scores[<%= student.id %>][<%= topic.id %>]">
I also need a hint about how to convert all of this data into an update statement.
Use multiple UPDATE statements for simplicity:
const { scores } = req.body;
for (const studentId in scores) {
const studentScores = scores[studentId];
for (const topicId in studentScores) {
const points = studentScores[topicId];
// TODO: check for permission (current user is a teacher who teaches the topic to the student)
await pgClient.query(
'UPDATE scores SET points = $3 WHERE student_id = $1 AND topic_id = $2',
[studentId, topicId, points]
);
}
}
You might want to throw in a parseInt or two with proper input validation for the studentId, topicId and points if you need them to be integers instead of strings; otherwise postgres will throw an exception.

Related

How can I render specific piece of data from my mongodb into my html?

It's my first project. I'm creating a website where administrator have the privilege to change home page texts. So I'm keeping all texts in a collection called "Texts" , each document has a text:value . When home page is rendered I use Texts.find() to return an array containing objects each has a "text" value. I link it to my home page using index.
Like this..
<h2>Texts[0].text</h2>, so if i have 100 texts I go all the way to <h2>Texts[100].text</h2>. and they are different texts and I need to put them in a specific order so I can't just throw them into my html.
I know that's so stupid , so I'm looking for some idea instead of this.
--------------Modification------------------>>>>
I tried using find method for arrays but it also is so tiring , so something simpler would be great , here's a portion of the code
<div class="card bg-dark text-white">
<img src="<%=imgsArr.find(x => x.name === 'main2').src%>" class="card-img" alt="...">
<div class="card-img-overlay ">
<h2 class="card-title"><%=textArr.find(x => x.id === 14).text%></h2>
<div class="triangle-up"></div>
<hr class="ml-0 ">
<div class="triangle-down"></div>
<p class="card-text "><%=textArr.find(x => x.id === 15).text%></p>
<div class="card-topic">
<h2 class="card-title" style=";"><%=textArr.find(x => x.id === 16).text%></h2>
<p class="card-text"><%=textArr.find(x => x.id === 17).text%></p>
</div>
<div class="card-topic">
<h2 class="card-title" style=""><%=textArr.find(x => x.id === 18).text%></h2>
<p class="card-text"><%=textArr.find(x => x.id === 19).text%></p>
</div>
<div class="btnOut ">
<button class="btn btn-lg shadow-lg ">MENU</button>
</div>
</div>
</div>
Good news is that for loops are always a good option. If you are using ejs or a similar rendering engine something like this should work, although you will have to tweak it a bit. Take a look are the EJS rendering engine documentation for an exact format, but it would look something like below and no matter how long the list gets it will render as much as it receives(if it is a lot you might want to consider using pagination)
<% for text in Texts.text %>
<% if text==="some value"%>
<h2>text</h2>
<% else %>
<p> text </p>
If your front-end is perhaps react.js or vue.js or similar the same prnciple would work but the format will be different.
No offense friend but you need to get some tutorials. But let me help you as best i can. Everything in a web application is somewhat defined. Meaning it falls into one predetermined category or another. When you get json for example you get something base on how that particular data is defined in the database. Hence you can get json data from your backend that looks
{ 'title':'sneaker', 'price':'200', 'quantity':'50' }
Now assuming it is a list of json objects what you can do is loop through this and assign them to tags based on their object key(because it gets converted to a javscript object). so again you code would look something like
<% for text in Texts.text %>
<h2>text.title</h2>
<div>
<p>text.price</p>
<p>text.quantity</p>
</div>
This would be how you would render data from your database(the format might not exactly be spot on). Dealing with forms is a whole other ball game. So get a book or some tutorial videos that discuss it. You will better understand how to handle it.
For your code you are being very rigid, at first glance there is already a pattern, work with that pattern, you code just needs a simple for loop from what i gather.
for(let i=0; let i>100; i++){
if (i%2===0){
console.log(<h2> i </h2>)
}else{
console.log(<p> i </p>
}
}
The reason i have resulted to pure javascript is so you can see what the inner workings of your render should look like. You can get the total number of ids in your database(that is what the integer 100 represents in this case), loop through one by one and produce what you want. It is still javascript, do not let the html throw you off

returning or looking up object from html input in node express

I have an html/handlebars form set up with a Node/Express backend. the form offers options populated from a database. I am able to get the form to return a single user selected value and save it to my mongodb, but I really need the whole object.
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxyTitle" value="{{title}}"/>
<span>{{title}}</span>
</label>
</p>
{{/each}}
and this is the express:
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
const newProxy = {
proxyTitle: req.body.proxyTitle
// I need the other object values to go here, or to be able to retrieve them later
};
// Add to proxy array on the Project object in the collection
project.proxies.push(newProxy);
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});
Is it more sensible to try to load in the entire object as a value in the input field, or to return the id of the object, and look it up in the db? I need to display some of the returned object information on the same page, and also to use it later. Which is more efficient, and what is the best way to achieve it?
If I'm getting it right, the problem is that you're trying to put multiple inputs with the same name on one form in <input type="radio" name="proxyTitle" value="{{title}}"/>, which gives you something like
<input type="radio" name="proxyTitle" value="Title 1"/>
<input type="radio" name="proxyTitle" value="Title 2"/>
<input type="radio" name="proxyTitle" value="Title 3"/>
As explained here, the browsers will chew it, but the server-side handling may require some adjustments.
In your case, the easiest fix would be to add index to the names of parameters. So, your form would be looking like this:
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxies[{{#key}}]" value="{{this}}"/>
<span>{{this}}</span>
</label>
</p>
{{/each}}
(note that if proxyObj is an array, you would have to use #index instead of #key; also, depending on the proxyObj fields' structure, you may have to use this.title as the values to display and whatnot).
As for your server-side handling, you'll have to loop through the proxies you receive and handle them one by one, e.g.
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
project.proxies = []; // this is only in case you wanna remove the old ones first
const proxies = req.body.proxies;
for(let i = 0; i < proxies.length; i++) {
// Add to proxy array on the Project object in the collection
project.proxies.push({ proxyTitle: proxies[i].title });
}
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});

How do I update a specific document from a form through mongoose?

Struggling with this problem because I'm missing some fundamentals I think, but after hours looking through the docs I think I need some help :)
I have a (simplified) collection as so:
const ExampleSchema= new Schema ({
Word: String,
Difficulty: String,
)};
Which I am succesfully displaying on a page as so:
<% ExampleSchemas.forEach(exampleschema=> { %>
<p><%= exampleschema.word %></p>
<p style="display: none" name="ExampleSchemaID" value="<%= ExampleSchema._id %>"</p>
<% }) %>
For each word I have a form below, I would like the user to be able to select easy, ok or hard and for this to set the value in the DB.
<form method="PUT">
<button formaction="/review/feedback/easy">Easy</button>
<button formaction="/review/feedback/ok">Ok</button>
<button formaction="/review/feedback/hard">Hard</button>
/form>
I have played around with a route like this to no avail
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate(
req.params.ExampleSchema._id,
req.Difficulty= easy);
The further issue is that I would like to display x number of these documents to the user, therefore I need a route that gets only that specific word.
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate({"_id": req.params.ExampleSchema._id},{"Difficulty": "3"});
but please not your difficulty is a number, so why not use a number instead:
const ExampleSchema= new Schema ({
Word: String,
Difficulty: Number,
)};
router.put("/review/feedback/easy", function (req,res){
var ID = req.body.ExampleSchemaID;
ExampleSchema.findByIdAndUpdate({"_id": req.params.ExampleSchema._id},{"Difficulty": 3});

Get Request for data in MongoDB with ExpressJs

I am new to nodejs/web app and I am trying to get data out from MongoDB.
My mongoDB has documents under collection "a"
{_id:("1"), "Question":"mcq1", "Answer":"one", "Keyword":"CLASS"}
{_id:("2"), "Question":"mcq2", "Answer":"two", "Keyword":"CLASS"}
{_id:("3"), "Question":"mcq3", "Answer":"three", "Keyword":"OVERLOAD"}
{_id:("4"), "Question":"mcq4", "Answer":"four", "Keyword":"OODP"}
I want to extract the data "Question": field_value, "Answer":field_value out using nodejs -> expressjs through a button and textbox form using the unique Keyword and to be displayed in a table form as below.
<tr>
<td><%= Question %></td>
<td><%= Answer %> </td>
</tr>
I have been able to get what i want with the monogodb shell using
db.a.find({"Keyword":CLASS},{"Question":1,"Answer":1,_id:0})
Currently the textbox and button onclick codes are below.
Input Keyword to search: <input type="text" id="searchBtn" value="">
<button type="button" onclick="alert(document.getElementById('searchBtn').value)">Click me!</button>
How can i extract the Question and Answer with the button click?
Using db.a.find({"Keyword":CLASS},{"Question":1,"Answer":1,_id:0})
i want to get a table in the form of
Question, Answer,Keyword
mcq1, one, CLASS
mcq2, two, CLASS
If you do a db query based on that keyword you'll get the occurrences in mongo, so you can do a form(GET/POST) field with the input and the button you already have. This will be caught in your express code as a route, there you can implement some basic code filling your search needs and the return value will be some simple data or an array if multiple match.
This is a basic search that one user (Miqe) once taught me here, first you need the query (or you can just put directly there) and later search in the mongo. But pay attention that your callback function will return the result, here is just a console.log() after returning the values you can assign them to a variable and pass through a template engine rendering in the html in the desire format.
query = {username: username, password: password}
dbo.collection('Users').find(query, function(err, user){
if(err) throw new Error(err);
if(!user)
console.log('Not found');
else
console.log('Found!');
})
Here is just the code to find in a collection named Users, you still need to join with the route and do a connection to the DB.
I'll leave a link who helped me a lot! The mongo documentation is still a good start too.
https://www.w3schools.com/nodejs/nodejs_mongodb_query.asp

Inline lambda - get rate with latest date

The MVC ViewPage contains Users (intranet.Model.User). Each user may have one or more rates of pay represented in the model as intranet.model.User.UserRates.
I need to be able to display the latest rate of pay per user using an inline Lambda expression but I am having no success (an error message is displayed rather than the rate of pay).
My latest incarnation is:
<% var latestRate = item.UserRates
.GroupBy(hr=>hr.rate)
.Select(g=>g.Single(
d=>d.effective == g.Max(m=>m.effective))
); %>
<%= Html.Encode(latestRate) %>
</td>
Error message in column is:
System.Linq.Enumerable+WhereSelectEnumerableIterator2[System.Linq.IGrouping2[System.Decimal,intranet.Models.UserRate],intranet.Models.UserRate]
I can't figure out exactly what your data looks like, but couldn't you do something like this?
var latestRate = item.UserRates
.OrderByDescending(x => x.effective)
.First();
Final iteration (for now):
<% var userRate = item.UserRates
.OrderByDescending(hr=>hr.effective)
.FirstOrDefault() ?? new intranet.Models.UserRate(); %>
<td class="hrlyRate">
<%= Html.Encode(string.Format("{0:0.000}", userRate.rate))%>
</td>

Resources