Node.js with express insert new field into existing mongoDB document from server side - node.js

Hello I am looking to implement a way to dynamically insert new fields to an existing mongoDB document from the server side with node.js and express.
For example in the local mongoDB the document looks like this.
{
value: 'Google',
url: 'https://google.com',
env: 'Test'
}
I have a route that will already update the current document fields from a form on the UI. However I want to combine that logic with the ability to insert new fields upon updating.
The route below handles updating the document with the existing fields.
router.put("/:id", (req, res) => {
let value = req.body.value;
Application.findByIdAndUpdate(req.params.id, req.body.application, (err,
updatedApp) => {
if(err){
console.log(err);
} else {
console.log(updatedApp)
req.flash("info", updatedApp.value + " " + "successfully edited!");
res.redirect("/qa-hub/applicationmanager");
}
});
});
On the front end I use EJS with a form to update the document. Example below:
<div class="row">
<div class="col-md-6">
<div class="form-group">
<input class="form-control" type="url" name="application[url]" value="<%= application.url %>" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<select class="form-control" name="application[env]" required="true">
<option class="text-center" value='<%= application.env %>'><%= application.env %></option>
<option value='Beta'>Beta</option>
<option value='Dev'>Dev</option>
<option value='Does Not Apply'>Does Not Apply</option>
<option value='Prod'>Prod</option>
<option value='QA'>QA</option>
<option value="Test">Test</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<a class="btn btn-outline-warning" href="/qa-hub/applicationmanager">Cancel</a>
</div>
<div class="form-group">
<button class="btn btn-outline-primary" id="btn" >Update</button>
However i'd like to add three additional fields upon submitting the form. I want to capture the currently logged in user that performed the edit and the date and time. I already have that worked out but how could I implement inserting new data to the existing document from the route.put while also keeping the logic to update the current fields if any changes are made.
So after the user makes some changes and updates the three fields the document would look something like below, except id handle the logic to get the currently logged in user at that time and the date/time and pass it in but for the example below I will hardcode it.:
{
value: 'Google',
url: 'https://google.com',
env: 'Test',
updatedBy: "Test User"
timeUpdated: "12:54",
dateUpdated: "7/25/2018"
}
So ultimately I want to keep a log of the changes and than be able to add it to the UI.

So with a little help from this post TypeError: callback.apply is not a function (Node.js & Mongodb) I was able to append new fields to the existing document using $set. However when trying to perform the req.body.application before $set it would throw an error stating that callback.apply is not a function. So I just created a callback if you will to update the document after setting the new fields. I know its messy but just wanted to get it working feel free to use and clean up the code for your self.
router.put("/:id", (req, res) => {
let value = req.body.value;
let value = req.body.value;
let date = new Date();
let hour = date.getHours();
hour = (hour < 10 ? "0" : "") + hour;
let min = date.getMinutes();
min = (min < 10 ? "0" : "") + min;
let time = hour+":"+min;
let year = date.getFullYear();
let month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
let day = date.getDate();
day = (day < 10 ? "0" : "") + day;
today = month+"/"+day+"/"+year;
let updatedTo = month+"/"+day+"/"+year;
let updatedT = hour+":"+min;
let updatedBy = req.user.username;
//Find the document based on it's ID and than append these three new fields
Application.findByIdAndUpdate(req.params.id,
{ $set: {
updatedTime: `${updatedT}`,
updatedToday: `${updatedTo}`,
updatedBy: `${updatedBy}`
}}, { upsert: true },
(err,updatedApp) => {
if(err){
return handleError(err);
} else {
// Than if any changes were made from the UI we apply those updates taken
// from the form with req.body.application
Application.findByIdAndUpdate(req.params.id, req.body.application,
(err, updatedApp) => {
if(err){
return handleError(err);
} else {
console.log(updatedApp)
req.flash("info", updatedApp.value + " " + "successfully edited!");
res.redirect("/qa-hub/applicationmanager");
}
}
});
});

Related

how to get single value from input name field instead of array in NodeJS

I am trying to delete elements from the note list. when I try to match a single name to a list title, it shows an array of items.
i want to match
const input name ===day
and output should be "home" from input name field
but it show ["home","home","home","home"]
here is my delete form code:
<form action="/delete" method="POST">
<% for (let i=0; i<newListItems.length; i++) { %>
<div class="item">
<input type="checkbox" onChange="this.form.submit()" name="checkboxname" value="<%=newListItems[i]._id%>">
<p><%= newListItems[i].name %></p>
</div>
<input type="hidden" name="listName" value="<%= listTitle %>"></input>
<% } %>
</form>
app.js code:
app.post("/delete", function (req, res) {
const deleteItem = req.body.checkboxname
const listName = req.body.listName
console.log(listName)
if (listName === day) {
console.log("hello")
} else {
console.log("custome list value")
}
})
There is 4 hidden inputs rendered with same name listName.
So your request payload will be fulfilled with array of values from all of these inputs.
Move hidden input outside of PHP loop.
The point is to make one input with name='listName' instead of four

"Function Query.where() called with invalid data. Unsupported field value: undefined" Error in angular with firebase

I am using angular to create a booking web app using firebase. I am trying to query through the firebase data and filter them to avoid duplicate bookings to be recorded at the same time slots, but I keep getting the error Function Query.where() called with invalid data. Unsupported field value: undefined. I am new to angular and firebase and this is my first project as well. I could really use some help on this.
checkTimeSlot(){
this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot;
const queryTimeRefUFC = this.afs
.collection("ufc", ref => ref.where("date_time", "==", this.formData.date_time))
.get();
if (queryTimeRefUFC == null){
console.log("true");
return true;
}
else{
console.log("false");
return false;
}}
This is the method in the component.ts where I created the query search. This method is called when the value in the select input changes.
submitBookingUFC() {
this.formData.fullName = this.user.name;
this.formData.userID = this.user.id;
this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot;
if (this.checkTimeSlot() == true){
alert("The Time Slot you have selected has already been taken. Please select another.");
}
else if (this.checkTimeSlot() == false){
console.log(this.formData.date_time);
this.crudApi.SaveBookingUFC(this.formData);
alert("Record Saved Successfully");
this.ResetFormUFC(); // Reset form when clicked on reset button
} }
This is the method called when the user clicks the submit button.
<div>
<input
type="date"
class="in-bdate"
id="dateUFC"
name="dateUFC"
[min]="today"
max="{{bookMax}}"
value="{{formData.bookDate}}"
[(ngModel)]="formData.bookDate"
#dateUFC="ngModel"
required>
</div>
<div>
<div style="color:red"
*ngIf="dateUFC.errors && (dateUFC.dirty || dateUFC.touched)">
<p *ngIf="dateUFC.errors.required">
<sup>*</sup>Booking Date is required
</p>
</div>
</div>
<div *ngIf="!dateUFC.errors && (dateUFC.dirty || dateUFC.touched)">
<div>
<label for="timesUFC">Time Slot</label>
</div>
<div>
<select
name="timesUFC"
class="in-tslot"
value="{{formData.timeSlot}}"
(change)="checkTimeSlot()"
[(ngModel)]="formData.timeSlot"
#timeSlotUFC="ngModel"
required>
<option *ngFor="let time of timesUFC" >{{time.name}}</option>
</select>
</div>
<div>
<div style="color:red"
*ngIf="(timeSlotUFC.dirty || timeSlotUFC.touched)">
<p *ngIf="timeSlotUFC.errors && timeSlotUFC.errors.required">
<sup>*</sup>Time Slot is required
</p>
<p [ngModel]="timeError" name="error" ngDefaultControl>{{ timeError }}</p>
</div>
</div>
</div>
This is the html code I used to attain the date for the booking and the time of the booking. Using these I created a composite field as date_time using this.formData.date_time = this.formData.bookDate + "_"+ this.formData.timeSlot; to avoid getting duplicate bookings for the same time slot on the same day. The formData here is linked to a class I created for the entire booking array using formData = new UfcData; where UfcData is the name of the class.
export class UfcData {
docID!: string;
userID!: string;
fullName!: string;
contactNo!: string;
timeSlot!: string;
bookDate!: string;
date_time!: string;}
This is the data in the UfcData class.
This is how my Collection and Documents look like
What I need is to acquire the user's input from the form which is acquire from ngModel to the UfcData class and make sure that there aren't any documents in the cloud firestore with the exact same date and time.
*After tweaking the code a little bit the above error does not display. However the if condition in the checkTimeSlot() method always returns true no matter what the option on the select is.
Thanks in advance.

MeanStack: conflict on editing feature'_id' would modify the immutable field '_id'

I've seen solutions in this post and in others, but I did follow the instructions and I'm still getting the error. I understand all the logic, id's can't be replaced, they are immutable, but in my mean stack app, the error persists. The code:
Node route
router.put("/:id" ,(req, res, next) => {
const note = new Note({
_id:req.body.id,
project: req.body.project,
note: req.body.note,
date:req.body.date,
});
Note.updateOne({ _id: req.params.id }, note).then(result => {
console.log(result)
res.status(200).json({ message: "Update successful!" });
});
});
Front End edit component:
ngOnInit(): void {
this.notesForm=this.fb.group({
note:['', Validators.required],
project:['', Validators.required],
date:[null,Validators.required]
})
this.route.paramMap.subscribe((paraMap:ParamMap)=>{
if(paraMap.has('noteId')){
this.mode='edit';
this.noteId= paraMap.get('noteId');
this.note=this.notePadService.getNote(this.noteId)
.subscribe(noteData=>{
this.note = {id: noteData._id, note: noteData.note, date: noteData.date, project: noteData.project};
})
}else{
this.mode='create';
this.noteId=null;
}
})
this.getProjects();
}
onSubmit(){
if(this.mode==='create'){
this.notePadService.submitNotes(this.notesForm.value)
console.log(this.notesForm.value);
this.router.navigate(['/notes-listing'])
}else{
this.notePadService.updateNote(
this.noteId,this.notesForm.value
)
}
}
Service:
getNote(id:string){
return this.http.get<any>('http://localhost:3000/api/notes/'+id)
}
updateNote(id:string,note:Notes){
this.http.put('http://localhost:3000/api/notes/'+id,note)
.subscribe(response=>console.log(response))
}
Also I cant pre-populate the reactive form with the values to edit:
<label>Select a Project</label>
<select (change)="test($event.target.value)" formControlName="project"
class="form-control">
<option
*ngFor="let p of projects"
[value]="p.name">{{ p.name }}</option>
</select>
<!------->
<label for="notes">Note</label>
<textarea class="form-control" formControlName="note" type="text" rows="4"></textarea>
<div class="input-group">
<mat-form-field appearance="fill">
<mat-label>Choose a date</mat-label>
<input formControlName="date" matInput [matDatepicker]="picker">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<button mat-raised-button color="warn">Submit</button>
</form>
When I click the edit button (in another component), I get the correct URL but no values, and onsubmit, the backend crashes:
http://localhost:4200/edit/601ec21882fa6af20b454a8d
Can someone help me out? I'm very confused and I cant understand the error since I've done everything the other posts suggest...
when you call updateOne you already pass as the first argument the id that should be updated. As second argument you pass note that should contain only the properties that will be updated, but not _id itself:
const note = {
project: req.body.project,
note: req.body.note,
date:req.body.date,
};

Filtering node.js (express, mongoose) app

I am trying to a node.js application (express, mongoose) and I want to give the option to the user to filter the data they receive through checkboxes. In the front end I store the user's choice in cookies (I do not know know if there is a better way) but I failed to filter the data in my database. The html code is that:
A modal that gives the user the option to check for specific data
<div id="modal">
<div id="modalContent">
<h3>Filters</h3><svg xmlns='http://www.w3.org/2000/svg' id="closeBtn" class='ionicon' viewBox='0 0 512 512'><title>Close</title><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='32' d='M368 368L144 144M368 144L144 368'/></svg>
<div>
<form method="GET">
<div>
<input type="checkbox" class="save-cb-state" name="Vasilopoulos" value="Vasilopoulos">Vasilopoulos
</div>
<div>
<input type="checkbox" class="save-cb-state" name="Masoutis" value="Masoutis">Masoutis
</div>
<div>
<input type="checkbox" class="save-cb-state" name="Web Only" value="Web Only">Web Only
</div>
<div>
<input type="checkbox" class="save-cb-state" name="In Store" value="In Store">In Store
</div>
<button type="submit">Αναζήτηση</button>
</form>
</div>
</div>
</div>
My model is that:
The checkboxes are filtering for storeName and/or offerType (The problem is when the user wants to filter both data)
const mongoose = require("mongoose");
const offersSchema = new mongoose.Schema({
imgLink: String,
title: String,
startPrice: Number,
discountPrice: Number,
pageLink: String,
storeName: String,
offerType: String,
discountPercentage: {
type: String,
trim: true
}
});
module.exports = mongoose.model("Offers", offersSchema);
The first two checkboxes are referring to StoreName attribute of my model and the last two are referring to offerType
The code in my controller is this:
async function getOffers(req) {
const cookiesArray = Object.values(req.cookies);
const page = parseInt(req.query.page || 1);
const sort = req.query.sort || "discountPrice";
const itemsPerPage = 10;
let products;
//If cookiesArray length is greater than 0 means that the user has check at least one checkbox
if(cookiesArray.length !== 0) {
products = await Offers.find({ offerType: { $in: cookiesArray}, storeName: { $in: cookiesArray } })
.skip((page - 1) * itemsPerPage)
.limit(itemsPerPage)
.sort(sort);
} else {
products = await Offers.find()
.skip((page - 1) * itemsPerPage)
.limit(itemsPerPage)
.sort(sort);
}
return {
category: "offers",
products,
sort,
currentPage: page,
hasNextPage: itemsPerPage * page < totalProducts,
hasPreviousPage: page > 1,
nextPage: page + 1,
previousPage: page - 1,
lastPage: Math.ceil(totalProducts / itemsPerPage)
}
}
getOffers is a helper function that my controller uses to fetch data. I tried to use the in operator but if the user check one or two values from the first two checkboxes and one or two values from the last two check boxes the in operator fails.

validating bootstrap datepicker using Qunit

I have a bootstrap calendar on my page that lets the user pick a date. I have a start time and endtime. I would like to test to do the following.
If the user leaves the date selector empty, the test will fail with a message like cannot leave fields empty.
If the user enters an endtime that is less than the startime the test will fail and throw a message like Cannot have endtime less than starttime.
I will paste the code that works with the datepicker. I am using Qunit for testing purposes and Bootstrap 4.
button.js
// Runs date picker plugin
$('input.date').datepicker();
// Gets data
var data;
fetch('/reportSaver', {
// data: dates,
method: 'POST'
}).then(function (response) {
return response.json();
}).then(function (json) {
data = json;
});
// Form submit
$('form').on('submit', function (event) {
event.preventDefault();
var dates = {
startdate: new Date($('.startdate').val()),
enddate: new Date($('.enddate').val())
};
// Minimum validation for dates
if ((dates.startdate && dates.startdate > dates.enddate) ||
(dates.enddate && dates.enddate < dates.startdate)) {
return alert('Use valid dates!');
}
// Filter rows
var rows = data.filter(function (register) {
var date = new Date(register.receivedDateTime);
return (
(!dates.startdate || date > dates.startdate) &&
(!dates.enddate || date < dates.enddate)
);
// Convert to HTML
}).map(function (row) {
return `
<tr>
<td> </td>
<td>${row.subject || '-'}</td>
<td>${row.receivedDateTime || '-'}</td>
<td>${row.isRead || '-'}</td>
<td>${row.sendDateTime || '-'}</td>
</tr>
`;
});
// Show content
$('table tbody').html(rows.join(''));
});
// Clear click
$('.clear-table').on('click', function () {
// Clears table
$('table tbody').html('<tr><td colspan="5">Make a search</td></tr>');
// Clears inputs
$('input').val('');
});
form.html
{{!-- Post form for Date Picker --}}
<form id="post_form" method="GET" action="/routes/reportSaver.js">
<div class="date-picker">
<h3>Date</h3>
<input placeholder="Initial date" type="text" class="date startdate"> -
<input placeholder="End date" type="text" class="date enddate">
</div>
<hr>
{{!-- Button to save the Report --}}
<button id="bt1" type="submit" class="btn btn-danger">Click to Get Reports</button>
<button id="bt2" type="submit" class="btn btn-danger clear-table">Clear</button>
</form>
Qunit Test Example
QUnit.test("Datepicker Test", function (assert) {
var datepicker = $("#startDate");
var event = $.Event("onSelect");
datepicker.on("onSelect"),
function () {
alert("Test");
};
// Trigger the key event
datepicker.trigger(event);
});

Resources