MeanStack: conflict on editing feature'_id' would modify the immutable field '_id' - node.js

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

Related

how to delete object array from node js

I am creating a simple todolist using react js and node js without database (to store data in objact array) I need to delete elements one by one by clicking. For each element I have Delete button. now todo list delete from front end(react), how to delete it from node any one please help me
todo.js
function Remove(id) {
const updateTodos=[...tasks].filter((obj)=>obj.id!==id)
setTasks(updateTodos)
}
if(isLogged){
return (
<div >
<h1>ToDo List</h1>
<form onSubmit={HandleSubmit}>
<input type="text" placeholder=" Add item..." name="list" value={toDos} onChange={(e)=>setToDos(e.target.value)}/>
<button id="btn" type="submit">Add</button>
</form>
<ul>
{tasks.map(obj=><li key={obj.id}>{obj.toDo}<button type='delete' onClick={(e)=>Remove(obj.id)} >Delete</button>
<input type="checkbox" id="" name=""onChange={(e)=>{
console.log(e.target.checked)
console.log(obj)
setTasks(tasks.filter(obj2=>{
if(obj2.id===obj.id){
obj2.status=e.target.checked
}
return obj2
}))
}} value={obj.status} /> </li>)}
</ul>
<h4>Completed tasks</h4>
{tasks.map((obj)=>{
if(obj.status){
return(<h5>{obj.toDos}</h5>)
}
return null
})}
</div>
);
}
}
export default Todo;
node- index.js
const lists =[
{id: new Date(),toDo:"learn react"}
]
app.post('/Todo',function(req, res){
lists.push({"id":new Date(),"toDo":req.body.list,"status":false})
console.log(lists)
res.status(200).json(lists[lists.length-1])
})
app.get('/Todo', (request, response) => response.status(200).json(lists));
req.body.list.pop()
res.status(200).json({"id":new Date(),"toDo":req.body.list,"status":false})
This will remove the last element in the list

Angular : [Object Object]

I'm working on a NodeJS/Angular project and while trying to do a simple CRUD, I'm blocked when I try to get an element by ID.
I would like to retrieve all the info of a "Member" based on its ID and display the info in a table. I manage to get my JSON with the API call but when trying to display it in the table, it doesn't show anything.
My service, with the API call :
public getMember(id: number) {
return new Promise((resolve, reject) => {
this.http.get(this.config.apiServer + `astreintes/member/get/${id}`)
.subscribe((res) => {
resolve(res as Member);
console.log(res);
}, err => {
reject(err);
});
});
}
Result of the console.log: My correct JSON with the info of the member
My component.ts :
public search(){
this.memberService.getMember(this.id).then((data) => {
if(data){
this.member = (data as any).recordset;
console.log("Get member :"+ data);
this.indice = true;
}else{
}},
(error) => {
console.log(error);
}
);
}
Result of the console.log: "Get member : [Object Object]"
For the interface, I just have a dropdown list of all my members, and when I select one and click on the button "Search", it gets the info of my member correctly in the console. Then, I want to display it in my table below. My html code:
<form (submit)='search()' #searchMemberForm="ngForm" class="form-horizontal">
<select [(ngModel)]="id" name="member">
<option *ngFor="let member of membersList"
[value]="member.Id_OnCall_Member">{{member.Oncall_Member_Name}}</option>
</select>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-save btn-primary">Search</button>
</div>
</div>
</form>
<div *ngIf="indice">
<h1 style="text-align: center">
Informations
</h1>
<table class="table table-striped" *ngIf="indice">
<th>ID</th>
<th>Nom</th>
<th>Numéro de téléphone</th>
<th>Statut d'activité</th>
<tr *ngFor="let m of member">
<td>{{m.Id_OnCall_Member}}</td>
<td>{{m.Oncall_Member_Name}}</td>
<td>{{m.OnCall_Member_Phone}}</td>
<td>{{m.OnCall_Member_Status}}</td>
</tr>
</table>
</div>
Thanks for your help !
you used "+" in console.log: console.log("Get member :"+ data);
it means javascript is trying to convert the output to one type (string), but data is object. Below you can find how to get the correct output.
const res = {
memberId: 1
}
console.log(res) // {memberId: 1}
console.log('Member: '+ res); //Member: [object Object]
console.log('Member: ', res); // Member: {memberId: 1}
console.log('Member: '+ JSON.stringify(res)); // Member: {memberId: 1}
thanks for your answers.
My problem wasn't the console.log though, it was just an indication.
My real problem was that it didn't show anything in my table. But I solved it, here's how in case it might help other people.
In my component.ts, I replaced:
this.member = (data as any).recordset;
By this, simply:
this.member = data;

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.

Can't Edit and Update properties with form Reactjs and MongoDB

So I'm using Nodejs, MongoDB and Reactjs
and I'm trying to Edit properties of projects.
I have multiple projects and when I want to edit properties of one I can't do it. We can access to properties inside inputs, we can see Title and Type but can't even delete, write, he access to properties by its ID but then I can't change it, I guess I have multiple problems here than.
I'll write here my server code, and my Edit/Update project page and a gif with an example when I say that I can't even change anything on inputs.
My server code:
//Render Edit Project Page byId
app.get('/dashboard/project/:id/edit', function(req, res){
let id = req.params.id;
Project.findById(id).exec((err, project) => {
if (err) {
console.log(err);
}
res.json(project);
});
}
//Update Projects Properties byId
app.put('/dashboard/project/:id/edit', function(req, res){
var id = req.params.id;
var project = {
title: req.body.title,
typeOfProduction: req.body.typeOfProduction
};
Project.findByIdAndUpdate(id, project, {new: true},
function(err){
if(err){
console.log(err);
}
res.json(project);
})
};
My React Component Edit Project Page
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import './EditProject.css';
class EditProject extends Component {
constructor(props){
super(props);
this.state = {
//project: {}
title: '',
typeOfProduction: ''
};
}
inputChangedHandler = (event) => {
const updatedProject = event.target.value;
}
componentDidMount() {
// console.log("PROPS " + JSON.stringify(this.props));
const { match: { params } } = this.props;
fetch(`/dashboard/project/${params.id}/edit`)
.then(response => { return response.json()
}).then(project => {
console.log(JSON.stringify(project));
this.setState({
//project: project
title: project.title,
typeOfProduction: project.typeOfProduction
})
})
}
render() {
return (
<div className="EditProject"> EDIT
<form method="POST" action="/dashboard/project/${params.id}/edit?_method=PUT">
<div className="form-group container">
<label className="form--title">Title</label>
<input type="text" className="form-control " value={this.state.title} name="title" ref="title" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container">
<label className="form--title">Type of Production</label>
<input type="text" className="form-control " value={this.state.typeOfProduction} name="typeOfProduction" ref="typeOfProduction" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container button">
<button type="submit" className="btn btn-default" value="Submit" onClcik={() => onsubmit(form)}>Update</button>
</div>
</form>
</div>
);
}
}
export default EditProject;
Erros that I have:
1- DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.
2- Inputs can't change
3- When click "Update" button:
I think your update override the entire object because you forgot the $set operator. This is the operator to change only the atributtes of an object and not the entire object replacing!
Example:
Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
First of all, concerning the deprecation warning, you need to change the method findAndModify (As I do not see it here, I guess you're using it elsewhere, or maybe one of the methods you use is calling it) by one of the suggested methods and change your code accordingly.
Then, you need to learn about React and controlled components : https://reactjs.org/docs/forms.html
You need to set the component's state in your onChange handler, such as :
this.setState({
title: event.target.value // or typeOfProduction, depending on wich element fired the event
});
This is called a controlled component in React.
Concerning the response body you get when clicking on Update button, this is actually what you asked for :
res.json(project);
returns the project variable as a JSON file, which is displayed on your screenshot.
See this question for more information about it : Proper way to return JSON using node or Express
Try replace "value" in input tag with "placeholder"

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.

Resources