I am trying to make a relationship between the post and the user/author but I'm struggling to populate the posts with the author and display the author of the post. I can create posts with various fields but how would I populate the author (username) in the post with node and display it in react?
Part of Post schema
const {ObjectId} = mongoose.Schema.Types
...
author: {
id: {
type: ObjectId,
ref: "User"
},
username: String
},
controllers/posts.js
exports.create = (req, res) => {
const {title, body, date } = req.body
const post = new Post({title, body, date, author: ........ })
post.save()
.then(response => {res.send(response)})
.catch(err => {
res.send(err)
})
}
exports.list = (req, res) => {
Post.find()
.populate("author", "_id name")
.select("-photo")
.limit(5)
.exec((err, posts) => {
if (err) {
res.send(err);
}
res.send(posts)
})
}
components/CreatePost.js
class CreatePost extends React.Component {
constructor(props) {
super(props)
this.state = {
title: '',
body: ''
}
}
changeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
submitHandler = e => {
e.preventDefault()
axios({ url: `${API}/new-post`, method: 'POST', data: this.state})
.then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
render() {
const {title, body} = this.state
return (
<div>
<form onSubmit={this.submitHandler}>
<input type="text" name="title"
onChange={this.changeHandler} value={title} />
<input type="text" name="body"
onChange={this.changeHandler} value={body}/>
<button type="submit">Submit</button>
</form>
</div>
)
}
}
export default CreatePost
You are not populating the author in your create controller. Consider the following:
exports.create = (req, res) => {
const {title, body, date } = req.body
const post = new Post({title, body, date, author: ........ }).populate('author', '_id name')
As you do in the .list controller.
Related
Details about the questions: How can I increase & decrease product quantity in the same route in node MongoDB? I did Increase quantity. But I want to decrease 1 quantity of the Delivered button.
// server
app.put("/product/:id", async (req, res) => {
const id = req.params.id;
const updateQuantity = req.body;
console.log(updateQuantity.quantity);
const filter = { _id: ObjectId(id) };
const option = { upsert: true };
const updateDoc = {
$inc: {
quantity: parseInt(updateQuantity.quantity),
},
};
const result = await productCollection.updateOne(
filter,
updateDoc,
option
);
res.send(result);
});
//client
const onSubmit = (data) => {
const url = `http://localhost:5000/product/${id}`;
fetch(url, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data =>{
toast('Quantity Updated')
})
.catch((error) => {
console.error("Error:", error);
});
};
return(
<div>
<button>Delivered</button>
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="number"
{...register("quantity")}
placeholder="Number of Quantity"
/>
<input
type="submit"
value="Add Product"
/>
</form>
</div>
</div>
);
I was struggling for several months to implement Cloudinary into a MERN web app, so I am running out of options to find the answer.
Basically, the problem here is that I don't have the knowledge to write successfully the necessary code to make it work.
Here is some insight:
I need to upload photos to Cloudinary for a Blog web page, (the problem is in the first two code files)
I was able to make it work for user's profilePics in the last two code files ( Settings.jsx & Settings.js ).. now I had to do almost the same,but that works for images in posts (Write.jsx & Posts.js ).
Feel free to ask for more info about
Front-End Write.jsx
import { useContext, useState } from "react";
import "./write.css";
import { Context } from "../../context/Context";
import { axiosInstance } from "../../config";
import { FcAddImage } from "react-icons/fc";
export default function Write() {
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [file, setFile] = useState(null);
const [success, setSuccess] = useState(false);
const { user, dispatch } = useContext(Context);
const photo = user.photo;
console.log("post info", title, desc, file, user);
const handleSubmit = async (e) => {
e.preventDefault();
if (file) {
const data = new FormData();
data.append("id", user._id);
data.append("type", "file");
data.append("avatar", file);
try {
const res = await axiosInstance.post("/upload", "/posts", data);
setSuccess(true);
dispatch({ type: "UPDATE_SUCCESS", payload: res.data });
} catch (err) {
console.log(err);
dispatch({ type: "UPDATE_FAILURE" });
}
}
};
/*try {
await axiosInstance.post("/upload", data);
} catch (err) {}
}
try {
const res = await axiosInstance.post("/posts", newPost);
window.location.replace("/post/" + res.data._id);
} catch (err) {}*/
return (
<div className="write">
{file && (
<img
className="writeImg"
src={file ? URL.createObjectURL(file) : photo}
alt=""
/>
)}
<form className="writeForm" onSubmit={handleSubmit}>
<div className="writeFormGroup">
<label htmlFor="fileInput">
Image
<FcAddImage className="icon" />
</label>
<input
action="/:id"
method="POST"
type="file"
id="fileInput"
style={{ display: "none" }}
onChange={(e) => setFile(e.target.files[0])}
/>
<input
type="text"
placeholder="Title"
className="writeInput"
autoFocus={true}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className="writeFormGroup">
<textarea
placeholder="Tell your story..."
type="text"
className="writeInput writeText"
onChange={(e) => setDesc(e.target.value)}
></textarea>
</div>
<button className="writeSubmit" type="submit">
Publish
</button>
{success && (
<span
style={{ color: "green", textAlign: "center", marginTop: "20px" }}
>
Post has been uploaded...
</span>
)}
</form>
</div>
);
}
Server-side Post.js
const User = require("../models/User");
const Post = require("../models/Post");
const cloudinary = require("../utils/cloudinary");
const upload = require("../utils/multer");
//UPLOAD FILE
router.post("/upload", upload.single("avatar"), async (req, res) => {
try {
// Upload image to cloudinary
const result = await cloudinary.uploader.upload(req.file.path);
//save changes
post.photo = result.secure_url;
res.status(200).json(savedPost);
} catch (err) {
res.status(500).json(err);
}
});
//CREATE POST
router.post("/", async (req, res) => {
const newPost = new Post(req.body);
try {
const savedPost = await newPost.save();
res.status(200).json(savedPost);
} catch (err) {
res.status(500).json(err);
}
});
//UPDATE POST
router.put("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (post.username === req.body.username) {
try {
const updatedPost = await Post.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
{ new: true }
);
res.status(200).json(updatedPost);
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(401).json("You can update only your post!");
}
} catch (err) {
res.status(500).json(err);
}
});
//DELETE POST
router.delete("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (post.username === req.body.username) {
try {
await post.delete();
res.status(200).json("Post has been deleted...");
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(401).json("You can delete only your post!");
}
} catch (err) {
res.status(500).json(err);
}
});
//GET POST
router.get("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
res.status(200).json(post);
} catch (err) {
res.status(500).json(err);
}
});
//GET ALL POSTS
router.get("/", async (req, res) => {
const username = req.query.user;
const catName = req.query.cat;
try {
let posts;
if (username) {
posts = await Post.find({ username });
} else if (catName) {
posts = await Post.find({
categories: {
$in: [catName],
},
});
} else {
posts = await Post.find();
}
res.status(200).json(posts);
} catch (err) {
res.status(500).json(err);
}
});
module.exports = router;
Server-side Settings.js
//POST IMAGES
router.post("/upload", upload.single("avatar"), async (req, res) => {
console.log('/users/upload triggered')
try {
// Upload image to cloudinary
const result = await cloudinary.uploader.upload(req.file.path);
// Save user
User.findById(req.body.id, (err, user) => {
if (err) console.log(err)
user.profilePic = result.secure_url
user.save((err) => {
if (err) console.log(err)
res.json(user);
console.log('user saved')
})
});
} catch (err) {
console.log(err);
res.status(500).json(err);
}
});
I made conection to cloudinary changing the line
const res = await axiosInstance.post("/posts/upload", data);
but now, I can't post the 'posts', but is a step in the right direction
const handleSubmit = async (e) => {
e.preventDefault();
if (file) {
const data = new FormData();
data.append("id", user._id);
data.append("type", "file");
data.append("avatar", file);
try {
const res = await axiosInstance.post("/posts/upload", data);
setSuccess(true);
dispatch({ type: "UPDATE_SUCCESS", payload: res.data });
} catch (err) {
console.log(err);
dispatch({ type: "UPDATE_FAILURE" });
}
}
};
I set up a proxy to bypass CORS for the intended api in this react application. I'm having trouble to pass data from react component to proxy(nodeJS server). I've read a few links such as here and here but still have no clues.
/*React component*/
import React, { useState } from "react";
import axios from "axios";
export default function Mail() {
const [emailInput, setEmailInput] = useState('')
const getMail = () => {
axios.get("/list/members").then(json => {
console.log(json.data);
});
};
const subscribe = (email) => {
console.log('inside subscribe')
axios.post("/member", email)
.then(data => console.log('post succeeds'))
.catch(err => console.log(err))
}
const handleSubmit = e => {
e.preventDefault();
const email = {
email_address: `${emailInput}`,
status: "subscribed"
};
subscribe(email)
};
const handleChange = (e) => {
setEmailInput(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="email" placeholder="enter your email" value={emailInput} onChange={handleChange}/>
<input type="submit" value="subscribe" />{" "}
</form>
);
}
In node server, I have
app.post("/member", (req, res) => {
const email = {
email_address: "axios1234#gmail.com",
status: "subscribed"
};
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});
I've checked that my conduit between react front end app and proxy server is working. I also examined both req and res in app.post("/member", (req, res) but found thatreq.body is undefined and couldn't find the email object that was passed in from react function component. Did I miss anything here?
Are you using a body-parser? If not, install body-parser and then change your code to this:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post("/member", (req, res) => {
const email = req.body.email_address;
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});
I have this ejs template that inputs the name of the city, the city gets stored into the database and then display the related weather result. I created a controller to POST the city from that input box. The city name gets stored easily and gets a success message but it does not pass that city to the GET request into the weather API URL to display the related weather details.
Here is my controller for city:
const mongoose = require('mongoose');
const axios = require('axios');
const City = require('../models/city');
exports.addCity = (req, res, next) => {
const city = new City({
_id: new mongoose.Types.ObjectId(),
cityName: req.body.cityName
});
city
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Created city successfully"
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.getCity = (req, res, next) => {
City.find({}).then(docs => {
cities: docs.map(doc => {
return {
city: doc.cityName
}
})
let apiKey = '**************************';
var city = cities;
var url= `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`;
axios(url)
.then( (response) => {
var cityData = response.data;
var weather = {
city: city,
temperature: Math.round(cityData.main.temp),
description: cityData.weather[0].description,
icon: cityData.weather[0].icon
}
// var weather_data = { weather : weather };
console.log('heyyyy', weather);
res.render('index', {
weather: weather
});
})
.catch( (error) => {
console.log(error);
})
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
})
}
And here is the snippet of the ejs template:
<article class="media">
<div class="media-left">
<figure class="image is-50x50">
<img src="http://openweathermap.org/img/w/<%= weather.icon %>.png" alt="Image">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<span class="title"><%= weather.city %></span>
<br>
<span class="subtitle"><%= weather.temperature %></span>
<br> <%= weather.description %>
</p>
</div>
</div>
</article>
Whenever I run my localhost, it only creates the city into the database and display console with the lots of error data back with last two lines saying:
data: { cod: '404', message: 'city not found' } } }
{ _id: 5c6d4e18d1ad342458c3df64, cityName: 'mumbai', __v: 0 }
Kindly help to figure out the problem.
Looks like the getCity controller you provided has some syntax errors but I tried my best to work with it. Main changes are 1. the logic to find matching document and 2. the way you construct the GET query using Axios
exports.getCity = (req, res, next) => {
const cityName = req.query.cityName;
City.find({})
.then(docs => {
// 1. Find matching city document
const city = docs.find(doc => {
return doc.cityName === cityName;
});
if (city) {
const apiKey = "**********";
const url = "https://api.openweathermap.org/data/2.5/weather";
// 2. Axios GET query with params as object instead of interpolating inside url
axios
.get(url, {
params: {
q: city.cityName,
appId: apiKey
}
})
.then(response => {
const cityData = response.data;
const weather = {
city: city,
temperature: Math.round(cityData.main.temp),
description: cityData.weather[0].description,
icon: cityData.weather[0].icon
};
// Do something with weather
console.log(weather);
})
.catch(err => {
// Weather query failed
console.log(err);
});
} else {
// No city found matching cityName
}
})
.catch(err => {
// Database fetch failed
console.log(err);
});
};
I'm building a fullstack React + NodeJS to-do list app following a tutorial. I've created a React Task component that displays info for a particular task and provides update/delete buttons. For some reason, my React updateTask and deleteTask functions update/delete the oldest task regardless of which task I've selected to change.
I'm using the update state variable in React to toggle the update form.
Also, logging res in the updateTask function returns success and the correct id to update. However, this is not the id that actually gets updated.
React Task component (Task.js):
import React, { Component } from "react";
import axios from "axios";
class Task extends Component {
constructor({_id, title, due, desc}) {
super();
this.state = {
id: _id,
title: title,
due: due,
desc: desc,
update: false
}
this.updateTask = this.updateTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
updateTask = () => {
const {id, title, due, desc} = this.state;
axios.post("/tasks/update", {
id: id,
update: {
title: title,
due: due,
desc: desc
}
})
.then(
res => {
console.log(res);
console.log(res.data);
}
);
};
deleteTask = () => {
const {id} = this.state;
axios.delete("/tasks/delete", {
id: id
});
};
render() {
const {id, title, due, desc, update} = this.state;
return (
<div className="task">
<div>{id}</div>
{update ?
<div className="task-update">
<input
type="text"
onChange={e => this.setState({ title: e.target.value })}
value={title}
/>
<input
type="date"
onChange={e => this.setState({ due: e.target.value })}
value={!due ? "" : due.slice(0,10)}
/>
<textarea
onChange={e => this.setState({ desc: e.target.value })}
value={!desc ? "" : desc}
/>
<button onClick={this.updateTask}>
Update Task
</button>
</div>
:
<div className="task-show">
<div className="task-info">
<div>{title}</div>
<div>{!due ? "no date" : due.slice(0,10)}</div>
<p>{desc}</p>
</div>
<div className="task-btns">
<button
className="task-edit"
onClick={e => this.setState({update: true})}
>
</button>
<button
className="task-done"
onClick={() => this.doneTask()}
></button>
<button
className="task-del"
onClick={this.deleteTask}
>
</button>
</div>
</div>
}
</div>
);
}
}
export default Task;
NodeJS task controller:
var express = require('express');
var router = express.Router();
const Task = require("../models/task");
const Data = require("../models/data");
router.get("/all", (req, res) => {
Task.find((err, tasks) => {
if (err) return res.json({ success: false, error: err });
return res.json({ success: true, tasks: tasks });
});
});
router.post("/update", (req, res) => {
const { id, update } = req.body;
Task.findOneAndUpdate(id, update, err => {
if (err) return res.json({ success: false, error: err });
return res.json({ success: true, id: id});
});
});
router.delete("/delete", (req, res) => {
const { id } = req.body;
Task.findOneAndDelete(id, err => {
if (err) return res.send(err);
return res.json({ success: true });
});
});
router.post("/create", (req, res) => {
let task = new Task();
const {title, due, desc} = req.body;
if (!title) {
return res.json({
success: false,
error: "INVALID INPUTS"
});
}
task.title = title;
task.due = due;
task.desc = desc;
task.save(err => {
if (err) return res.json({ success: false, error: err });
return res.json({ success: true });
});
});
module.exports = router;
Thank you for the help in advance!
You should use Task.findOneAndUpdate({_id: id}, ...). If you don't use valid query like {_id: id}, it always returns the first object as far as I know.
Mogoose findOneAndUpdate accepts query as first parameter.More info here.
Try to update this.state.id onclick of delete task or pass in e.target.value to delete function and then pick the Id to the api call