html form input name for mongoose - node.js

MongoDB
{
"_id" : ObjectId("5bfac9c5c44526e73f960e89"),
"brand" : "Under Armour",
"amazon" : {
"order" : "666-666-666",
"id" : "B072LNJPS1"
}
...
}
HTML
<form....>
<input class="form-control" type="text" name="brand">
<input class="form-control" type="text" name="amazon.id">
<input class="form-control" type="text" name="amazon.order">
...
</form>
NodeJs
const myPost = request.body
const query = { _id: request.body._id }
const options = { upsert: true, new: true }
await model.findOneAndUpdate(query, myPost, options);
I have this error message because of the '.' in the field name
errmsg: 'Updating the path 'amazon.id' would create a conflict at 'amazon''
You have tips for the field name to match the model structure

This may not be a complete answer, but you need to replace "amazon.id" with "amazon[id]" to match the nested structure.
Reference: Colt Steele's "The Web Developer Bootcamp"

Related

How to get Express route's body parameters as 'string' instead of 'any' in Typescript?

I'm sending a string productId using a hidden input type through a <form>:
<form action="/cart" method="POST">
<button class="btn" type="submit">Add to Cart</button>
<input type="hidden" name="productId" value="<%= product.id %> "> // EJS template engine
</form>
And then receiving the productId in an Express route as follows:
export const postCart = async (request: Request, response: Response): Promise<void> => {
const productId = request.body.productId // This is inferred as type 'any'
const allProducts = //...Array<Product>
const requestedProduct = allProducts.find((product) => product.id === productId) // Problem: this is always false
}
Problem
The condition product.id === productId is always false because the type of product.id from the database is string and type of productId received from the body is any. I need a way for both of them to be of same type.
What I tried so far
I tried annotating and casting the type of productId:
const productId: string = request.body.productId // Doesn't work
const productId: string = request.body.productId as string // Doesn't work
const productId: string = request.body.productId + '' // Doesn't work
The only thing that works is, if I create the Numbers from the ids:
Number(product.id) === Number(productId)
But this can't be a solution because I'm using UUID strings for representing the product id. And casting UUIDs to the numbers may not be a good idea.
Any input would be much appreciated.
So we chatted out in the comment section but the problem seems to have been the trailing space in the html:
<input type="hidden" name="productId" value="<%= product.id %> ">
updated to:
<input type="hidden" name="productId" value="<%= product.id %>">

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,
};

Selecting first element from the dropdown [ select tag ] cause the backend to crash but selecting other works just fine

I'm building a simple app with node.js and reactjs but the problem arises when I want to add book especially while selecting dropdown value.
In the select tag when I select the first element it crashes the backend but when I select the second or below it they work just fine. What could be the reason behind it ? Dropdown menu has been filled correctly. My code is :
import React, { Component } from 'react'
import axios from 'axios'
export default class AddBook extends Component {
state = {
name : '',
author : '',
publishers : '',
pages :'',
genres : [],
genre : '',
addedMessage : null
}
async componentDidMount() {
const genres = await axios.get('http://localhost:5000/api/genres')
this.setState({ genres : genres.data})
}
onInputChange = (e) => {
this.setState({[e.target.name] : e.target.value }) //looks for name
}
onFormSubmit = (e) => {
e.preventDefault();
const book = {
name : this.state.name,
author : this.state.author,
publishers : this.state.publishers,
pages : this.state.pages,
genres : this.state.genre
}
axios.post('http://localhost:5000/api/books', book)
.then(res => console.log(res))
.catch(err => console.log("error occured while posting data ", err))
console.log(book)
//alert("book added successfully !!!")
this.setState({addedMessage : 'Book added successfully'})
window.location = "/books"
}
render() {
const { name, author, publishers, pages, genres, addedMessage } = this.state;
return (
<div className="container">
<h2>ADD BOOK </h2>
{ addedMessage && <h2 style={{textAlign : 'center'}} className="alert alert-info">{addedMessage}</h2> }
<form onSubmit = { this.onFormSubmit }>
<div className="form-group">
<label htmlFor="name">BookName:</label>
<input type="text" required className="form-control" id="name"
placeholder="Enter Book name" name="name"
onChange={this.onInputChange}
value={name}
/>
</div>
<div className="form-group">
<label htmlFor="author">Author:</label>
<input type="text" required={ true } className="form-control"
id="author" placeholder="Enter Author" name="author"
onChange={this.onInputChange}
value={author}
/>
</div>
<div className="form-group">
<label htmlFor="publishers">Publishers:</label>
<input type="text" required className="form-control"
id="publishers" placeholder="Enter Publishers Name"
name="publishers"
onChange={this.onInputChange}
value={publishers}
/>
</div>
<div className="form-group">
<label htmlFor="pages">Pages:</label>
<input type="number" required className="form-control"
id="pages" placeholder="Enter No of Page" name="pages"
onChange={this.onInputChange}
value={pages}
/>
</div>
<div className="form-group">
<label htmlFor="genre">Category:</label>
<select type="select" className="form-control"
id="genre"
name="genre"
onChange={this.onInputChange}
>
{/* <option value="selectCategory" onChange={this.onInputChange}>Select</option> */}
{
genres.map( genre => (
<option key={genre._id} onChange={this.onInputChange} value={genre.name}>
{genre.name }</option>
))
}
</select>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
)
}
}
My backend is up and running and has successfully fetched the genres[ categories ] , but I am not able
to figure out why selecting the first doesn't work but others just work fine. How should I solve it ?
It shows the following error for this case.
Error: Book validation failed: genres: Path `genres` is required.
at ValidationError.inspect
(E:\nodejs\nodejs\MERN_STACK_Book\node_modules\mongoose\lib\error\validation.js:48:26)
at formatValue (internal/util/inspect.js:718:31)
at inspect (internal/util/inspect.js:287:10)
at afterInspector (internal/errors.js:682:14) {
errors: {
genres: ValidatorError: Path `genres` is required.
at validate (E:\nodejs\nodejs\MERN_STACK_Book\node_modules\mongoose\lib\schematype.js:1178:13)
at E:\nodejs\nodejs\MERN_STACK_Book\node_modules\mongoose\lib\schematype.js:1161:7
at Array.forEach (<anonymous>)
at SchemaString.SchemaType.doValidate
(E:\nodejs\nodejs\MERN_STACK_Book\node_modules\mongoose\lib\schematype.js:1106:14)
at E:\nodejs\nodejs\MERN_STACK_Book\node_modules\mongoose\lib\document.js:2387:18
at processTicksAndRejections (internal/process/task_queues.js:79:11) {
properties: [Object],
kind: 'required',
path: 'genres',
value: '',
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: 'Book validation failed'
}
[nodemon] app crashed - waiting for file changes before starting...
And code for my book schema is as follows
const mongoose = require('mongoose')
//schema for genres
const bookSchema = mongoose.Schema({
name : {
type : String,
required : true,
trim : true,
unique : true //title is made unique.
},
author : {
type : String,
required : true,
trim : true
},
publishers : {
type : String,
trim : true,
},
pages : {
type : Number,
required : true,
trim : true
},
genres : {
type : String,
required : true,
trim : true
},
create_date : {
type : Date,
default : Date.now
}
})
//It will create books collection in your database and documents
//inside that collection will have fields from bookSchema when you save first document.
const Book = module.exports = mongoose.model('Book', bookSchema)
//get the books
module.exports.getBooks = (callback, limit) => {
// Book.find(callback)
Book.find(callback).limit(limit)
}
//get single book
module.exports.getBookById = (id,callback) => {
// Book.find(callback)
Book.findById(id,callback);
}
module.exports.addBook = (book,callback) => {
Book.create(book, callback);
}
module.exports.updateBook = (id,book,options, callback) => {
const query = {
_id : id
}
const updatedBook = {
name : book.name,
author : book.author,
publishers : book.publishers,
pages : book.pages,
genres : book.genres
}
Book.findByIdAndUpdate(query, updatedBook, {} , callback);
}
module.exports.deleteBook = (id,callback) => {
const query = {
_id : id
}
Book.findByIdAndDelete(query, callback);
}
I checked some of the similar answers in the stackoverflow but couldn't figure out why I'm getting that
error.
Here's the problem with your code :
The first option of the dropdown is chosen by default, and thus "selecting" the first option doesn't trigger the Category dropdown's onChange function (because the selected value doesn't really change).
Since the default value of this.state.genre is an empty string, the genres property of the book you send to the backend also has genres as an empty string.
Finally, Mongoose does not accept an empty string for a required string field, which gives you the Path 'genres' is required error.
Try switching to another option and back to the first. It will work then because the onChange function will then get called and this.state.genre will be set properly.
There are a few ways to fix this:
Add a placeholder option to the dropdown - something like "Select an option...". Then the user will be forced to select a genre from the dropdown and onChange would be called.
Or, in componentDidMount, set this.state.genre to the first genre of the list of genres you fetch.

Ajax status 0, success not firing, no info with the errors thrown

I'm working on an app where the user enters info into a form, the form passes the info to my route, the route passes the info to the db, and then when done, it's supposed to update the page accordingly.
The issues I am having is that when you hit submit, the info does land on the database and gets saved but then on the callback/success/complete part of ajax, they dont fire and you have to hit a manual reload for the info to show up. I'm going to assume that this is because of the readystate of 0.
I've tried many solutions other people here on SO have gotten but to no avail.
Here is my code.
HTML:
<div id="createTodoFormW">
<form method="post" id="createTodoForm">
<input type="text" name="todoCompanyName" id="todoCompanyName" placeholder="Enter a Company name or leave empty if its a personal ToDo "><br>
<input type="text" name="todoTitle" id="todoTitle" placeholder="Enter a Title for this note"><br>
<input type="text" name="todoIsFor" id="todoIsFor" placeholder="Who is this ToDo for? ( If left empty, ToDo will be assigned to [ YOU ] )"><br>
<input type="hidden" name="todoPostedBy" id="todoPostedBy" value="LOGGED IN USER HERE">
<input type="hidden" name="todoCompleted" id="todoCompleted" value="no">
<input type="text" name="todoFirstMessage" id="todoFirstMessage" placeholder="Enter your ToDo message"><br>
<select name="todoPriority" id="todoPriority">
<option id="todoPriorityDefaultSelected" >Please select your ToDo priority</option>
<option id="todoPrioritySelected" selected>green</option>
<option id="todoPrioritySelected">yellow</option>
<option id="todoPrioritySelected">red</option>
</select><br>
<input type="submit">
</form>
</div><!-- createTodoFormW ender -->
express/node route:
app.post("/notesapi", (request, response) =>{
//mongoose.connect(databaseLoc);
const newNoteToAdd = new NotesModel({
todoCompany : request.body.todoCompany,
todoIsFor : request.body.todoIsFor,
todoPostedBy : request.body.todoPostedBy,
todoCompleted : request.body.todoCompleted,
todoPriority : request.body.todoPriority,
todoMessages : request.body.todoMessages,
todoTitle : request.body.todoTitle,
todoDate : currDate
});
newNoteToAdd.save((err, data)=>{
if(err){
console.log("There was an error uploading your data " + err);
}else{
//mongoose.connection.close();
console.log("Data upload success.");
}
});
});
front end js:
$("#createTodoForm").submit(function(evt){
evt.preventDefault();
$.ajax({
type : "POST",
cache : false,
dataType : "json",
contentType : "application/json; charset=utf-8",
url : "/notesapi",
data : JSON.stringify({
todoCompany : $("#todoCompanyName").val(),
todoTitle : $("#todoTitle").val(),
todoPostedBy : $("#todoPostedBy").val(),
todoIsFor : $("#todoIsFor").val(),
todoMessages : $("#todoFirstMessage").val(),
todoPriority : $("#todoPriority").val(),
todoCompleted : false
}),
success : function(){
console.log("did ajax fire and comlete?");
},
error : function(XMLHttpRequest, textStatus, errorThrown) {
if (XMLHttpRequest.readyState == 4) {
console.log("HTTP error (can be checked by XMLHttpRequest.status and XMLHttpRequest.statusText");
}
else if (XMLHttpRequest.readyState == 0) {
console.log("Network error (i.e. connection refused, access denied due to CORS, etc.");
console.log(textStatus);
console.log(errorThrown)
console.log(XMLHttpRequest.readyState);
}
else {
console.log("something weird is happening");
}
}
}); //ajax end
});
I need help. Been on this for days and cant figure it out. The main question i guess is, where is this state coming from and why doesnt my success function run?
thanks in advance for any assistance given.

How search value inside an object in Redis?

I'm newbie to Redis and Nodejs, I've watched this tutorial and i'm trying to search users by name,
Here is the object, returned from redis when i pass the id:
{
first_name: '‪john',
last_name: 'doe',
email: 'john#gmail.com',
phone: '543313305',
id: 'user001'
}
Here is the function of the search :
app.post('/user/search',function (req,res,next) {
let id = req.body.id;
client.hgetall(id ,function(err,obj){
if(!obj){
res.render('searchusers',{
error:"user doesn't exist",
});
} else {
obj.id = id
console.log(obj);
res.render('details',{
user:obj, });
}
});
});
I've tried to replace the search by id to search by first_name by doing this:
First I've changed the field name to "first_name" instead of "id"
<h1>Search Users</h1>
{{#if error}} <span>{{error}}</span>{{/if}}
<form class="form-inline" method="POST" action="/user/search">
<div class="form-group">
<input type="text" name="first_name" placeholder="Search" class="form-
control">
</div>
<input type="submit" class="btn btn-primary" value="Search">
And than I've changed it in the app.js ;
app.post('/user/search',function (req,res,next) {
let first_name = req.body.first_name;
client.hgetall(first_name ,function(err,obj){
if(!obj){
res.render('searchusers',{
error:"user doesn't exist",
});
} else {
obj.first_name = first_name
console.log(obj);
res.render('details',{
user:obj, });
}
});
});
The hgetall method that you are using in the search function of the method looks up a hash by the key, which in this case is the user id and returns all of the fields of the hash from Redis.
There is no function to search over the fields of the hash. If you need to be able to map from a first name to a user, you will need to manually build a secondary index using a data structure such as sets, that allows you to go from first name to the users that have that first name.

Resources