Omar Martin
asked on
Form validation does not work
I am using Joi (Happy) module in node.js to validate entries into a form to a mysql database. However, the validation does not work. I am receiving a "TypeError: Cannot read property 'path' of undefined" every time I leave the file field empty. Nothing occurs when I leave any other field empty. In other words, the validation simply does not work and no field message is being sent to the user based on the entry criteria. Please see the backend and the frontend code using sequelize. Without validation, the form works fine.
FRONTEND CODE WITH FORM:
PLEASE FIND THE BACKEND CODE:
FRONTEND CODE WITH FORM:
<!DOCTYPE html>
<html lang="en">
<head>
<!--JQuery UI: default css (smoothness)(always first)-->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--JQuery (always first)-->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>
<!--JQuery UI core (always first)-->
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<!--JQuery UI(after JQuery UI core) - Controls Dialog Modal -->
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head>
<body>
<!---(8)-->
<h3>Inserting Data into the database</h3>
<h3>Data List</h3>
<div id="storesF">
<div id="modalStationForm">
<!--form id="stationForm">-->
<form id="stationForm" method="POST" action="/api/station" enctype="multipart/form-data">
<input type="hidden" id="id" name="record_id">
<label>Store Name:</label>
<!--When submitting a form, the name value is important and is used in php file, while the id is used for css and javascript(below)-->
<input type="text" name="station" id="store" size="40" placeholder="Enter Store Name">
<br><br>
<label for="file">Profile Photo:</label>
<input type="file" name="image" id="image" onChange={this.uploadFile}>
<br><br>
<label>Address:</label>
<input type="text" name="address" id="location" size="40" placeholder="Enter the store's address">
<br><br>
<label>Store Sales:</label>
<input type="number" name="monthlycstoresales" id="sales" size="20">
<br><br>
<label>Operator:</label>
<input type="text" name="operator" id="dealer" size="40" placeholder="Enter the operator's name">
<br><br>
<label>Top SKU item:</label>
<input type="text" name="topsku" id="topitem" size="40" placeholder="Enter the store's top sku item">
<br><br>
<input id="insertD" type="submit" value="Insert">
<input id="insertE" type="submit" value="Edit">
<div id="statusF"></div>
</form>
</div> <!--id=storesF-->
</div> <!--id=modalStationForm-->
</div> <!--//id=container-->
</div> <!--//id=background-->
<script>
$(document).on('click', '.insert', function (e) {
//$('#insertD').click(() => {
e.preventDefault();
$("#insertE").remove();
$("#insertD").show();
$("#stationForm")[0].reset();
$("#modalStationForm")
.dialog("option", "title", "Inserting New Station")
.dialog("open");
});
$("#stationForm").on('submit', function (e) {
e.preventDefault();
console.log(this);
var data = new FormData(this);
console.log(data);
$.ajax({
url: '/api/station',
contentType: false,
processData: false,
type:'POST',
dataType: 'JSON',
data: data,
success: (data) => {
// console.log(JSON.stringify(data, null,2));
$('#result').text(JSON.stringify(data, null,2));
alert('Entry Record Successfully Submitted');
},
});
});
</script>
</body>
</html>
PLEASE FIND THE BACKEND CODE:
const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");
//Setting Storage Engine
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(
null,
`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
);
}
});
const fileFilter = function (req, file, cb) {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/gif"
) {
cb(null, true);
} else {
cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////
router.post("/station",upload.single("image"), (req, res, next) => {
let newStation = req.body.station;
let newImage = req.file.path.replace(/\\/g, "/");
let newAddress = req.body.address;
let newMonthlycstoresales = req.body.monthlycstoresales;
let newOperator = req.body.operator;
let newTopsku = req.body.topsku;
const schema = Joi.object().keys({
// ID: Joi.number().integer().required(),
newStation: Joi.string().alphanum().min(1).max(30).required(),
newImage: Joi.any().required(),
newAddress: Joi.string().alphanum().min(1).max(50).required(),
newMonthlycstoresales: Joi.number().integer().required(),
newOperator: Joi.string().alphanum().min(1).max(30).required(),
newTopsku: Joi.string().alphanum().min(1).max(30).required(),
});
//password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
//access_token: [Joi.string(), Joi.number()],
//birthyear: Joi.number().integer().min(1900).max(2013),
//}).with('username', 'birthyear').without('password', 'access_token');
let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']: newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };
let options = { abortEarly: false };
Joi.validate(data, schema, options, (err,value) => {
if(err) {
res.send('An error has occurred: ' + err);
}
});
Store.create(data)
.then(stores => {
console.log('Station record was successfully created: ' + stores);
})
//.catch(err => {
// res.send('error: ' + err)
return err;
});
module.exports = router;
ASKER
Please arrange it for me or give an example....I would greatly appreciate it.
replace :
let newImage = req.file.path.replace(/\\/ g, "/");
by :
let newImage = req.file && req.file.path.replace(/\\/ g, "/");
let newImage = req.file.path.replace(/\\/
by :
let newImage = req.file && req.file.path.replace(/\\/
ASKER
Did it work on your end Leakim971?
did not tried, but the error :"TypeError: Cannot read property 'path' of undefined"
is from here when req.file is null
let me know if you still have the error with the following :
router.post("/station",upl oad.single ("image"), (req, res, next) => {
let newStation = req.body.station;
let newImage = null;
if(req.file && req.file.path) {
newImage = req.file.path.replace(/\\/ g, "/");
}
let newAddress = req.body.address;
let newMonthlycstoresales = req.body.monthlycstoresale s;
let newOperator = req.body.operator;
let newTopsku = req.body.topsku;
is from here when req.file is null
let me know if you still have the error with the following :
router.post("/station",upl
let newStation = req.body.station;
let newImage = null;
if(req.file && req.file.path) {
newImage = req.file.path.replace(/\\/
}
let newAddress = req.body.address;
let newMonthlycstoresales = req.body.monthlycstoresale
let newOperator = req.body.operator;
let newTopsku = req.body.topsku;
ASKER
Leakim971,
It worked. I am not getting any error, however, there is no Joi error message when a field is left out or is incorrect. So the validation is not working yet.
It worked. I am not getting any error, however, there is no Joi error message when a field is left out or is incorrect. So the validation is not working yet.
const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");
//Setting Storage Engine
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(
null,
`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
);
}
});
const fileFilter = function (req, file, cb) {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/gif"
) {
cb(null, true);
} else {
cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////
router.post("/station",upload.single("image"), (req, res, next) => {
let newStation = req.body.station;
let newImage = null;
if(req.file && req.file.path) {
newImage = req.file.path.replace(/\\/g, "/");
}
let newAddress = req.body.address;
let newMonthlycstoresales = req.body.monthlycstoresales;
let newOperator = req.body.operator;
let newTopsku = req.body.topsku;
const schema = Joi.object().keys({
// ID: Joi.number().integer().required(),
newStation: Joi.string().alphanum().min(1).max(30).required(),
newImage: Joi.any().required(),
newAddress: Joi.string().alphanum().min(1).max(50).required(),
newMonthlycstoresales: Joi.number().integer().required(),
newOperator: Joi.string().alphanum().min(1).max(30).required(),
newTopsku: Joi.string().alphanum().min(1).max(30).required(),
});
//password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
//access_token: [Joi.string(), Joi.number()],
//birthyear: Joi.number().integer().min(1900).max(2013),
//}).with('username', 'birthyear').without('password', 'access_token');
let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']:newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };
let options = { abortEarly: false };
Joi.validate(data, schema, options, (err,value) => {
if(err) {
res.send('An error has occurred: ' + err);
}
});
Store.create(data)
.then(stores => {
console.log('Station record was successfully created: ' + stores);
})
//.catch(err => {
//res.send('error: ' + err)
//return err;
});
module.exports = router;
why your data have different names than the name in the schema?
ASKER
I corrected it as you pointed out and still the validation does not work.....all the fields are uploaded as is...
const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");
//Setting Storage Engine
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(
null,
`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
);
}
});
const fileFilter = function (req, file, cb) {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/gif"
) {
cb(null, true);
} else {
cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////
router.post("/station",upload.single("image"), (req, res, next) => {
let newStation = req.body.station;
let newImage = null;
if(req.file && req.file.path) {
newImage = req.file.path.replace(/\\/g, "/");
}
let newAddress = req.body.address;
let newMonthlycstoresales = req.body.monthlycstoresales;
let newOperator = req.body.operator;
let newTopsku = req.body.topsku;
const schema = Joi.object().keys({
// ID: Joi.number().integer().required(),
Station: Joi.string().alphanum().min(1).max(30).required(),
Image: Joi.any().required(),
Address: Joi.string().alphanum().min(1).max(50).required(),
['Monthly C-Store Sales']: Joi.number().integer().required(),
Operator: Joi.string().alphanum().min(1).max(30).required(),
['Top SKU']: Joi.string().alphanum().min(1).max(30).required(),
});
//password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
//access_token: [Joi.string(), Joi.number()],
//birthyear: Joi.number().integer().min(1900).max(2013),
//}).with('username', 'birthyear').without('password', 'access_token');
let data = {Station: newStation, Image: newImage, Address: newAddress, ['Monthly C-Store Sales']:newMonthlycstoresales, Operator: newOperator, ['Top SKU']:newTopsku };
//let data = {newStation: newStation, newImage: newImage, newAddress: newAddress, newMonthlycstoresales:newMonthlycstoresales, newOperator: newOperator, newTopsku: newTopsku };
let options = { abortEarly: false };
Joi.validate(data, schema, options, (err,value) => {
if(err) {
res.send('An error has occurred: ' + err);
}
});
Store.create(data)
.then(stores => {
console.log('Station record was successfully created: ' + stores);
})
//.catch(err => {
// res.send('error: ' + err)
//return err;
});
module.exports = router;
what about :
const express = require('express');
const router = express.Router();
const mysql = require("mysql");
const mysql2 = require("mysql2");
const db = require("../../middleware/db");
const Store = require("../../models/stores");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const Joi = require("@hapi/joi");
const multer = require("multer");
const path = require("path");
//Setting Storage Engine
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(
null,
`${new Date().toISOString().replace(/:/g, "-")}${file.originalname}`
);
}
});
const fileFilter = function (req, file, cb) {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/gif"
) {
cb(null, true);
} else {
cb(new Error("Only .jpeg, png or gifs files are accepted"), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
})
//////////////////////////////////////////////////////////////////////////////
router.post("/station",upload.single("image"), (req, res, next) => {
let Station = req.body.station;
let Image = null;
if(req.file && req.file.path) {
Image = req.file.path.replace(/\\/g, "/");
}
let Address = req.body.address;
let Monthlycstoresales = req.body.monthlycstoresales;
let Operator = req.body.operator;
let Topsku = req.body.topsku;
const schema = Joi.object().keys({
// ID: Joi.number().integer().required(),
Station: Joi.string().alphanum().min(1).max(30).required(),
Image: Joi.any().required(),
Address: Joi.string().alphanum().min(1).max(50).required(),
Monthlycstoresales: Joi.number().integer().required(),
Operator: Joi.string().alphanum().min(1).max(30).required(),
Topsku: Joi.string().alphanum().min(1).max(30).required(),
});
//password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
//access_token: [Joi.string(), Joi.number()],
//birthyear: Joi.number().integer().min(1900).max(2013),
//}).with('username', 'birthyear').without('password', 'access_token');
let data = { Station, Image, Address, Monthlycstoresales , Operator, Topsku };
//let data = {newStation: newStation, newImage: newImage, newAddress: newAddress, newMonthlycstoresales:newMonthlycstoresales, newOperator: newOperator, newTopsku: newTopsku };
let options = { abortEarly: false };
Joi.validate(data, schema, options, (err,value) => {
if(err) {
res.send('An error has occurred: ' + err);
}
});
Store.create(data)
.then(stores => {
console.log('Station record was successfully created: ' + stores);
})
//.catch(err => {
// res.send('error: ' + err)
//return err;
});
module.exports = router;
ASKER
Same result. No error but no validation. Perhaps, it is because the formData receives the data as one big object but nothing happens.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
it's look like you are not using expressjs/multer correctly
The init of multer needs to be outside of your middleware code.
Multer own middleware needs to be execute before yours.
When using array, there are multiple files being uploaded with same param name, so use req.files (with a s) , not req.file.