Not populating mongoDB database with data being entered on angular - node.js

I am writing a post form function using MEAN stack which saves the data to the DB.
When entering the data through postman on the node, express, mongoose side it stores in the database. however when entering the date through the angular frontend, the data isnt storing, this method i used for other forms and it worked however this one just doesn't:
HTML:
<form [formGroup]="form" (submit)="addMessage()">
<mat-form-field>
<mat-label>Username:</mat-label>
<input
placeholder="Username"
matInput
formControlName="username"
class="form-control"
type="string"
required
/>
</mat-form-field>
<br />
<mat-form-field>
<mat-label>Message:</mat-label>
<input
placeholder="Type Message Here..."
matInput
formControlName="message"
class="form-control"
type="string"
required
/>
</mat-form-field>
<br />
<mat-form-field>
<mat-label>Message Date:</mat-label>
<input
placeholder="Type Message Here..."
matInput
formControlName="messageDateTime"
class="form-control"
type="date"
required
/>
</mat-form-field>
<br />
<button mat-raised-button color="basic" type="submit">Send</button>
<br />
<mat-divider></mat-divider>
</form>
Typescript:
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup } from '#angular/forms';
import { Router } from '#angular/router';
import { MessageBoardService } from 'src/app/service/message-board.service';
import { Message } from 'src/app/models/messages.interface';
#Component({
selector: 'app-message-board',
templateUrl: './message-board.component.html',
styleUrls: ['./message-board.component.css']
})
export class MessageBoardComponent implements OnInit {
messages: Message[] = [];
constructor(private messageService: MessageBoardService) { }
form = new FormGroup({
username: new FormControl(''),
message: new FormControl(''),
messageDateTime: new FormControl(''),
});
addMessage() {
console.log('adding');
const formData = new FormData();
formData.append('username', this.form.value.username);
formData.append('message',this.form.value.message);
formData.append('messageDateTime',this.form.value.messageDateTime);
this.messageService.postMessage(formData).subscribe((d) => {
console.log(d);
});
//window.location.reload();
}
ngOnInit(): void {
this.messageService.getMessage().subscribe((M: Message[]) => {
this.messages = M;
})
}
}
Service:
postMessage(data: any){
return this.http.post<any>("http://localhost:3000/Messages", data)
.pipe(map((res:any)=>{
return res;
}))
}
The get function works fine in the services it is only the post.
Posting data using postman works well, but from the frontend it just saves the default data that is set in the mongoose schema
Schema:
const mongoose = require('mongoose');
const MessagesSchema = new mongoose.Schema({
username:{
type: String,
required: false,
default: "User"
},
message:{
type: String,
required: false,
default:"Content"
},
messageDateTime:{
type: Date,
required: false,
default: Date.now
}
})
const Messages = mongoose.model( 'Messages', MessagesSchema);
module.exports = Messages
Data Entered Using Angular Frontend:
Data Saved in Database:
(Console Output):
{username: 'User', message: 'Content', messageDateTime:
'2022-03-04T23:23:32.040Z', _id: '62229f740a9c53a525774f01', __v: 0}
message: "Content" messageDateTime: "2022-03-04T23:23:32.040Z"
username: "User"
__v: 0
_id: "62229f740a9c53a525774f01" [[Prototype]]: Object
(Data stored accessed by postman):
{
"_id": "62229f740a9c53a525774f01",
"username": "User",
"message": "Content",
"messageDateTime": "2022-03-04T23:23:32.040Z",
"__v": 0
},

I'm not sure why do you need FormData, as I have never used it in Angular
I generally send data like this to backend
let dataToSend: any = {
username: this.form.value.username,
message: this.form.value.message,
messageDateTime: this.form.value.messageDateTime
}
this.messageService.postMessage(dataToSend).subscribe((d) => {
console.log(d);
});
I'll also update the service and Content-Type header, assuming your backend is expecting JSON.
let headers = new Headers();
headers.append('Content-Type', 'application/json');
postMessage(data: any)
{
http.post('http://localhost:3000/Messages', JSON.stringify(data), {
headers : headers
}).pipe('Rest of the Code');
}

Related

how i can display the image path which i'm getting from the back end while editing the camp

i want to display the file path as input value when working on editing
the reason is that when i click on edit button all the others values (data) of camp is put into the input fields but the url of camp is not placing in the input filed
This is my camp.ts file
form = new FormGroup({
title:new FormControl('',),
images: new FormControl(''),
location: new FormControl(''),
price: new FormControl(''),
description: new FormControl('')
})
public uploadFile(event: Event): void {
const input = event.target as HTMLInputElement;
if (!input.files?.length) {
return;
}
const file = input.files[0];
this.form.patchValue({
images: file
});
this.form.get('images');
}
ngOnInit(): void {
this.id = this.route.snapshot.params.id;
this.getData();
}
get f(){
return this.form.controls;
}
getData(){
this.campgroundService.getDataById(this.id).subscribe(res => {
this.data = res;
this.campground = this.data;
this.form = new FormGroup({
title: new FormControl(this.campground.title, Validators.required),
images: new FormControl(this.campground.images[0].url , Validators.required),
location: new FormControl(this.campground.location, Validators.required),
price: new FormControl(this.campground.price, Validators.required),
description: new FormControl(this.campground.description, Validators.required)
});
console.log('Fetching Form Values from backend',this.form.value);
console.log('file data = ', this.campground.images[0].url);
});
}
This is my camp.html file
<div class="form-group" >
<label for="title"> Enter Title </label>
<input type="text" formControlName="title" placeholder="Enter Title" [ngClass]="{'is-invalid':submitted && f.title.errors}" class="form-control">
<div *ngIf="submitted && f.title.errors" class="invalid-feedback">
<div *ngIf="f.title.errors.required">
Title Is Required!
</div>
</div>
</div>
<!-- Image Input -->
<div class="form-group">
<label for="image"> Selcet Image </label>
<input type="file" name="images" id="image" (change)="uploadFile($event)" class="form-control" accept="image/x-png,image/gif,image/jpeg"/>
</div>
You need to add formControlName="images" to the second input:
<input
class="form-control"
formControlName="images"
type="file"
name="images"
id="image"
(change)="uploadFile($event)"
accept="image/x-png,image/gif,image/jpeg"
>
By the way, maybe it would be better if you do the form initialization at the beginning of your code, already with validators.
Then, when yo get the data response, you only have to update the values of the fields and do not re-create the form-controls for them.
Something like that:
form = new FormGroup({
title:new FormControl('', Validators.required),
images: new FormControl('', Validators.required),
location: new FormControl('', Validators.required),
price: new FormControl('', Validators.required),
description: new FormControl('', Validators.required)
})
...
getData(){
this.campgroundService.getDataById(this.id)
.subscribe(
res => {
this.data = res;
this.campground = this.data;
this.form.patchValue({
title: this.campground.title,
images: this.campground.images[0].url,
location: this.campground.location,
price: this.campground.price,
description: this.campground.description,
});
console.log('Fetching Form Values from backend',this.form.value);
console.log('file data = ', this.campground.images[0].url);
});
}

MERN stack: Cast to Number failed for value 'NaN'

I'm creating a simple user registration page where account level (1 = user & 2= admin) is putting up an error when i post a request through axios. This seemed to be a somewhat common error corrected mostly by having a default number value in the schema, which I already have. Furthermore, I do not come across this error when I test with postman.
Error:
Error: users validation failed: accLevel: Cast to Number failed for value "NaN" (type number) at path "accLevel", username: Path `username` is required., password: Path `password` is required.
at ValidationError.inspect (E:\SLIIT\Y2S2\ITP\FitnessFactory\backend\node_modules\mongoose\lib\error\validation.js:48:26)
However, when I test with post man I do not get this error and mongodb works. Like so. It also works for accLevel in int form.
{
"username": "Mary",
"password": "mary",
"accType": "customer",
"accLevel": "1"
}
Heres the user schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
username : {
type: String,
required: true
},
password : {
type: String,
required: true
},
accType: {
enum: ["customer", "instructor"],
//required: true
},
accLevel: {
type: Number,
default: 1
}
})
const User = mongoose.model('users', userSchema);
module.exports = User;
Heres the user route and controller file
const router = require("express").Router();
let User = require("../models/user");
//createUserAccount
router.route("/createUserAccount").post((req,res) => {
const username = req.body.username;
const password = req.body.password;
const accType = req.body.accType;
const accLevel = Number(req.body.accLevel);
const newUser = new User({
username,
password,
accType,
accLevel
})
newUser.save().then(() => {
res.json("User account created")
}).catch((err) => {
console.log(err);
})
})
//login
router.route("/userLogin/:id").post(async(req,res) => {
})
module.exports = router;
Heres is the component file for createUserAccount.jsx
//Create account component
import React, {useState} from "react";
import axios from 'axios';
//bootstrap imports
import Form from 'react-bootstrap/Form';
import Button from "react-bootstrap/Button";
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
//custom styles
import '../styles/CreateAccount.css';
function CreateAccount(){
//set states
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [reEnterPassword, setReEnterPassword] = useState("");
const [accType, setAccType] = useState("");
const [accLevel, setAccLevel] = useState("");
//handle change
function handleSubmit(e){
e.preventDefault();
if (password !== reEnterPassword){
alert("Passwords do not match!")
}
else if (accType === ''){
alert("Select account type!")
}
else if (accLevel === ''){
alert("Select account level!")
}
else{
const newUser = {
username,
password,
accType,
accLevel
}
console.log(newUser);
axios.post("http://localhost:8070/user/createUserAccount").then(()=>{
alert("account created")
}).catch((err)=>{
alert(err)
})
}
}
return (
<div>
<div className='createAccountBlock'>
<h1>Create Account</h1>
<form onSubmit={handleSubmit}>
<Form.Group name="username" className="mb-3" controlId="username">
<Form.Label>Username</Form.Label>
<Form.Control type="text" placeholder="Enter Username" className='formText'
onChange = {(e)=>{
setUsername(e.target.value);
}}
/>
</Form.Group>
<Form.Group name="password" className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password" className='formText'
onChange = {(e)=>{
setPassword(e.target.value);
}}
/>
</Form.Group>
<Form.Group name="reEnterPassword" className="mb-3" controlId="reEnterPassword">
<Form.Label>Re-enter Password</Form.Label>
<Form.Control type="password" placeholder="Re-enter Password" className='formText'
onChange = {(e)=>{
setReEnterPassword(e.target.value);
}}
/>
</Form.Group>
<Container>
<Row>
<Col>
<Form.Select name="accType" aria-label="Default select example"
onChange = {(e)=>{
setAccType(e.target.value);
}}
>
<option>Select Account Type</option>
<option value="customer">Customer</option>
<option value="instructor">Instructor</option>
</Form.Select>
</Col>
<Col>
<Form.Select name="accLevel" aria-label="Default select example"
onChange = {(e)=>{
setAccLevel(e.target.value);
}}
>
<option>Select Account Level</option>
<option value="1">Level 1 (user)</option>
<option value="2">Level 2 (admin)</option>
</Form.Select>
</Col>
</Row>
<div className='flex-container'>
<Button variant="primary" type="submit" className='btn1'>
Create Account
</Button>
</div>
</Container>
</form>
</div>
</div>
);
}
export default CreateAccount;
It works when I test with postman for both int and string, but fails using axios. Btw, I tried parseInt from the frontend as well.
Any Ideas?
It doesn't look like you're sending a data object in your POST request.
axios.post("http://localhost:8070/user/createUserAccount").then(()=>{
alert("account created")
}).catch((err)=>{
alert(err)
})
axios.post needs a second argument of the object that you are sending.

How do we load the selected Item values from MongoDB for <Select /> in react

I am new to learning React. I have build a small application which includes React,Node and MongoDB. I have two modules here, the Create data and the Edit data. My question is regarding the Select component from Reacts's built in library. When I create a user, I enter his availability time slots(isMulti in Select) from a component. On Submit, this data along with the the slots is getting inserted in the Mongo Db.This is all fine. I am having problem when loading the page for edit. How to make the previously selected items from dropdown show up on page load. The other fields show up fine with componentDidMount().
Here is what my update module looks like-
// eslint-disable-next-line
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Select from 'react-select';
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css";
// Connecting from front end to backend with Axios
import axios from "axios";
export default class EditPanel extends Component {
constructor(props) {
super(props);
//defining this
this.onChangefield1 = this.onChangefield1.bind(this);
this.onChangefield2 = this.onChangefield2.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.handleChange=this.handleChange.bind(this);
this.state = {
field1: "",
field2: "",
timeSlots:[],
filterOptions:[
{ label:"9:00am to 10:00am", value: "9:00am to 10:00am" },
{ label: "10:00am to 11:00am", value: "10:00am to 11:00am" },
{ label: "11:00am to 12:00pm", value: "11:00am to 12:00pm" },
{ label: "12:00pm to 01:00pm", value: "12:00pm to 01:00pm" },
{ label: "01:00pm to 02:00pm", value: "01:00pm to 02:00pm" },
],
selectedOption:[]
}
}
componentDidMount() {
console.log("inside componentmount");
axios.get('http://localhost:5000/someEndpoint/' + this.props.match.params.id)
.then(response => {
this.setState({
field1: response.data.field1,
field2: response.data.field2,
mailId: response.data.mailId,
timeSlots:response.data.timeSlots,
selectedOption: response.data.timeSlots,
})
console.log("Meow"+response.data.timeSlots);
})
.catch(function (error) {
console.log(error);
})
}
onChangefield1(e) {
this.setState({ field1: e.target.value });
}
onChangefield2(e) {
this.setState({ field2: e.target.value });
}
// This is for insertion of any new selected list items
handleChange = selectedOption => {
console.log(selectedOption.value);
this.setState({ selectedOption: selectedOption.value }); // selected option value
console.log(selectedOption);
var dataArray = [];
for(var o in selectedOption) {
dataArray.push(selectedOption[o].value);
this.setState({ timeSlots: dataArray });
console.log(this.timeSlots);
}
};
onSubmit(e) {
e.preventDefault();
const panel =
{
field1: this.state.field1,
field2: this.state.field2,
timeSlots:this.state.timeSlots
}
axios.post('http://localhost:5000/someEndpoint/update/' + this.props.match.params.id, panel)
.then(res => console.log(res.data));
console.log("calling from edit");
window.location = '/';
}
render() {
return (
<div>
<h3>Edit Panel Info</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Field 1: </label>
<input type="text"
required
className="form-control"
value={this.state.field1}
onChange={this.onChangefield1}
/>
</div>
<div className="form-group">
<label>field2: </label>
<input
type="text"
className="form-control"
value={this.state.field1}
onChange={this.onChangefield2}
/>
</div>
<div className="form-group">
<label>Time Slots</label>
<div>
<Select
options={this.state.filterOptions} // Options to display in the dropdown
isMulti
value={this.state.selectedOption} // would like to see the values we have in DB
onChange={this.handleChange}
closeIcon="close"
/>
</div>
</div>
<div className="form-group">
<input type="submit" value="Edit Panel" className="btn btn-primary" />
</div>
</form>
</div>
)
enter code here
}
}`enter image description here`
[enter image description here][1]
[1]: https://i.stack.imgur.com/NliEH.png
The mongo db data looks like
timeSlots
:
Array
0
:
"10:00am to 11:00am"
1
:
"12:00pm to 01:00pm"

What Are The Best Error Handling Processes For a Stateless React Form Both Client & Server Errors?

I added the entire component, being that I'm new to posting I figured more information was better than not enough. Basically where it stands the form is "stateless", other than using the state the form itself holds. I'm attempting to handle errors that will come from two angles. Client side errors like,
"Passwords dont match, "Not a valid email", ect. Those I'm attempting to use validator lib for. Second angle being server side errors. Currently the errors will come in from my mutation response. I'm hoping that I can find a smooth way to handle these errors without state, and currently I'll try to use handleChange function for client. If that's a bad approach or to data heavy please share your advice/wisdom. Also, any advice or better practice is greatly appreciated in the code, and in future posting. Thanks (Very new to Full Stack Development)
Imports
import React from 'react'
import Container from '#material-ui/core/Container'
import { makeStyles } from '#material-ui/core/styles'
import TextField from '#material-ui/core/TextField'
import Button from '#material-ui/core/Button'
import Paper from '#material-ui/core/Paper'
import gql from 'graphql-tag'
import { useMutation } from '#apollo/react-hooks'
import FormHelperText from '#material-ui/core/FormHelperText'
import validate from 'validate.js'
import FormControl from '#material-ui/core/FormControl'
Material UI for styling
const useStyles = makeStyles({
form: {
width: '100%',
marginTop: 15,
},
paper: {
marginTop: 15,
},
register: {
margin: 15,
color: 'white',
backgroundColor: '#414849',
width: '95%'
},
email: {
margin: 15,
width: '95%'
},
username: {
margin: 15,
width: '95%'
},
password: {
margin: 15,
width: '95%'
},
confirmPassword : {
margin: 15,
width: '95%'
}
})
Form Component
// need to access error elements, update errors from validator & from server
// and also reset errors either on resubmit, or handleChange
function Form (props){
function handleChange(event, value){
const name = event.target.name
console.log(event.target.value)
}
async function onSubmit(props){
var form = document.querySelector('form')
var values = validate.collectFormValues(form)
console.log(form.email)
console.log(form.querySelectorAll('p')[0])
try{
const results = await props.client
.mutate({
mutation: gql`
mutation register
($userName: String!, $email: String!, $password: String!){
register(userName: $userName, email: $email, password: $password){
id
userName
}
}`,
variables: {
userName: form.userName.value,
email: form.email.value,
password: form.password.value,
}
})
console.log(results)
}
catch (err) {
console.log(err)
}
}
const classes = useStyles()
return(
<Container component="main" maxWidth="sm">
<Paper className={classes.paper}>
<form
name='form'
className={classes.form}>
<TextField
className={classes.email}
name='email'
variant='outlined'
type='text'
label='Email'
InputLabelProps={{
shrink: true,
}}
onChange={(event) => {
handleChange(event)
}}/>
<FormHelperText
name='emailError'
>
Future Email Error
</FormHelperText>
<TextField
className={classes.username}
name='userName'
variant='outlined'
type='text'
label='Username'
InputLabelProps={{
shrink: true,
}}
onChange={(event) => {
handleChange(event)
}}/>
<TextField
className={classes.password}
name='password'
variant='outlined'
type='text'
label='Password'
InputLabelProps={{
shrink: true,
}}
onChange={(event) => {
handleChange(event)
}}/>
<TextField
className={classes.confirmPassword}
name='confirmPassword'
variant='outlined'
type='text'
label='Confirm Password'
InputLabelProps={{
shrink: true,
}}
onChange={(event) => {
handleChange(event)
}}/>
<Button
className={classes.register}
variant='contained'
onClick={()=> {
onSubmit(props)
}}>
REGISTER
</Button>
</form>
</Paper>
</Container>
)
}
export default Form
I got it all wired up at the moment and so far so good. I posted my solution below for anyone interested in critique or maybe it'll help solve your own issue.
inside onSubmit function.
var clientError;
var form = document.querySelector('form')
var values = validate.collectFormValues(form)
var errors = await validate(values, constraints)
if(errors == undefined){
clientError = false
} else clientError = true
resetErrors(form)
handleErrors(form, errors || {})
if(clientError == false){
try{
const results = await props.client
.mutate({
mutation: gql`
mutation register
($userName: String!, $email: String!, $password: String!){
register(userName: $userName, email: $email, password: $password){
id
userName
}
}`,
variables: {
userName: form.userName.value,
email: form.email.value,
password: form.password.value,
}
})
console.log(results)
props.props.history.push('/login')
}
catch (err) {
handleGraphqlErrors(form, err)
}
}
}
The actual functions for error handling
//handle return errors from mutation
function handleGraphqlErrors(form, err){
const graphqlErrors = err.toString().slice(22)
console.log(graphqlErrors)
if(graphqlErrors == 'Username is already taken'){
document.getElementById('userNameError').innerHTML = graphqlErrors
}
if(graphqlErrors == 'Email is already in use'){
document.getElementById('emailError').innerHTML = graphqlErrors
}
}
//map errors to proper input fields
function handleErrors(form, errors){
Object.keys(errors).map((key, index) => {
console.log(errors[key])
document.getElementById(`${key}Error`).innerHTML = errors[key]
})
}
//reset error input fields to blank
function resetErrors(form){
var errorInputs = form.querySelectorAll('p')
Object.keys(errorInputs).map(key => {
errorInputs[key].innerHTML = ''
})
}

Failing to make axios.put from react to node.js server

I have react component where i trying to change status of object. However I'm failing and didn't understand whether I'm on right way.
This is nodeJS server with express and mongoose.
Frond end side:
handleSubmit2(event){
const body= {status: this.state.newstatus}
event.preventDefault()
axios.put('http://127.0.0.1:3010/api/users/:user_id',{body})
alert("Status Changed")
}
in render:
{hits.map(item => <li key={item._id}> {item.firstname} {item.lastname} <br /> <strong> {item.status}</strong>
<form autoComplete="off" className="new_status" onSubmit={this.handleSubmit2}>
<FormControl>{this.loadoptions()}</FormControl>
<input type="submit" value="Submit" className="input"/>
<br />
</form>
My back-end:
router.put( '/:user_id', ( req, res ) => {
console.log(req.body.body.status)
User.findByIdAndUpdate(req.params.user_id, req.body.body.status)
.then(data => {
if(follower)
res.send(data);
else
res.status(404).send("Follower not found");
})
.catch (e => res.status(400).send("Follower not exist"));
});
UserSchema:
const userSchema = new mongoose.Schema ({
firstname: String,
lastname: String,
status: String
});
const User = mongoose.model('User', userSchema);
Link to git:
https://github.com/fmpro12/users_add
When i submit form i want to change status of specific people from array.
What I'm doing wrong?

Resources