Link to home
Start Free TrialLog in
Avatar of Omar Martin
Omar MartinFlag for United States of America

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:

<!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>

Open in new window



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;

Open in new window

Avatar of lenamtl
lenamtl
Flag of Canada image

Hi,

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.
Avatar of Omar Martin

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, "/");
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",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;
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.

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;

Open in new window

why your data have different names than the name in the schema?
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;

Open in new window

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;

Open in new window

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
Avatar of Omar Martin
Omar Martin
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial