I am building a quiz app with Vue 3 and Bootstrap 4.
I have this method for checking if the clicked answer is the (same as the) correct answer:
checkAnswer(answer) { return answer == this.results[this.questionCount]["correct_answer"]; }
It should be executed upon clicking a list item, as seen below:
<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
If the clicked answer is correct, the list item should be added the classes text-white bg-success
, otherwise it should be added text-white bg-danger
.
const quizApp = { data() { return { questionCount: 0, results: [ { question: "The book "The Little Prince" was written by...", correct_answer: "Antoine de Saint-Exupéry", incorrect_answers: [ "Miguel de Cervantes Saavedra", "Jane Austen", "F. Scott Fitzgerald" ] }, { question: "Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?", correct_answer: "Camino Island", incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"] }, { question: "In Terry Pratchett's Discworld novel 'Wyrd Sisters', which of these are not one of the three main witches?", correct_answer: "Winny Hathersham", incorrect_answers: [ "Granny Weatherwax", "Nanny Ogg", "Magrat Garlick" ] } ] }; }, methods: { nextQuestion() { if (this.questionCount < this.results.length - 1) { this.questionCount++; } }, prevQuestion() { if (this.questionCount >= 1) { this.questionCount--; } }, checkAnswer(answer) { // check if the clicked anwser is equal to the correct answer return answer == this.results[this.questionCount]["correct_answer"]; }, shuffle(arr) { var len = arr.length; var d = len; var array = []; var k, i; for (i = 0; i < d; i++) { k = Math.floor(Math.random() * len); array.push(arr[k]); arr.splice(k, 1); len = arr.length; } for (i = 0; i < d; i++) { arr[i] = array[i]; } return arr; } }, computed: { answers() { let incorrectAnswers = this.results[this.questionCount][ "incorrect_answers" ]; let correctAnswer = this.results[this.questionCount]["correct_answer"]; // return all answers, shuffled return this.shuffle(incorrectAnswers.concat(correctAnswer)); } } }; Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app { height: 100vh; } .container { flex: 1; } .quetions .card-header { padding-top: 1.25rem; padding-bottom: 1.25rem; } .quetions .card-footer { padding-top: 0.7rem; padding-bottom: 0.7rem; } .answers li { cursor: pointer; display: block; padding: 7px 15px; margin-bottom: 5px; border-radius: 6px; border: 1px solid rgba(0, 0, 0, 0.1); background: #fff; } .answers li:last-child { margin-bottom: 0; } .answers li:hover { background: #fafafa; } .pager { list-style-type: none; margin: 0; padding: 0; display: flex; justify-content: space-between; } .pager li > a { display: inline-block; padding: 5px 10px; text-align: center; width: 100px; background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 999px; text-decoration: none !important; color: #fff; } .pager li > a.disabled { pointer-events: none; background-color: #9d9d9d !important; } .logo { width: 30px; } .nav-item { width: 100%; } .card { width: 100%; } @media (min-width: 768px) { .nav-item { width: auto; } .card { width: 67%; } } @media (min-width: 992px) { .card { width: 50%; } }
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <script src="https://unpkg.com/vue@next"></script> <div id="quiz_app" class="container quetions d-flex align-items-center justify-content-center my-3"> <div v-if="results.length" class="card shadow-sm"> <div class="card-header bg-light h6"> {{results[questionCount]['question']}} </div> <div class="card-body"> <ul class="answers list-unstyled m-0"> <li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li> </ul> </div> <div class="card-footer bg-white"> <ul class="pager"> <li><a href="#" @click="prevQuestion" class="bg-dark" :class="{'disabled' : questionCount == 0}">Previous</a></li> <li class="d-flex align-items-center text-secondary font-weight-bold small">Question {{questionCount + 1}} of {{results.length}}</li> <li><a href="#" class="bg-dark" :class="{'disabled' : questionCount == results.length - 1}" @click="nextQuestion">Next</a></li> </ul> </div> </div> </div>
The problem
To my surprise, the checkAnswer(answer)
method is executed before (and in the absence of) any click.
Question
What am I doing wrong?
Advertisement
Answer
UPDATED
checkAnswer()
is invoked immediately if used outside a handler.
maybe this will help, when checkAnswer()
is called, store the selected answer selectedAnswer
and check if answer is correct isCorrect
, and use these 2 states to compare the looped answers.
<li v-for="answer in answers" :key="answer" @click="checkAnswer(answer)" :class="{ 'text-white bg-success' : (selectedAnswer === answer && isCorrect), 'text-white bg-danger' : (selectedAnswer === answer && !isCorrect) }" > {{answer}} </li> data() { return { isCorrect: false, selectedAnswer: '' ... } }, methods: { checkAnswer(answer) { // check if the clicked anwser is equal to the correct answer this.selectedAnswer = answer if (answer == this.results[this.questionCount]["correct_answer"]) { this.isCorrect = true } else { this.isCorrect = false } }, }