0

Context

I am building a voting app as part of the Free Code Camp back end certification. More info about it here, including the required user stories. http://www.freecodecamp.com/challenges/basejump-build-a-voting-app

One of the user stories is that I can create a poll with an unlimited number of options that people can vote on. There has to be a minimum of 2 user stories (otherwise you couldn't vote on anything LOL), but a user should have the ability to create a poll 4, 10, even 20 options to their polls, and other authenticated users should have the ability to add poll options if they don't like any of the options from the original author.

Rather Easy... But!

This is rather easy to do if there is a fixed or known number of text fields that I have to require. For example, if I wanted all new polls to have two and only two possible options, then I would write something simple like req.body.option1 and req.body.option2 to grab the values from my HTML forms, and from there, I would validate the data and then pass it on to my data models. (I'm using MongoDB and Mongoose)

But What if...

There are more than 2 options? How could my node app interact with the browser to count the number of text fields (with a specified name attribute)? How could I grab all of these text fields (quantity unknown) and store them in an array or object?

What I've Got So Far...

From my routes

router.post('/create', function(req, res, next) {
  var question = req.body.question,
      option1 = req.body.option1,
      option2 = req.body.option2;

  req.checkBody('question', 'You cannot have a poll without a question!').notEmpty();
  req.checkBody('option1', 'You need at least 2 options. Complete option 1').notEmpty();
  req.checkBody('option2', 'You need at least 2 options. Complete option 2').notEmpty();

  var errors = req.validationErrors();

  if (errors) {
    res.render('create', {
      errors: errors,
      question: question,
      option1: option1,
      option2: option2
    });
  } else {
    var newPoll = new Poll({
      question: question,
      option1: option1,
      option2: option2
    });
  }

  // create user
  Poll.createPoll(newPoll, function(err, poll) {
    if (err) throw err;
    console.log(poll);
  });

  req.flash('success', 'Your poll has been posted!');
  res.location('/');
  res.redirect('/');
});

from my Mongoose model

// user schema
var PollSchema = mongoose.Schema({
    username: {
        type: String,
        index: true
    },
    question: {
        type: String
    },
    option1: {
      type: String
    },
    option2: {
      type: String
    }
});

var Poll = module.exports = mongoose.model('Poll', PollSchema);

module.exports.createPoll = function(newPoll, callback) {
    newPoll.save(callback);
};

from my view where I'm capturing data

form(method='post', action='/polls/create', enctype='multipart/form-data', id="createpoll")
        .form-group
            label Poll Question
            input.form-control(name='question', type='text', placeholder='Write a question here')
        .form-group
            label Option 1
            input.form-control(name='option1', type='text', placeholder='First Option')
        .form-group
            label Option 2
            input.form-control(name='option2', type='text', placeholder='Second Option')
        input.btn.btn-default(id="newoption", value="add option")
        input.btn.btn-default(name='submit', type='submit', value='newpolls')

and some simple jQuery that allows users to add a new text field

$(document).ready(function() {
  var numOptions = 2;
  var htmlTag = '';
  $('#newoption').on('click', function() {
    numOptions++;
    htmlTag = 'option' + numOptions;
    $('#newoption').before('<div class="form-group"><label>Option '+numOptions+'</label><input type="text" class="form-control" name='+htmlTag+' placeholder="Additional Option"></div>');
  });
});

What I Know Already

I know that I can't directly interact with the DOM from the server side. I have read a little bit about the browserify module, but that seems to be exporting node modules for the browser, which isn't what I need. I could perhaps use the x-ray module to scrape the page for all text fields, but that would be incredibly slow, and it doesn't even work. Any ideas?

2 Answers2

0

I would do some transformation from DOM to JSON on the client, perhaps using a reusable approach such as this Convert form data to JavaScript object with jQuery

Then post that json and the body-parser node module (initialized with the right setting) can transparently turn the submitted JSON object into an in-memory object that can easily be iterated https://github.com/expressjs/body-parser#bodyparserjsonoptions

Community
  • 1
  • 1
Fabio Beltramini
  • 2,441
  • 1
  • 16
  • 25
0

You use a for loop (or a while loop if you wanted truly infinite capability).

    for(let i=1; i<100000; i++){
        let opt = req.option[`label:${i}`];     

        if(!opt)
            break;

        // .. do something
    }

This way, you test to see if the parameter was sent through (meaning the object is not undefined). If it is, you exit the loop. If it isn't, you do something with it.

Robert S
  • 496
  • 4
  • 14