javascriptnode.jsmongodbexpressejs

Why is req.body empty when posting form in quiz.ejs


app.js

const express = require('express');
const mongoose = require('mongoose');
const path=require('path');
const app = express();

// Connect to MongoDB
async function main(){
    await mongoose.connect('mongodb://localhost:27017/quizApp');
    console.log('db is connected');
}
main();
// Middleware
app.set('view engine', 'ejs');
app.set('views',path.join(__dirname,'/views'));
app.use(express.static(path.join(__dirname,'public')));
app.use(express.urlencoded({extended:true}));
app.use(express.json());

// Routes
const indexRoutes = require('./routes/index');
app.use('/', indexRoutes);

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

routes/index.js

const express = require('express');
const router = express.Router();
const Quiz = require('../models/quiz');

router.get('/', async (req, res) => {
    const quizzes = await Quiz.find({});
    res.render('quiz', { quizzes });
});

router.post('/submit', async (req, res) => {
    const answers = req.body;
    console.log(req.body);
    let score = 0;
    const quizzes = await Quiz.find({});
    quizzes.forEach(quiz => {
        if (answers[quiz._id] === quiz.answer) {
            score++;
        }
    });
    res.render('result', { score, total: quizzes.length });
});

module.exports = router;

quiz.ejs

<form id="quizForm" action="http://localhost:3000/submit" method="post">
  <% quizzes.forEach((quiz, index) => { %>
  <div
    class="question-container"
    id="question-<%= index %>"
    style="display: none"
  >
    <p class="lead"><%= quiz.question %></p>
    <% quiz.options.forEach(option => { %>
    <div class="custom-control custom-radio">
      <input
        type="radio"
        id="<%= 'question' + index + option %>"
        name="<%= quiz._id %>"
        value="<%= option %>"
        class="custom-control-input"
        onclick="checkAnswer('<%= quiz.answer %>', this)"
      />
      <label
        class="custom-control-label"
        for="<%= 'question' + index + option %>"
        ><%= option %></label
      >
    </div>
    <% }) %>
  </div>
  <% }) %>
  <button
    type="button"
    class="btn btn-secondary"
    id="prevBtn"
    onclick="changeQuestion(-1)"
    disabled
  >
    Previous
  </button>
  <button
    type="button"
    class="btn btn-secondary"
    id="nextBtn"
    onclick="changeQuestion(1)"
    style="display: none"
  >
    Next
  </button>
  <button
    type="submit"
    class="btn btn-primary"
    id="submitBtn"
    style="display: none"
  >
    Submit
  </button>
</form>
<script>
  const questions = document.querySelectorAll(".question-container");
  let currentQuestionIndex = 0;

  function showQuestion(index) {
    questions.forEach((question, i) => {
      question.style.display = i === index ? "block" : "none";
    });
    document.getElementById("questionNumber").textContent =
      `${index + 1}/${questions.length}`;
    document.getElementById("prevBtn").disabled = index === 0;
    document.getElementById("nextBtn").style.display =
      index === questions.length - 1 ? "none" : "inline-block";
    document.getElementById("submitBtn").style.display =
      index === questions.length - 1 ? "inline-block" : "none";
  }

  function changeQuestion(direction) {
    currentQuestionIndex += direction;
    showQuestion(currentQuestionIndex);
  }

  function checkAnswer(correctAnswer, selectedOption) {
    const questionContainer = selectedOption.closest(".question-container");
    const options = questionContainer.querySelectorAll(".custom-control-input");
    options.forEach((option) => {
      const label = questionContainer.querySelector(
        `label[for="${option.id}"]`,
      );
      if (option.value === correctAnswer) {
        label.style.backgroundColor = "green";
      } else if (option.checked) {
        label.style.backgroundColor = "red";
      }
      option.disabled = true;
    });
    document.getElementById("nextBtn").style.display = "inline-block";
  }

  // Initial display of the first question
  showQuestion(currentQuestionIndex);
</script>

Quiz model Schema

const mongoose = require('mongoose');

const quizSchema = new mongoose.Schema({
    question: String,
    options: [String],
    answer: String
});
module.exports = mongoose.model('Quiz', quizSchema);

Problem In quiz.ejs why form give me empty object in console I proper give name and value of input after submit the form and goes to the submit router I mention in above code and I console.log(req.body) it give me a empty object. I make a quiz app website and when i give the all the answers of the questions and final submit then it not show the score it and when i saw all my code then I see req,body give me empty object why i also use the middlewares to parse the post then why it give me empty object


Solution

  • The problem is with JavaScript checkAnswer function: it sets every option to disabled, and disabled options don't send values when form is submitted, which is why you get empty object on the server.

    So, you need to change your logic, and prevent having disabled option which should send data.

    For example, you could disable options that are not correct and not checked, and to the correct one add a function which will prevent selecting it afterwards, and by that enable it being submitted along with the form, however, it would not be styled as disabled, but you could style it to appear as disabled, for example, reduce opacity, remove pointer events or similar.

    Try this code:

      if (option.value === correctAnswer) {
        label.style.backgroundColor = "green";
        // leave it enabled, but prevent later selections
        option.onclick = () => false;
        // style it a bit to look like disabled
        option.style.opacity = ".5";
        option.style.pointerEvents = "none";
      } else {
        // disabled wrong and unchecked
        option.disabled = true;
        // style wrong
        if (option.checked) label.style.backgroundColor = "red";
      }