Req.Body is empty object on Angular 2 Form submission + Bodyparser - node.js

I have a reactive (model driven) form in my application and I'm having trouble getting the form data through to mongoose. When I submit the data via http post method, I keep getting an error because the backend is receiving an empty object.
I know the backend works because when I submit a post request with Postman, I get a result status of "201 Created". I've been reading the forums and various blogs for the past few days and I'm still stumped.
Questions
What do I need to change to get the form data to hit the backend
appropriately?
Is there a best practice for Angular2 Form data submission?
Notes
It may look like there are some things missing, such as the URL, and you will notice some additional fields in the form. I purposely did not include all of these for conciseness and obfuscated the actual links to my backend.
Backend - Mongoose
jobs.controllers.js
var mongoose = require('mongoose');
var Job = mongoose.model('Job');
module.exports.addNewJob = function(req, res){
console.log(req.body);//added to see req payload from client
Job
.create({
_id: req.body.jobid,
desc: req.body.name,
type: req.body.jobType,
location: req.body.location
},function(err, job){
if(err){
console.log("Error creating job");
res
.status(400)
.json(err);
}else{
console.log("Job Created", job);
res
.status(201)
.json(job);
}
});
}
jobs.model.js
var mongoose = require('mongoose');
var jobSchema = new mongoose.Schema({
_id: {
type: String
},
desc: {
type: String
},
type: {
type: String,
default: "TM"
},
location: String
});
mongoose.model('Job', jobSchema);
db.js
var mongoose = require ('mongoose');
var dburl = 'mongodb://localhost:27017/dbname';
mongoose.connect(dburl);
mongoose.connection.on('connected', function(){
console.log('Mongoose connected to ' + dburl);
});
mongoose.connection.on('disconnected', function(){
console.log('Mongoose disconnedted');
});
mongoose.connection.on('error', function(err){
console.log('Mongoose connection error: ' + err);
});
require('./jobs.model.js');
index.js
var express = require('express');
var router = express.Router();
var ctrlJobs = require('../controllers/jobs.controllers.js');
router
.route('/jobs')
.get(ctrlJobs.jobsGetAll)
.post(ctrlJobs.addNewJob);
module.exports = router;
app.js
require('./api/data/db.js');
var express = require('express');
var app = express ();
var path = require('path');
var bodyParser = require('body-parser');
var routes = require('./api/routes');
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({extended: false}));
app.use('/api', routes);//Goes to index.js
Front End - Angular2 Final
jobs.service
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class JobsService{
private _url = "http://123.456.789.100/api/jobs"; //removed actual address for post
constructor(private _http: Http){}
addNewJob(form: Object){
let headers = new Headers({'Content-Type':'application/json'});
let options = new RequestOptions({headers: headers});
return this._http
.post(this._url, JSON.stringify(form), options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response){
let body = res.json();
return body.data || { };
}
private handleError (error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
job-form.component
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { JobsService } from './jobs.service';
#Component({
templateUrl: './job-form.component.html',
providers: [JobsService]
})
export class JobFormComponent implements OnInit {
id: string;
job: any;
jobForm: FormGroup;
title: string;
constructor(
private _fb: FormBuilder,
private _router: Router,
private _route: ActivatedRoute,
private _jobsService: JobsService
){
}
ngOnInit(): void {
this.jobForm = this._fb.group({
jobid: ['', Validators.required],
name: ['', Validators.required],
jobType: ['', Validators.required],
location: ['', Validators.required]
});
this.id = this._route.snapshot.params['id'];
this.title = this.id ? "Edit Job" : "Create New Job";
}
save(form: any){
console.log(this.jobForm.value);
this._jobsService.addNewJob(form).subscribe((dataResponse) => {
console.log("Submitting" + dataResponse);
});
}
reset(){
this.jobForm.reset();
}
}
job-form.component.html
<form [formGroup]="jobForm" novalidate (ngSubmit)="save(jobForm.value)">
<div class="row">
<div class="col-xs-2">
<label>Job Number</label>
<input [ngModel]="job?.jobid ? job.jobid : ''" class="form-control" type="text" formControlName="jobid">
</div>
<div class="col-xs-8">
<label>Title</label>
<input [ngModel]="job?.name ? job.name : ''" class="form-control" type="text" formControlName="name">
</div>
<div class="col-xs-2">
<label>Type</label><br />
<select [ngModel]="job?.jobType ? job.jobType : ''"class="form-control" formControlName="jobType">
<option value="TM">T&M</option>
<option value="LS">Lump Sum</option>
</select>
</div>
</div>
<div class="row">
<div class="col-xs-2">
<label>Status</label><br />
<select class="form-control" formControlName="status" [ngModel]="job?.status ? job.status : ''">
<option value="Pending">Pending</option>
<option value="Active">Active</option>
<option value="On Hold">On Hold</option>
<option value="Complete">Complete</option>
<option value="Closed">Closed</option>
</select>
</div>
<div class="col-xs-5">
<label>Location</label>
<input [ngModel]="job?.location ? job.location : ''" class="form-control" type="text" formControlName="location">
</div>
</div>
<br />
<div class="row">
<div class="col-xs-1">
<button type="btn btn-primary" class="btn btn-primary" [disabled]="!jobForm.valid">Submit</button>
</div>
<div class="col-xs-1">
<button class="btn btn-default" (click)="reset()">Reset</button>
</div>
</div>
</form>
Testing
With the headers set to application/json the req.body is an empty object
{}
When I set the headers to application/x-www-form-urlencoded the req.body shows
{ '{"jobid":"8746541","name":"asdfasdfasdfasdf","jobType":"LS","location":"asdfa"}':''}
on submit
XHR
PostmanSuccess

in a file jobs.service you are setting Header to
'Content-Type':'application/json'
in a file index.js you are handling body request
app.use(bodyParser.urlencoded({extended: false}));
so there are two solution you can do.
1.) Either change in a jobs.service
'Content-Type': 'application/x-www-form-urlencoded'
2.) Or change in index.js
app.use(bodyParser.json());

Solution
Added line app.use(bodyParser.json()); to app.js. Essentially, this tells the application that it should natively understand JSON. Submitting the form in the application now results in a new record being added to mongodb.

In app.js add the following middlewares...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
For more information visit this site:link!

1.At front end level:-
When using reactive driven forms
Pass the front end class file binding
object as getRawValue().
1.1 At front end to backend integration
such as service in which our definite
method when call we have to pass the
binded object with when saving includes
headers as though it was post method.
Add middleware by requiring bodyparser. Use use as a bodyparser.json.
Navigate to particular route in postman and first check with new data and post it.
Now bind with front end object.
What we are passing to post rest API method was object from front end side.
Happy ending 😃

Related

Not sure why my frontend and backend aren't connecting; is it an API issue? Help pls

The issue is that the connection doesn't seem to be going through. The error log doesn't show anything: there's no sign data is being sent to the database. E.g. when I log in, or sign up, I can see that I'm doing that in the console. No dice here.
Vue component:
<script>
import KitchenAssignment from "../models/kitchenAssignment";
export default {
name: 'sendAssignment',
data(){
return {
kitchenAssignment: new KitchenAssignment('', '', '', '')
};
},
methods: {
submitForm(){
this.$store.dispatch('auth/saveAssignment')
}
}
}
</script>
Pertinent bit of my auth.service.js file:
import axios from 'axios';
const API_URL = 'https://appname.herokuapp.com/api/auth/';
...
saveAssignment(kitchenAssignment){
return axios.post(API_URL + 'kitchenAssignments', {
kAName: kitchenAssignment.kAName,
startTime: kitchenAssignment.startTime,
endTime: kitchenAssignment.endTime,
minRoleRequired: kitchenAssignment.password
})
}
My routes file:
const { authJwt } = require("../middleware"); // nothing in here yet, just trying to be sure i can actually send data to database
const controller = require("../controllers/kitchenAssignment.controller.js");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"Origin, Content-Type, Accept"
);
next();
});
app.post('/api/auth/kitchenAssignments', controller.saveAssignment);
};
My controller file:
const db = require("../models");
const KitchenAssignment = db.kitchenAssignment;
const Op = db.Sequelize.Op;
//const { default: kitchenAssignment } = require("../../vue-vuex-jwt-auth-master/src/models/kitchenAssignment");
exports.saveAssignment = async (req, res) => {
try {
const kitchenAssignment = await KitchenAssignment.create({
kAName: req.body.kAName,
startTime: req.body.startTime,
endTime: req.body.endTime,
minRoleRequired: req.body.minRoleRequired
});
}
catch (error) {
res.status(500).send({ message: error.message });
}
}
Not sure it's relevant, but here's my input area for the user in the .vue component? Maybe it's incorrect somehow.
<template>
<div class="container">
<header class="jumbotron">
<h3>{{content}}</h3>
<div>
<input v-model="kitchenAssignment.kAName"
placeholder="Input kitchen assignment name."
/>
<input v-model="kitchenAssignment.startTime"
placeholder="Input start time."
type="time"/>
<input v-model="kitchenAssignment.endTime"
placeholder="Input end time."
type="time"/>
<input
v-model="kitchenAssignment.minRoleRequired"
placeholder="Select minium role required."
v-validate="'required|pattern:^(Kitchen Leader|Sous Chef|Line Cook|Junior Cook|Dishwasher)$'"
name="minimum role required"
/>
<button #click="submitForm">Enter</button>
</div>
</header>
</div>
</template>
Would appreciate any help. I've tried a lot of stuff, can't recall everything but it led me to this point. Been reading up on Apis to no avail.

Cors error when sending data from front-end client side react component to node.js server [duplicate]

This question already has answers here:
CORS error even after setting Access-Control-Allow-Origin or other Access-Control-Allow-* headers on client side
(2 answers)
Closed 7 months ago.
I am attempting to send data from my client side react component below to my node.js server. Within my console it shows the data is being passed through from client side to server. However, I am getting the following cors error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
I am not sure why I am still getting this as I have required and implemented cors below, as well as put res.setHeader("Access-Control-Allow-Origin", "*"); in the post request.
Any help would be greatly appreciated! Thank you :)
UPDATE
I solved the issue by enabling the cors middleware app.use(cors())
node server.js file
const express = require("express");
const cors = require("cors");
const app = express();
const port = 4000;
app.post("/", cors(), (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const emailInfo = req.body.emailUserInput;
console.log(emailInfo);
// sendgrid details //
require("dotenv").config();
const sgMail = require("#sendgrid/mail");
const apikey = process.env.SENDGRID_API_KEY;
sgMail.setApiKey(apikey);
const msg = {
to: emailInfo,
from: "email",
subject: "Sending with Twilio SendGrid is Fun",
text: "and easy to do anywhere, even with Node.js",
html: "<strong>and easy to do anywhere, even with Node.js</strong>",
};
// email sending logic //
//ES8
(async () => {
try {
await sgMail.send(msg);
} catch (error) {
console.error(error);
if (error.response) {
console.error(error.response.body);
}
}
})();
});
app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
client-side react component
import "../StyleComponents/CreateEvent.css";
import { useState } from 'react';
import { db } from '../firebase';
import { uid } from "uid";
import { set, ref } from "firebase/database";
import Header from "./Header";
import { useNavigate } from 'react-router-dom';
import { getStorage, ref as sref, uploadBytes } from "firebase/storage";
import PlacesAutocomplete from 'react-places-autocomplete';
import { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import axios from 'axios';
function CreateEvent() {
// store user inputs in state //
const [titleUserInput, setTitleUserInput] = useState('');
const [dateUserInput, setDateUserInput] = useState('');
const [timeUserInput, setTimeUserInput] = useState('');
const [address, setAddress] = useState('');
const [setCoordinates] = useState({
lat: null,
lng: null,
})
const [headerUserInput, setHeaderUserInput] = useState('');
const [detailsUserInput, setDetailsUserInput] = useState('');
const [lengthUserInput, setLengthUserInput] = useState('');
const [emailUserInput, setEmailUserInput] = useState('');
const [userSubmit, setUserSubmit] = useState('');
const [error, setError] = useState(false);
// Create a root reference for storing event photos //
const storage = getStorage();
// handle changing user input data //
const handleTitleChange = (e) => {
setTitleUserInput(e.target.value);
}
const handleDateChange = (e) => {
setDateUserInput(e.target.value);
}
const handleTimeChange = (e) => {
setTimeUserInput(e.target.value);
}
const handleSelect = async (value) => {
const results = await geocodeByAddress(value);
const latLng = await getLatLng(results[0]);
setAddress(value);
setCoordinates(latLng);
}
const handleDetailsChange = (e) => {
setDetailsUserInput(e.target.value);
}
const handleLengthChange = (e) => {
setLengthUserInput(e.target.value);
}
const handleHeaderChange = (e) => {
// Create a root reference for storing event photos //
setHeaderUserInput(e.target.files[0]);
}
const handleEmailChange = (e) => {
setEmailUserInput(e.target.value);
}
const navigate = useNavigate();
// make call to the backend database to send email user input data //
const url = 'http://localhost:4000';
const getEmailInput = () => {
axios.post(url, {emailUserInput}).then((res) => {
console.log(res);
}).catch(console.log('error'));
}
// submit user data to database with unique ID for each event //
const writeToDataBase = () => {
let uuid = uid()
if (titleUserInput.length === 0 || dateUserInput.length === 0 || timeUserInput.length === 0 || address.length === 0 || emailUserInput.length === 0) {
setError(true);
}
if (titleUserInput && dateUserInput && timeUserInput && address && emailUserInput) {
const storageRef = sref(storage, uuid);
set(ref(db, `/${uuid}`), {
EventPhoto: headerUserInput,
EventTitle: titleUserInput,
EventDate: dateUserInput,
EventTime: timeUserInput,
EventLength: lengthUserInput,
EventLocation: address,
EventDetails: detailsUserInput,
});
getEmailInput('');
setUserSubmit('');
uploadBytes(storageRef, headerUserInput).then(() => {
navigate(`/EventCreated/${uuid}`);
});
}
}
return (
<>
< Header />
<div className="event-creation-container">
<h1>Create a New Event</h1>
<form>
<div className="event-name-container event-input">
<label for="eventTitle">Name of Event<span>*</span></label>
<input type="text" id="EventTitle" value={titleUserInput} onChange={handleTitleChange} />
{error && titleUserInput === '' ?
<label id="form-validation-label">Event name must be filled</label> : ""}
</div>
<div className="date-time-length">
<div className="date-input-container event-input">
<label for="Date">Date<span>*</span></label>
<input type="date" id="EventDate" value={dateUserInput} onChange={handleDateChange} />
{error && dateUserInput === '' ? <label id="form-validation-label">Event date must be filled</label>: ""}
</div>
<div className="time-input-container event-input">
<label for="Time">Time<span>*</span></label>
<input id="EventTime" type="time" name="time" timezone="timezone" value={timeUserInput} onChange={handleTimeChange} />
</div>
{error && timeUserInput === '' ? <label id="form-validation-label">Event time must be filled</label> : ""}
<div className="length-input-container event-input">
<label for="Length">Length</label>
<input id="EventLength" type="text" value={lengthUserInput} onChange={handleLengthChange} />
</div>
</div>
<div className="location-input-container event-input">
<label for="Location">Location<span>*</span></label>
<PlacesAutocomplete onChange={setAddress} value={address} onSelect={handleSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input id="EventLocation" {...getInputProps()} />
<div className="location-suggestions">
{loading ? <div>...loading</div> : null}
{suggestions.map((suggestion) => {
const style = {
backgroundColor: suggestion.active ? "#41b6e6" : "#fff"
};
return <div {...getSuggestionItemProps(suggestion, { style })}>{suggestion.description}</div>
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
{error && address ==='' ? <label id="form-validation-label">Event location must be filled</label> : ""}
<div className="details-input-container event-input">
<label for="Event_Details">Event Details</label>
<textarea type="text" id="EventDetails" value={detailsUserInput} onChange={handleDetailsChange} />
</div>
<div className="header-input-container event-input">
<div className="upload-image-flex-container">
<label for="header_image">Upload Header Image (optional)</label>
<input className="upload-input" type="file" id="
EventImage" name="filename" accept="image/png, image/jpeg" onChange={handleHeaderChange} />
</div>
</div>
<div className="orangizer-email-container">
<label for="organizer-email">Organizer's Email<span>*</span></label>
<p>The event page link will be sent to your email</p>
<input id="EventEmail" type="email" name="email" value={emailUserInput} onChange={handleEmailChange} />
{error && emailUserInput === '' ? <label id="form-validation-label">Event organizer's email must be entered</label> : ""}
</div>
<div className="create-event-btn-container">
<button className="event-create-button" type="button" value={userSubmit} onClick={writeToDataBase}>Create Event</button>
</div>
</form>
</div>
</>
)}
export default CreateEvent;
Your issue here may be the browser sending out a CORS preflight request which is then followed by your POST request. This is an OPTIONS request sent automatically which is where it is expecting to get the CORS headers from. See here.
Currently even though you've implemented CORS on your post endpoint, it's not catching that preflight request which is likely what you need to do. The 'cors' npm package describes that on their page. Implementing this should then allow the browser to recognise the origin so you no longer get errors back.

how to post form data to the server backend

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 !");
});

File upload (with other inputs and textarea) using Angular 13 and Node Js

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

How to pass data from React form to Node code?

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
}

Resources