Tommy
Tommy

Reputation: 638

node/express 4: displaying errors with express-validator on ajax post

I'm new to node and trying to show validation errors (using express-validator and express 4) when a user tries to submit a form.

The validator seem to work since if I log the data to console everything is as expected. However, I cannot display the errors on the page when I render the view (nothing is displayed). The only difference with the "standard" tutorials I have been following on express validation, is that I'm posting the data with AJAX.

My code is below. I use [...] to indicate that there is more code I removed, and I stripped down some of the form fields for brevity.

app.js

var express = require('express')
  , indexController = require('./routes/index')
  , membersController = require('./routes/members')
  [...]
  , cookieParser = require('cookie-parser')
  , bodyParser = require('body-parser')
  , expressValidator = require('express-validator');

var app = express();

[...]
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressValidator()) // tried both placing it here or below
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// I realize this may be not optimal
app.use(function(req,res,next){
    req.db = db;
    next();
});

app.use('/', indexController);
app.use('/members', membersController);

[...]
module.exports = app;

handle_members.js

$(document).ready(function() {

  $('#join_btn').click(function(event, req, res){
    event.preventDefault(); 

      var newMember = {
                      'name': $('#join_form input#join_name').val(),
                      'email': $('#join_form input#join_email').val()
                      }

      $.ajax({
              type: 'POST',
              data: newMember,
              url: '/members/addmember',
              dataType: 'JSON'
              }).done(function(response){
                if (response.msg === 'success'){
                  alert('New member added successfully!')
                }
                // maybe this is not necessary?
                else if (response.msg === 'validation'){  
                    alert('validation failed');
                }
                else{
                  alert('Error: ' + response.msg)
                }
              });        
  });     
});

members.js

var express = require('express');
var router = express.Router();

router.post('/addmember',validator, function(req, res) {
    var db = req.db;
    var collection = db.get('memberstest');
    collection.insert(req.body, function(err, result){
        res.send(
            (err === null) ? { msg: 'success' } : { msg: err }
        );
    });
});


function validator(req, res, next) {
    req.checkBody('email', 'not valid email').isEmail();
    req.checkBody('name', 'cannot be empty').notEmpty();
    var errors = req.validationErrors();
    if (errors) {
        console.log(errors) // these are as expected
        res.render('index',{errors:errors}); // no errors displayed
        }
    else {
        next();
    }
};

module.exports = router;

index.jade

        [...]
                form#join_form(method='POST', action='', role='form')
                    div.form-group
                        input#join_name.form-control(type='text', name='join_name')
                        input#join_email.form-control(type='email', name='email')
                    button#join_btn.btn(type='button') join

                if errors
                    ul
                        for error in errors
                            li = error.msg
          [...]

The errors variable when logged to console looks like this:

[ { param: 'email', msg: 'not valid email', value: 'xxxx' },   { param: 'name', msg: 'can't be empty', value: '' } ]

In the past couple of days I really tried a lot of different things but none of them worked. The problem seem to be always the same: all good but when I render the view is like it never enters the "if errors ..." statement. Maybe there is something very basic I'm missing here? Any help is greatly appreciated, thank you.

PS. Eventually the form ideally should be inside a modal/pop-up, in case that changes something.

Upvotes: 1

Views: 2085

Answers (1)

user7560588
user7560588

Reputation:

First attempt

Try to add the validator middleware immediately after any of the bodyParser middlewares, like the documentation asks to:

var express = require('express')
  , indexController = require('./routes/index')
  , membersController = require('./routes/members')
  [...]
  , cookieParser = require('cookie-parser')
  , bodyParser = require('body-parser')
  , expressValidator = require('express-validator');

var app = express();

[...]
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressValidator()); // this line must be immediately after any of the bodyParser middlewares!
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// I realize this may be not optimal
app.use(function(req,res,next){
    req.db = db;
    next();
});

app.use('/', indexController);
app.use('/members', membersController);

[...]
module.exports = app;

Second attempt

Based on the documentation, errors seems to be an array of objects with the msg and param properties. Try to update your jade code to the following:

ul
  if errors
    each error, i in errors
      li #{error.msg}

Third attempt

After analyzing the code that the OP provided me through Github, I found the problem. The OP is sending the data to the backend using an Ajax call, and responding it using res.render, which is clearly wrong, since res.render isn't to respond requests made with Ajax, the right method to do this is res.json.

Example:

Call /addmember endpoint using jQuery:

var data = {}; // data here

$.ajax({
  type: 'POST',
  data: data,
  url: '/addmember',
  dataType: 'JSON'
}).done(function() {
  alert('done');
}).fail(function() {
  alert('fail');
});

Answer it from the backend using res.json:

router.post('/addmember', (req, res) => {
  var db = req.db;
  var collection = db.get('memberstest');
  collection.insert(req.body, (error, result) => {
    if (error)
      return res.json({ error });
    return res.json({ result });
  });
});

Upvotes: 1

Related Questions