I was building a weather app using OpenWeather API. The API was fetched in Node, then data was passed to React front end, code as follows:
Node index.js
const express = require('express');
const cors = require('cors');
const app = express();
const axios = require('axios');
const dotenv = require('dotenv');
dotenv.config();
const url = `http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=${process.env.REACT_APP_WEATHER_API_KEY}`;
app.use(cors());
app.get('/', (req, res) => {
res.send('go to /weather to see weather')
});
app.get('/weather', (req, res) => {
axios.get(url)
.then(response => {res.json(response.data)})
.catch(error => {
console.log(error);
});
})
let port = process.env.PORT || 4000;
app.listen(port, () => {
console.log(`App running on port ${port} `);
});
The weather data can then be viewed in http://localhost:4000/weather. Then React is used to display the data. Assume there is a simple React component to accept weather input and update state:
React WeatherForm.js
import React from 'react';
class WeatherForm extends React.Component {
constructor(props) {
super(props);
this.state = {
country: '',
city: ''
}
}
updateLocation(e) {
this.setState({
country: e.target.value,
city: e.target.value
});
}
render() {
return (
<form>
<div className="field">
<label className="label">Country</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type country name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<label className="label">City</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type city name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<div className="control">
<input
type='submit'
value='Search' />
</div>
</div>
</form>
)
}
}
export default WeatherForm
Question: How can I pass the country and city user input from the React app form to the country and city in the url variable in this line in the Node code?
const url = `http://api.openweathermap.org/data/2.5/weather?q=city,country&APPID=${process.env.REACT_APP_WEATHER_API_KEY}`
UPDATE I have updated the WeatherForm component as follows:
import React from 'react';
import Axios from 'axios';
class WeatherForm extends React.Component {
constructor(props) {
super(props);
this.state = {
country: '',
city: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const url = 'http://localhost:4000/weather';
const location = {
country: this.state.country,
city: this.state.city
}
Axios.post(url, location).then((res) => {
// what should I do here?
}).catch((e) => {
console.log(e);
})
}
updateLocation(e) {
this.setState({
country: e.target.value,
city: e.target.value
});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<p className="title">Weather</p>
<p className="subtitle">Check weather by city and country</p>
<div className="field">
<label className="label">Country</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type country name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<label className="label">City</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type city name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<div className="control">
<input
type='submit'
value='Search' />
</div>
</div>
</form>
)
}
}
export default WeatherForm
and I got error: POST http://localhost:4000/weather 404 (Not Found)
You want to use http requests to send the data to your backend. You can either use the native window.fetch API to send the data via a post request, or you can use a third-party library (I recommend axios).
The recommended way to send a post request on form submit in react is to store the field data in state (use the onChange prop on the input fields to update the state whenever the input value changes), and then use a handler function that gets fired when the submit button is clicked (use the onClick prop for your button element).
The handler function should get the current state (the form input field data) and pass it into the post request as the body.
When your express API receives the request, it can parse the data, and then fire off it's own API request to the openWeather API with that data as the url parameters.
UPDATE:
Updating due to updated question.
You don't have a post route defined in your express API. Therefore it won't accept post requests at the /weather route. What you need to do is write a handler that accepts post requests:
app.post('/weather', (req, res, next) => {
let { country, city } = req.body.data;
// here you send a post request to the weather API url
// to retrieve the results, then send them back
// to your react app to display them
}
Related
Form.js
import "./form.css";
import React, {useEffect,useState} from "react";
import {addBeauty,deleteB} from "./beauty";
import Modal from "./Modal";
import axios from 'axios';
export default function CreateForm() {
const [Name, setName] = useState("");
const [Intro, setIntro] = useState("");
const [isOpen, setIsOpen] = useState();
const [backendData, setBackendData] = useState([{}]);
useEffect(()=>{
fetch("http://localhost:5000/api").then(
response=>response.json()
).then(
data=>{ setBackendData(data)}
)
},[]);
const handleSubmit = (event)=>{
event.preventDefault();
axios.post("http://localhost:5000/api",{
id: userList[userList.length - 1].id + 1, Name:Name, Introduction:Intro
}).then(res=>{
console.log(res.data);
})
}
return (
<div className="container">
<form className="add" onSubmit={handleSubmit} >
<h2>User</h2>
<label htmlFor= "name">Name</label>
<input type="text" value={Name}
onChange={(event) => {setName(event.target.value);}}/>
<label htmlFor= "Intro">Introduction</label>
<input type="text" value={Intro}
onChange={(event) => {setIntro(event.target.value);}}/>
<p></p>
<p></p>
<div className = "press">
<button id = "1" type="submit">
Add Beauty
</button>
<button id = "2"
onClick={clearForm}
>
Clear Form
</button>
</div>
</form>
<br></br>
<br></br>
<br></br>
<div className="display">
{(typeof userData.user1 === 'undefined')?(
<h1>Loading</h1>):(
backendData.user1.map((user,i)=>{
return (
<div>
<h1> {user.Name}</h1>
<button onClick={()=>{
setIsOpen(user.id);
}}>View in popup</button>
<Modal open={isOpen === user.id} onClose={()=>setIsOpen(undefined)}>
<h3> {User.Introduction}</h3>
</Modal>
</div>
);})
)}
</div>
</div>
);
}
Server.js
const express = require('express');
const app = express();
const cors=require("cors");
const corsOptions ={
origin:'*',
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200,
}
app.use(cors(corsOptions)) // Use this after the variable declaration
app.get("/api",(req,res)=> {
res.json({"user1":[
{
id: 1,
Name: "Isabella",
},
{
id:2,
Name: "Catalina
}
]})
});
app.listen(5000,()=>{
console.log("Server started on port 5000");
})
I create a from using react. And I try to send the formdata to backend and insert the formdata into the data stored at backend using axios.post. But it doesn't work. I know it's because I didn't add the prefix of backend data "user1" in axios.post. But I am not sure how to do that. Could anyone help me with this?
You have not created the route on the server correctly. You have opened a route for GETting "/api" but you need to open a route for POSTing
Replace this line:
app.get("/api",(req,res)=> {
with
app.post("/api",(req,res)=> {
Hi Here you need to create one route for post API as below
app.post("/api",(req,res)=> {
console.log(req.body) //here you got the requested data.
res.send("Success !");
});
I am trying to upload files to server using Angular and Node, using multer.
I have Todo Model as :
export class TodoModel {
todo_id !:number;
todo_title !:string;
todo_description !:string;
todo_status !:number;
todo_deleted_flag !:boolean;
todo_image !:Object;
}
todo.component.ts
title:string;
desc:string;
selected_image:File = null;
fileUploadListener(event){
//console.log(event)
//console.log(event.target.files[0])
this.selected_image = <File>event.target.files[0]
console.log(this.selected_image)
}
onSubmit(form:NgForm){
const fd = new FormData()
if(this.selected_image) {
fd.append('todo_image',this.selected_image,this.selected_image.name)
}
console.log(fd);
const todo_model : TodoModel = {
todo_id: null,
todo_title:this.title,
todo_description:this.desc,
todo_status:1,
todo_deleted_flag:false,
todo_image:null
}
console.log(fd);
this.todoAdd.emit(todoadded);
this.todoAdd_DB.emit(todo_model);
this.addTodo_DB(todo_model, fd)
form.resetForm();
}
addTodo_DB(todo_db: TodoModel, fileUpload:Object){
//const todo_db
return this.http.post<{message:any}>('http://localhost:3000/api/todos/post_all_todos_db', todo_db,fileUpload).subscribe(data => {
console.log(data.message);
console.log(todo_db);
})
}
todo.component.html
<div class="col-md-12">
<form (ngSubmit)="onSubmit(todoForm)" #todoForm="ngForm">
<div class="mb-3">
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title">
</div>
<div class="mb-3">
<label for="label" class="form-label">Description</label>
<textarea class="form-control" id="todo_description" [(ngModel)]="desc" name="desc"></textarea>
</div>
<div class="mb-3">
<label for="todo_image" class="form-label">Image</label>
<input type="file" class="form-control" id='todo_image' (change)="fileUploadListener($event)">
</div>
<button type="submit" class="btn btn-success">Add To Do</button>
</form>
</div>
</div>
And on Server Side, using Node Js and PgSQL :-
app.post('/api/todos/post_all_todos_db',upload_using_multer.single('todo_images') , (req, res, next) => {
// const todo_post = req.body;
const files = req.file;
console.log(files) // - ----------> This does NOT work
console.log(req.body) //------> this works
//PGSQL insert query here
res.status(201).json({
message:"Post Added Successfully"
})
})
While doing console.log() in Angular side, I am getting the form data, but, on Node Js side, I get it as null.
Almost every tutorial I see, uses only one file upload , and that too, try to submit the form using the Form's action. I dont want to do that, so I tried doing this.
I
i once had the same issue and solved it with formdata, my example uploads multiple files. here is an example:
Node.JS
const serverRoutes = (function () {
const express = require('express');
const router = express.Router();
const multer = require('multer');
const upload = multer();
router.post('/myresource', upload.any(), (req, res) => {
console.log(req.files);
});
return router;
});
on angular
export class DataService {
constructor(private http: HttpClient) { }
sendMyFiles(file): Observable<MyResponse> {
const formData = new FormData();
formData.append("file", file);
return this.http.post<MyResponse>(
`${environment.backendAPI}myresource`,
formData
);
}
}
I building a simple todo app using the MERN stack with router and able to get it going except the edit part. I have 3 routes, the "Todos" page where i use axios to get the data from my express server route. The "Add" page for create new todo and the "Edit" page for editing and deleting. Here's my todos page where each todo has a button that takes the id as a url parameter unto the Edit page.
That little pencil/paper icon on each todo is a button link that get's the ID on click. Here's my Edit page with the data.
The warning:
Here's my Todos page i'm using a custom hook to fetch the data from express server route:
import React from 'react';
import useGetAPI from '../custom_hooks/useGetAPI';
import Todo from './todo_item/Todo';
const Todos = () =>{
const data = useGetAPI('http://localhost:4000');
return (
<div className="page">
<div className="page-header">
<h1>Todo Lists</h1>
</div>
<div className="page-content">
<ul className="todo-list">
{
data.map((todo)=><Todo todo={todo} key={todo._id}/>)
}
</ul>
</div>
</div>
);
}
export default Todos;
Here's my custom hooks for fetching data - used in Todos.
import {useState,useEffect} from 'react';
import axios from 'axios';
const useGetAPI = (url)=>{
const [data,setData] = useState([]);
useEffect(()=>{
const fetchData = async ()=>{
const response = await axios.get(url);
const data = [...response.data];
const error = response.error;
if(error)
console.log(error)
else{
console.log(data);
setData(data);
}
};
fetchData();
},[url])
return data;
}
export default useGetAPI;
Here's my Edit Page
import React,{useState, useEffect, useContext, useCallback} from 'react';
import useGetApiWithParams from '../custom_hooks/useGetApiWithParams';
import {FaTelegramPlane} from 'react-icons/fa';
import axios from 'axios';
import { matchPath } from 'react-router'
const EditTodo = (props) =>{
const todoID = props.match.params.id;
const [todo,setTodo] = useState(null);
const responseData = useGetApiWithParams('http://localhost:4000/edit',todoID);
console.log(`Todo id: ${todoID}`);
/* Set default data from database */
useEffect(()=>{
setTodo(responseData);
},[responseData,setTodo]);
const [description,setDescription] = useState('');
const [responsible,setResponsible] = useState('');
const [priority,setPriority] = useState('');
const [completed,setCompleted] = useState(false);
const handleDescription = useCallback((e)=>{
setDescription(e.target.value);
},[setDescription]);
const handleResponsible = useCallback((e)=>{
setResponsible(e.target.value);
},[setResponsible]);
const handlePriority = useCallback((e)=>{
setPriority(e.target.value);
},[setPriority]);
const handleCompleted = useCallback((e)=>{
setCompleted(!completed);
},[completed,setCompleted])
const handleSubmit = useCallback((e)=>{
e.preventDefault();
console.log('Form submitted');
console.log(`Description ${description}`);
console.log(`Description ${responsible}`);
console.log(`Description ${priority}`);
console.log(`Description ${completed}`);
const updatedTodo = {
description,
responsible,
priority,
completed: false
}
axios.put(`http://localhost/4000/edit/${props.match.params.id}`, updatedTodo)
.then(res=>console.log(res.data))
.catch(function (error) {
console.log(error);
});
},[description,responsible,priority,completed,props.match.params.id]);
return (
<div className="page">
<div className="page-header">
<h1>Edit Todo</h1>
</div>
<div className="page-content">
<form id="edit-todo-form" className="todo-form" onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input id="description" type="text" className="form-control" onChange={handleDescription} value={responseData.description} />
</div>
<div className="form-group">
<label htmlFor="responsible">Responsible:</label>
<input id="responsible" type="text" className="form-control" onChange={handleResponsible} value={responseData.responsible} />
</div>
<div className="form-group">
<label htmlFor="priorities">Priorities:</label>
<div id="priorities" className="form-radios">
<label htmlFor="radio1" className="radio-label">
<input name="priorityOptions" type="radio" id="radio1" value={responseData.priority} checked={priority === 'Low'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Low</span>
</label>
<label htmlFor="radio2" className="radio-label">
<input type="radio" id="radio2" value={responseData.priority} checked={priority === 'Medium'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">Medium</span>
</label>
<label htmlFor="radio3" className="radio-label">
<input type="radio" id="radio3" value={responseData.priority} checked={priority === 'High'} onChange={handlePriority}/>
<span className="radiomark"></span>
<span className="radiotext">High</span>
</label>
</div>
</div>
<div className="form-group">
<label htmlFor="todo_completed">Status:</label>
<div id="todo_completed">
<label htmlFor="checkcompleted" className="check-label">
<input type="checkbox" id="checkcompleted" value={responseData.completed} onChange={handleCompleted}/>
<span className="checkmark"></span>
<span className="checktext">Completed</span>
</label>
</div>
</div>
<div className="form-group">
<button type="submit" className="form-btn"><FaTelegramPlane />Save Changes</button>
</div>
</form>
</div>
</div>
);
}
export default EditTodo;
Here's my custom hook for fetching data based on the Todo ID i get from the url:
import { useState,useEffect } from 'react';
import axios from 'axios';
const useGetApiWithParams = (url,params)=>{
const [data,setData] = useState([]);
useEffect(()=>{
const fetchData = async ()=>{
const response = await axios.get(`${url}/${params}`);
const data = response.data;
const error = response.error;
if(error)
console.log(`Error: ${error}`)
else{
console.log(...data);
setData(...data);
}
};
fetchData();
},[url,params])
return data;
}
export default useGetApiWithParams;
And the url with id param from MongoDB:
How to solve this? I tried the placeholder and set it's value to the response data from api but it only works for text boxes, what about the radio buttons and checkboxes? They don't seem to set the default value. For example the radio button for setting the priority value={responseData.priority} doesn't set the value as you can see here:
What i would like to happen is: from the Todos page on clicking the edit button, the edit page with a form already filled with values based on the todo id from the url parameter and be able to edit the values as well. Need help, thanks!
I'm using Axios to send POST request to Node.js server but it doesn't work. Do you have any idea how to resolve it?
My code is shown below:
server.js:
app.post('/registration', (req, res) => {
console.log(req.body);
});
my class:
export default class Registration extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {}
}
handleSubmit (e) {
e.preventDefault;
axios.post('/registration', {name: document.getElementById('name') }).then(res => {
console.log(res);
})
}
render() {
return (<form className="registrationForm">
<input type="text" name="name" id="name" required="required" placeholder="name"/>
<br/>
{/*<input type="text" name="email" required="required" placeholder="email"/>
<br/>
<input type="number" name="phoneNumber" required="required" placeholder="phoneNo"/>
<br/>
<input type="password" name="password" required="required" placeholder="pass"/>
<br/> */}
<button className="registerButton" onClick={this.handleSubmit}>register</button>
</form>)
};
}
You have various problems in your code
preventDefault is method. You need to call it
I doubt you want to send DOM element to the server
You want to handle network failure using catch
Corrected handleSubmit should look like this
handleSubmit (e) {
e.preventDefault(); // NB
const data = {name: document.getElementById('name').value /* NB */ };
axios.post('/registration', data).then(res => {
console.log(res);
}).catch(console.error) // now you could see what the actual problem is
}
Also it is generally not adviced to use DOM lookup methods in your React up. You should better keep a ref to the input.
<input ... ref={input => this.name = input}/>
const data = {name: this.name.value };
The problem was for just a single line that I didn't write in my server side application.
The only thing to check is to put the following line after requiring the body-parser in your file.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
I'm trying to redirect to a React Component when submit the form with express. This is my code:
Express.js
app.use(bodyParser.urlencoded({extended: true}))
app.use(express.static(publicPath));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'))
})
app.post('/charge', (req, res) => {
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
stripe.charges.create({
amount: req.body.totalAmount * 100,
currency: 'eur',
source: req.body.stripeToken,
description: "Example charge",
receipt_email: req.body.receiptEmail
}).then((charge) => {
console.log(req.body)
}).catch((err) => {
console.log(err)
})
CheckoutForm.js
handleSubmit = (e) => {
e.preventDefault()
this.props.stripe.createToken({ name: this.state.name }).then(({ token }) => {
console.log(token)
this.stripeTokenHandler(token)
})
}
stripeTokenHandler = (token) => {
const form = document.getElementById('form')
const hiddenInput = document.createElement('input')
hiddenInput.setAttribute('type', 'hidden')
hiddenInput.setAttribute('name', 'stripeToken')
hiddenInput.setAttribute('value', token.id)
form.appendChild(hiddenInput)
form.submit()
}
render() {
return (
<div className="payment-container">
<form id="form" className="checkout-form" onSubmit={this.onSubmit}>
<label>
Card Number
<CardNumberElement
onChange={this.onChange}
style={{ base: { color: 'white' } }}
/>
</label>
<label>
Titular Name
<input
type="text"
value={this.state.name}
onChange={this.handleText}
className="input-text"
placeholder="Full Name"
/>
</label>
<label>
Expiration Date
<CardExpiryElement style={{base: {color: 'white'}}} />
</label>
<label>
CVC
<CardCVCElement style={{base: {color: 'white'}}} />
</label>
<label>
Email
<input
type="text"
value={this.state.email}
onChange={this.handleEmail}
className="input-text"
name="receiptEmail"
placeholder="Email"
/>
</label>
<button className="btn-buy">Pay now</button>
</form>
</div>
)}
}
The form submitted correctly and register the payment. When I submitted now gets the request body. What's the way to redirect a component with the request form?
If what you're looking for is your component to redirect once you hit the "Pay Now" button then I suggest you change your <button> to a <Link> component using React Router.
It would look like this
<Link to='/charge' className='btn-buy' onClick={this.formSubmit}>Pay Now</Link>
Then just remove the this.formSubmit from the form itself since its now on the button. It will redirect and submit.
If you want to WAIT to redirect after you get a reply, then in your formSubmit function, after you get a reply back from the server you can use this.props.history.push('/charge') in then then portion of your form submitter.
Keep in mind you will need to import { Link } from 'react-router-dom' for this.
Hope it helps!
Another way you could handle this is by using a Redirect component from React Router. Something like this:
// in the route, if the post was successful
res.json({data: 'whatever data you want', success: true})
// and if it was unsuccessful
res.json({success: false})
And here's a really stripped down react component to go with it:
class FormComponent extends Component {
constructor() {
super();
this.state = {
// inputs or whatever
success: true,
}
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleFormSubmit(e) {
e.preventDefault();
fetch('send the data', { with: 'some options' })
.then(res => res.json())
.then(jsonRes => {
const { success } = jsonRes;
this.setState({ success });
})
.catch(err => console.log(err))
}
// assorted other stuff to handle inputs or whatever
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
{/* inputs or whatever */}
</form>
{this.state.success && <Redirect push to='/some-other-url' />}
</div>
)
}
}
If this.state.success evaluates to true, the redirect will render; otherwise, it won't at all.
This allows you to make sure the request was successful before redirecting to another page. It also lets you give the user an error message, do something with whatever the response is, etc etc. Maybe you'd need to move the state and so on further up your component tree so more components have access to the response data, if that's something they need.