Where to pass an objectId as a property when deleting an entry? - node.js

I have an app that allows users to add notes, and I'm trying to add a delete functionality to the page. My route:
router.route('/:id').delete((req, res) => {
Note.findByIdAndDelete(req.params.id)
.then(() => res.json('Exercise deleted!'))
.catch(err => res.status(err).json('Error ' + err))
})
works when I test it in Postman, but I haven't managed to get the ObjectId from the database. It throws an error: Invalid status code: CastError: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Note" .
This is my Note schema:
const noteSchema = new Schema({
category: {type: String, required: false},
title: {type : String, required: true},
content: {type: String, required: true},
noteID: { type: mongoose.SchemaTypes.ObjectId, required: true, index: true }
}, {
timestamps: true,
})
This is my Note component:
import React from "react";
function Note(props) {
function handleClick() {
props.onDelete(props.id);
}
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={handleClick}>
Delete
</button>
<p>{props.category}</p>
</div>
);
}
export default Note
my App component:
function App() {
const [notes, setNotes] = useState([]);
useEffect(() => {
fetch('http://localhost:5000/notes')
.then(res => res.json())
.then(json => setNotes(json))
}, [])
function deleteNote(id) {
axios.delete('http://localhost:5000/notes/'+id)
.then(response => { console.log(response.data)});
}
{notes.map((noteItem, index) => {
return (
<Note
key={index}
//id={index}
title={noteItem.title}
content={noteItem.content}
category={noteItem.category}
onDelete={deleteNote}
/>
);
I'm not sure where to pass the id from the database, I tried passing it as a parameter in App.js (deleteNote(note.id)) or some variation of it, but it doesn't work. Could someone please tell me which step I'm missing to get the ObjectId? I also tried passigng noteItem._id when mapping notes to the Note component, but that deletes all notes at once. I tried these solutions as well: https://stackoverflow.com/questions/71544895/how-do-i-solve-casterror-cast-to-objectid-failed-for-value-undefined-type-s and https://stackoverflow.com/questions/63253129/successfully-delete-an-object-in-mongodb-using-findbyidanddelete-but-returns-an but I still get errors.
Thanks in advance!

Two issues.
CastError: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Note"
First of all, the id you are getting is undefined. This may cause problems, check if your client is sending the id properly, either via logging or debugging. If you pass in a correct string, Mongoose should automatically cast it for you.
If it didn't work, try using mongoose.Types.ObjectId(req.params.id)

I finally figured it out! I also put everything in the Note component to avoid any confusion, and through that I discovered what the problem was my endpoint was incorrect: instead of <button onClick={handleClick}> I had to turn it into an arrow function to call handleClick correctly and pass noteItem._id. I also got rid of the noteID property in the schema.
This is the Note component now:
import React, {useState,useEffect} from "react";
import axios from "axios";
function Note(props) {
const [notes, setNotes] = useState([])
useEffect(() => {
fetch('http://localhost:5000/notes')
.then(res => res.json())
.then(json => {console.log(json)
setNotes(json)})
}, [])
function deleteNote(id) {
axios.delete(`http://localhost:5000/notes/${id}`)
.then(() => { console.log("Note successfully deleted")});
}
return (
<div>
{notes.map((noteItem, index) => {
return (
<div className="note">
<h1>{noteItem.title}</h1>
<p>{noteItem.content}</p>
<button onClick={() => {deleteNote(noteItem._id)}}>
Delete
</button>
<p>{noteItem.category}</p>
</div>
);
})}
</div>
)
}
export default Note

Related

Is there any method to store array values in MongoDB's field?

I have a schema in which in one of the field i wanted it to store array of values. The schema is given below:
const memberSchema=new mongoose.Schema({
id:{
type:String,
unique:true
},
prefCurrency:{
type:String,
default:'AUD',
required:false
},
});
In the front end part, The user will select multiple currencies and which can be stored in the prefCurrency field of schema. The front end code is given below:
export default function MemberInformation() {
const { t } = useTranslation();
const[memberData,setMemberData]=useState([]);
const [member,setMember]=useState({id:"",prefCurrency:""})
var name,valueV;
const handleInputs=e=>{
console.log("Updated ",member)
name=e.target.name;
valueV=e.target.value;
setMember({...member,[name]:valueV})
}
const postData= ()=>{
setMemberData({...memberData,...member})
const {id,prefCurrency}=member;
var UpdatedMemInfo ={id,prefCurrency};
axios.put('/memberInfoUpdate', UpdatedMemInfo)
.then( res => {
alert('Updated successfully!');
}
)
.catch(err => {
console.log(err.response);
alert('An error occurred! Try submitting the form again.');
});
}
useEffect(() => {
async function fetchBooks() {
const response = await fetch('/memberinfo');
const json = await response.json();
setMemberData(json.memberLogin);
setMember(json.memberLogin);
console.log(json.memberLogin)
}
fetchBooks();
},[]);
return (
<Form.Select aria-label="Floating label select example" name="prefCurrency" value={member.prefCurrency} onChange={e=>handleInputs(e)}>
<span><ReactCountryFlag countryCode="AU" svg style={myStyle}/></span>
<option value="AUD" name="prefCurrency">AUD</option>
<option value="CAD" name="prefCurrency">CAD</option>
<option value="CHF" name="prefCurrency">CHF</option>
<option value="CNY" name="prefCurrency">CNY</option>
</Form.Select>
<Button variant="success" onClick={()=>postData()}>
Save Changes
</Button>
)
}
As in the above code, only one value can be selected and stored into the MongoDB but i want select multiple values and store in the form of array in the prefCurrency field of the schema and then retrieve it from the database to display it. What will be the code changes here?
The API for posting the above data in database is given below:
router.put('/memberInfoUpdate', async (req, res) => {
const {id,prefCurrency}=req.body;
var _id = req.body.id;
var UpdatedMemInfo = {
id:id,
prefCurrency:prefCurrency
};
Member.findOneAndUpdate(_id, UpdatedMemInfo, { new: true }, function(
err,
UpdatedMemInfo
) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
res.send(UpdatedMemInfo);
}
});
});
The above update API is just for one value in the prefCurrency field but i want to have multiple selected values in it.
The prefCurrency Schema would look like this for storing array values.
const memberSchema=new mongoose.Schema({
id:{
type:String,
unique:true
},
prefCurrency:{
type:Array,
default:'AUD',
required:false
},
});
And the query for updating the prefCurrency is
var UpdatedMemInfo = {
id:id,
prefCurrency:[prefCurrency]
};
This will take array of values and update it.

fetching data from API in react poll

I want to fetch data from API and show frontend using react but I am getting error from frontend side which is (TypeError: answers.map is not a function ) so how can I solve this error --
MY CODE IS -
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
import { getPolls } from "../helper/coreapicalls";
const MainPoll = () => {
const [polls, setPoll] = useState([]);
const [error, seterror] = useState(false);
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([]);
useEffect(() => {
loadPoll();
}, []);
const loadPoll = () => {
getPolls().then((data) => {
if (data.error) {
seterror(data.error);
} else {
setPoll(data);
console.log(data);
}
});
};
// Handling user vote
// Increments the votes count of answer when the user votes
const handalchange = () => {
console.log("hello");
};
return (
<div className="">
<div className="container">
<h1 className="blog_heading">Poll's of the Day</h1>
<div className="row">
{polls.map((poll, index) => (
<div className="col-lg-4 col-12" key={index}>
<Poll
question={poll.question}
answers={poll.options.none}
onVote={handalchange}
/>
</div>
))}
</div>
</div>
</div>
);
};
export default MainPoll;
Data which I am getting from API is-
Here I have Question , 3 options how can I show to frontend
Error -
There is two little mistakes in the code that you show us:
the first One you imported import Polls from "./polls"; and you call <Poll noStorage question={poll.question} answers={poll.options} onVote={handleVote}/> just change Poll by Polls.
const [pollAnswers, setPollAnswers] = useState([...answers]); this didn't work because you need to pass a initial value for your state and answer is not yet initialize and accessible. just change useState([...answers]); by useState([]);
UPDATE:
you need to pass an array to answers props .
We can see in your console screenshot that the array of options has "none" as key so
try this : <Poll noStorage question={poll.question} answers={poll.options.none} onVote={handleVote}/> ("none" is a strange key...)
UPDATE
Your data object is not well formated to fit react-polls answers props.
in the npmjs doc of react-polls we can see an example of options and it's an array of object like this:
[
{ option: 'Yes', votes: 8 },
{ option: 'No', votes: 2 }
]
so based on the data console log that you add in your question it should looks like this:
[
{
createdAt: "2020-12-01T21:43:23:21.061Z",
options: {
none: [ { option: 'Yes', votes: 8 },
{ option: 'No', votes: 2 }],
student: ["12345678978945"],
teacher: ["7894567894231"]
},
question: "Are you student ot teacher",
updatedAt: "2020-12-01T21:43:23:21.061Z"
}
]
see a sandBox here working with your code (except getPolls()).
I think the issue come from the API.

Error: values.map is not a function ( why I get this error )

i am fetching details from database through Id wanted to display that into the table, but for initial purpose i wanted to just display on browser without table and stuff.. am getting values.map is not a function but when I did console.log(values), got {title: "", description: "", tags: "", photo: "", loading: false, …}
createdBlog: ""
description: ""
error: ""
formData: ""
getaRedirect: false
loading: false
photo: ""
tags: ""
title: ""
proto: Object
what to do, please help
import React, { useState, useEffect } from "react";
import "../../styles.css";
import { getoneBlog } from "../helper/coreapicalls";
import ImageHelper from "../helper/ImageHelper";
const Fullblog = ({ match }) => {
const [values, setValues] = useState({
title: "",
description: "",
tags: "",
photo: "",
loading: false,
error: "",
createdBlog: "",
getaRedirect: false,
formData: "",
});
const {
title,
description,
tags,
loading,
error,
createdBlog,
getaRedirect,
formData,
} = values;
const preload = (blogId) => {
getoneBlog(blogId).then((data) => {
//console.log(data);
if (data.error) {
setValues({ ...values, error: data.error });
} else {
// preloadCategories();
setValues({
...values,
title: data.title,
description: data.description,
tags: data.tags,
formData: new FormData(),
});
}
});
};
console.log(values);
useEffect(() => {
preload(match.params.blogId);
}, []);
return (
<div>
<div className="py-md-5 py-3">
<div className="Fullblog ">
{values.map((fullblog, index) => {
return (
<div>
<h1 className="FullblogTittle">
Founder Leandra Medine Cohen announced the news to her
employees on a Zoom call earlier this week.
{fullblog.title}
</h1>
<p className="tags">tags </p>
<img
src="https://cdn.pixabay.com/photo/2020/10/17/17/41/girl-5662873_960_720.jpg"
className="FullblogImg"
alt="img"
/>
<ImageHelper />
<p className="description">
CULTURE How to Celebrate Halloween at Home This Year From
horror movie marathons to Halloween-themed drive-in features
to virtual pumpkin carving parties, here's how to celebrate
Halloween safely this year. By Pahull Bains Date October 22,
2020 With cases on the rise in certain regions of Ontario ’s A
Little Blurry. The livestream will be viewable on demand for
24 hours for ticket holders. Get your tickets here.
</p>
</div>
);
})}
</div>
</div>
</div>
);
};
export default Fullblog;
coreapicalls code-
// get all Blogs
export const getBlogs = () => {
return fetch(`${API}/blogs`, {
method: "GET",
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
//get a Blog
export const getoneBlog = (blogId) => {
return fetch(`${API}blog/${blogId}`, {
method: "GET",
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
You can use .map() function on an Array but your state looks to be set up as an Object rather than an array.
Check these lines:
{values.map((fullblog, index) => {
&
const [values, setValues] = useState({ ... }); // This is an object
Reference
The map() method creates a new array populated with the results of
calling a provided function on every element in the calling array.
So the issue is, you are trying the map() on an object that's why it is showing you the error.
You can only use .map() on Array variables, as mentioned before.
You can simply do this:
Object.keys(values), which will make an Array with the keys of your object.
Object.keys(values).map( keys => console.log(values[key]) )
You used object destructuring on values but there is no such method as Object.prototype.map...
You can loop over Object.values(values) with map or Object.keys(values) if you want to loop over the keys.
Code:
Object.values(values).map((fullblog, index) => {
// insert code here
});

How to get around the "Missing field" warning when executing a mutation with an optimiticResponse in the apollo-client?

I'm using aws-appsync with the apollo-client and when I try to execute a mutation without providing all fields I get a warning like "Missing field x in {...}". Do I really need to provide all (including optional) fields? How can I handle this gracefully?
I wonder if this is the expected behaviour or wether I'm missing something obvious. I don't want to maintain the added complexity of having to pass all optional fields and having those fields stored in the database as null values.
I figured since they are just warnings I 'll just ignore them but I found that the updates would be executed in the database, but then the inmemorycache cache would not always update. It would sometimes show the update and other times not.
import {compose, graphql} from "react-apollo";
import gql from "graphql-tag";
import React from "react";
export const EditCard = (props) => {
const handleSave = () => {
props.update({
givenName :'someGivenName',
//middleName omitted on purpose
familyName :'someFamilyName',
});
};
return (
<>...more stuff here...</>
);
};
export const card = gql`
fragment card on Identity{
givenName
middleName
familyName
}
`;
export const CardsGraphQL = gql`
query GerCards {
cards: listIdentitys(filter: {type: {eq: "CARD"}}) {
items {
...card
}
}
}
${card}
`;
export const UpdateCardGraphQL = gql`
mutation UpdateCard($input: UpdateIdentityInput!) {
updateObject: updateIdentity(input: $input) {
...card
}
}
${card}
`;
export const selectConfig = () => {
return {
options: {
fetchPolicy: 'cache-and-network',
},
props: (props) => {
return {
cards: props.data.cards ? props.data.cards.items : [],
};
},
};
};
export const updateConfig = (query) => {
return {
options: {
update: (cache, {data: {updateObject}}) => {
// Read query from cache
const data = cache.readQuery({query});
// Add updated object to the cache
data.cards.items = data.cards.items.map(item => item.id === updateObject.id ? updateObject : item);
//Overwrite the cache with the new results
cache.writeQuery({query, data});
},
},
props: (props) => {
return {
update: (input) => {
props.mutate({
variables: {input},
optimisticResponse: () => ({
updateObject: input,
}),
});
},
};
},
};
};
export default compose(
graphql(CardsGraphQL, selectConfig),
graphql(UpdateCardGraphQL, updateConfig(CardsGraphQL)))
(EditCard);
For GraphQL this mutation seems to run without problems and the result in the dynamoDB is what I expect:
{
givenName :'someGivenName',
familyName :'someFamilyName'
}
However the cache is not always updated with the mutation result and the apollo-client shows the warning:
"Missing field middleName in {..."
If I add the middleName field, the warning goes away and the cache updates correctly but the result in the dynamoDB is:
{
givenName :'someGivenName',
middleName : null,
familyName :'someFamilyName'
}
This approach results in additional complexity in my client that I would like to avoid maintaining.
Does anyone else have this problem? How to solve this gracefully?
Any help is appreciated.

How to query a single document from a Mongodb collection with react

I'm trying to build a search bar with a react frontend and node backend, that will let me search a customer ID from a mongoDB collection, then pull all of the data from a single document down from within the collection and display it on my react app.
Currently, I am just trying to get to get the single document bit to work, if this is possible. At the moment, it pulls down the entire collection.
My current Node code:
Search router
const express = require('express');
const app = express();
const tfiPaintCodesRouter = express.Router();
const PaintInfoSchema = require('../models/PaintInfoSchema.js');
tfiPaintCodesRouter.route('/get').get(function (req, res) {
const tfipaintcode = new PaintInfoSchema(req.body);
console.log(req.body)
tfipaintcode.save()
.then(tfipaintcode => {
res.json('Got data!!');
})
.catch(err => {
res.status(400).send("unable to get data");
console.log('CustomerID is required', err.res);
});
});
tfiPaintCodesRouter.route('/').get(function (req, res) {
PaintInfoSchema.find(function (err, tfipaintcodes){
if(err){
console.log('this is an error!', err.res);
}
else {
res.json(tfipaintcodes);
}
});
});
module.exports = tfiPaintCodesRouter;
Mongo schema using mongoose.
const mongoose = require('mongoose')
var uniqueValidator = require('mongoose-unique-validator');
const Schema = mongoose.Schema;
// Create schema
const PaintInfoSchema = new Schema({
customerID: {
required: true,
index: true,
unique: true,
type: String
},
companyName: {
index: true,
type: String
},
curtainCodes: {
index: true,
type: String
},
sinageCodes: {
index: true,
type: String
},
Notes: {
index: true,
type: String
},
Method: {
index: true,
type: String
},
},{
collection: 'tfiPaintCodes'
});
PaintInfoSchema.plugin(uniqueValidator);
module.exports = mongoose.model('PaintInfoSchema', PaintInfoSchema)
My current react code is:
import React from 'react';
import { Form, FormGroup, Input, Container, Row, Col } from 'reactstrap';
import './Search.css'
import axios from 'axios'
class Search extends React.Component {
constructor(props) {
super(props)
this.state = {
searchInfo: []
};
}
handleInputChange = (event) => {
event.preventDefault();
const { value } = event.target;
console.log('Value', value)
this.setState({
query: value
});
this.search(value);
};
search = query => {
axios.get('http://localhost:3001/getData')
.then(res =>{
const searchInfo = (res.data || []).map(obj => ({
company: obj.companyName,
sinage: obj.sinageCodes,
method: obj.Method,
notes: obj.Notes}));
this.setState({ searchInfo });
})
};
componentDidMount() {
this.search("");
}
render() {
return(
<Container>
<Form>
<Row>
<Col md={{ size: 6 ,offset: 3}}>
<FormGroup className="SearchBar">
<Input onChange={this.handleInputChange} type="search" name="search" id="exampleSearch" placeholder="search" />
</FormGroup>
</Col>
</Row>
</Form>
<ul>
{this.state.searchInfo.map(function(searchInfo, index){
return (
<div key={index}>
<h1>NAME: {searchInfo.company}</h1>
<p>{searchInfo.sinage}</p>
<p>{searchInfo.method}</p>
<p>{searchInfo.notes}</p>
</div>
)
}
)}
</ul>
</Container>
);
}
}
export default Search
The code above queries mongodb, then pulls down all of the data stored in my collection, here is an image of the returned data.
Data displayed in frontend
But i want to know if it is possible to just pull down one document in that collection, so it would just display one Name: and then the other 4 bits of data.
I have the data stored in Mlab, here is a screenshot of the documents stored in my collection.
data in mongodb
Is this possible? Thanks!
The best way is to pull only one document from the DB (if you don't need more in your case).
Mongoose, as any other ORM/ODM, gives you those options:
https://mongoosejs.com/docs/api.html#model_Model.findOne
With FindOne you can search for documents but get only one (aka. "the first found") document back.
If you need a fixed number of returned documents, you can use limit(10) to, for example, return only 10 documents.
Though it appears to me that your code-snippets don't show the exact segment where do the query in Mongoose, otherwise we could have shown you what to do in your own example.

Resources