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);
});
};
Related
My PostSchema is like this:
let PostSchema = new mongoose.Schema({
content: String,
time: Date,
likes: Number,
image: String,
tag: String
});
let Post = mongoose.model("Post", PostSchema);
module.exports = Post;
And I'd like to get a list of all the posts with the same tag
Here is my code to get it rendered. It is in the folder routes/post
router.get("/tag", function(req, res) {
Post.find({ subreddit: req.params.tag }).lean()
.then(posts => {
res.render("posts/index", { posts });
})
.catch(err => {
console.log(err);
});
});
Of course I have the views/posts folder with index.ejx to route the view
<li class="list-group-item">
<div class="text-right">
<span>Tags: </span><%= post.tag %>
</div>
</li>
I don't really understand how or where I got it wrong. I tried to follow the instruction of the tutorial here: https://www.makeschool.com/academy/track/standalone/reddit-clone-in-node-js/create-subreddits
Maybe try to trim the value you search:
router.get("/tag", function(req, res) {
let tag = req.params.tag;
if (!tag) {
throw new Error('No tag was found');
}
tag = tag.trim();
Post.find({ subreddit: req.params.tag }).lean()
.then(posts => {
res.render("posts/index", { posts });
})
.catch(err => {
console.log(err);
});
});
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.
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
I am using Angular 5 on front-end, Node on back-end and Mongo as the database. Now I am trying to save an image to the database but constantly getting this error. I can't figure out if I'm making mistake on front or back because this is my first time working with files. I did my research but it points mostly to angular 1.x.
HTML Component
<form [formGroup]="form" (ngSubmit)="onSubmitPhoto()">
<div class="form-group">
<input type="file" class="form-control" formControlName="photo">
</div>
<button class="btn btn-default" type="submit">Sačuvaj</button>
</form>
TS Component
onSubmitPhoto() {
this.profile.photo = this.form.value.photo;
this.usersService.updatePhoto(this.profile, this.id)
.subscribe(
data => {
this.router.navigateByUrl('/');
},
error => console.error(error)
);
}
Service
updatePhoto(profile: Profile, id: string) {
const body = new FormData();
body.append('photo', profile.photo);
const headers = new Headers({ 'Content-Type': 'application/json' });
return this.http.post('http://localhost:3000/profile/photo/' + id, body, { headers: headers })
.map((response: Response) => response.json())
.catch((error: Response) => {
return Observable.throw(error.json());
});
}
Node.JS
router.post('/photo/:id', (req, res) => {
console.log(req.files);
User.find({ _id: req.params.id })
.exec((err, user) => {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
user.img.data = fs.readFileSync(req.files);
user.img.contentType = 'image/png';
user.save((err, obj) => {
if (err) {
throw err
}
console.log('success')
})
});
});
Model
const schema = new Schema({
img: { data: Buffer, contentType: String}
});
module.exports = mongoose.model('User', schema);
Any help is appreciated.
Also, loging req.files returns undefined.
To upload a file you need to wrapp it in a FormData instance as follows:
interface Profile {
photo: File;
}
updatePhoto(profile: Profile, id: string) {
const body = new FormData();
body.append('photo',profile.photo);
return this.http.post(`http://localhost:3000/profile/photo/${id}`, body,)
.map((response: Response) => response.json())
.catch((error: Response) => {
return Observable.throw(error.json());
});
}
Furthermore, your backend is most likely failing in the following section:
user.img.data = fs.readFileSync(req.body.photo);
Considering that you are now uploading a form with multipart/form-data encoding, you will need to use some middleware to parse the request in your backend as stated in the expressjs doc
You could use multer or express-fileupload
If you go with the second, you will need the following:
const fileUpload = require('express-fileupload');
router.use(fileUpload());// use express-fileupload as default parser for multipart/form-data encoding
router.post('/photo/:id', (req, res) => {
User.find({ _id: req.params.id })
.exec((err, user) => {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
user.img.data = req.files.photo.data;
user.img.contentType = 'image/png';
user.save((err, obj) => {
if (err) {
throw err
}
console.log('success')
})
});
});
I am using Angular 5 on front-end, Node on back-end and Mongo as the database. Now I am trying to save an image to the database but constantly getting this error. I can't figure out if I'm making mistake on front or back because this is my first time working with files. I did my research but it points mostly to angular 1.x.
HTML Component
<form [formGroup]="form" (ngSubmit)="onSubmitPhoto()">
<div class="form-group">
<input type="file" class="form-control" formControlName="photo">
</div>
<button class="btn btn-default" type="submit">Sačuvaj</button>
</form>
TS Component
onSubmitPhoto() {
this.profile.photo = this.form.value.photo;
this.usersService.updatePhoto(this.profile, this.id)
.subscribe(
data => {
this.router.navigateByUrl('/');
},
error => console.error(error)
);
}
Service
updatePhoto(profile: Profile, id: string) {
const body = new FormData();
body.append('photo', profile.photo);
const headers = new Headers({ 'Content-Type': 'application/json' });
return this.http.post('http://localhost:3000/profile/photo/' + id, body, { headers: headers })
.map((response: Response) => response.json())
.catch((error: Response) => {
return Observable.throw(error.json());
});
}
Node.JS
router.post('/photo/:id', (req, res) => {
console.log(req.files);
User.find({ _id: req.params.id })
.exec((err, user) => {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
user.img.data = fs.readFileSync(req.files);
user.img.contentType = 'image/png';
user.save((err, obj) => {
if (err) {
throw err
}
console.log('success')
})
});
});
Model
const schema = new Schema({
img: { data: Buffer, contentType: String}
});
module.exports = mongoose.model('User', schema);
Any help is appreciated.
Also, loging req.files returns undefined.
To upload a file you need to wrapp it in a FormData instance as follows:
interface Profile {
photo: File;
}
updatePhoto(profile: Profile, id: string) {
const body = new FormData();
body.append('photo',profile.photo);
return this.http.post(`http://localhost:3000/profile/photo/${id}`, body,)
.map((response: Response) => response.json())
.catch((error: Response) => {
return Observable.throw(error.json());
});
}
Furthermore, your backend is most likely failing in the following section:
user.img.data = fs.readFileSync(req.body.photo);
Considering that you are now uploading a form with multipart/form-data encoding, you will need to use some middleware to parse the request in your backend as stated in the expressjs doc
You could use multer or express-fileupload
If you go with the second, you will need the following:
const fileUpload = require('express-fileupload');
router.use(fileUpload());// use express-fileupload as default parser for multipart/form-data encoding
router.post('/photo/:id', (req, res) => {
User.find({ _id: req.params.id })
.exec((err, user) => {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
user.img.data = req.files.photo.data;
user.img.contentType = 'image/png';
user.save((err, obj) => {
if (err) {
throw err
}
console.log('success')
})
});
});