@DmitryNs

Как выводить варианты ответов в случайном порядке?

Хочу, чтобы в квизе каждый раз перемешивались варианты ответов у вопроса. Как это реализовать?

Quiz.vue

<template>
  <div >
        <h1 >
          KanaQuiz
        </h1>
        <div class="quiz-bg shadow-lg ">
          
          <div v-if="idx < count">
            
      <div v-for="questionw in randomQuestions" :key="questionw.id">
      </div>
            <p class="title">{{questions[idx]['question']}}</p>
            <div class="row">
              <div class="card" v-for="(answer, index) in questions[idx].answers" :key="answer.id">
            <label
  
              :key="index"
              :for="index"
              class=""
              :class="[{'hover:bg-gray-100 cursor-pointer' : selectedAnswer == ''}, {'bg-green-200' : index == questions[idx].correctAnswer && selectedAnswer != ''}, {'bg-red-200' : selectedAnswer == index}]"
            >
              <input
                :id="index"
                type="radio"
                class="hidden"
                :value="index"
                @change="answered($event)"
                :disabled="selectedAnswer != ''"
              />
              {{answer}}
              
            </label>
          </div>
          </div>
           
            <div class="mt-6 flow-root">
              <button
                @click="nextQuestion"
                v-show="selectedAnswer != '' && idx < count - 1"
                class="float-right bg-indigo-600 text-white text-sm font-bold tracking-wide rounded-full px-5 py-2"
              >
                Next &gt;
              </button>
              <button
                @click="showResults"
                v-show="selectedAnswer != '' && idx == count - 1"
                class="float-right bg-indigo-600 text-white text-sm font-bold tracking-wide rounded-full px-5 py-2"
              >
                Finish
              </button>
            </div>
          </div>
          <div v-else>
            <h2 class="text-bold text-3xl">Results</h2>
            <div class="flex justify-start space-x-4 mt-6">
              <p>
                Correct Answers:
                <span class="text-2xl text-green-700 font-bold"
                  >{{correctAnswers}}</span
                >
              </p>
              <p>
                Wrong Answers:
                <span class="text-2xl text-red-700 font-bold"
                  >{{wrongAnswers}}</span
                >
              </p>
            </div>
            <div class="mt-6 flow-root">
              <button
                @click="resetQuiz"
                class="float-right bg-indigo-600 text-white text-sm font-bold tracking-wide rounded-full px-5 py-2"
              >
                Play again
              </button>
            </div>
          </div>
        </div>
      </div>
</template>

<script>
import usersData from "../questions.json";
export default {
  name: 'KanaQuiz',
  props: {
    msg: String
  },

 
  data() {
    return {
      idx: 0,
      selectedAnswer: "",
      correctAnswers: 0,
      wrongAnswers: 0,
      count: 5,
      questions: usersData,
    };
    
  },
  computed:{
    randomQuestions () {
      usersData.sort(() => Math.random() - 0.5)
      return false
    },
  },
  
  methods: {
    answered(e) {
      this.selectedAnswer = e.target.value;
      if (this.selectedAnswer == this.questions[this.idx].correctAnswer) {
        this.correctAnswers++;
      } else {
        this.wrongAnswers++;
      }
      
    },
    nextQuestion() {
      this.idx++;
      this.selectedAnswer = "";
      document.querySelectorAll("input").forEach((el) => (el.checked = false));
      
    },
    showResults() {
      this.idx++;
      
    },
    resetQuiz() {
      this.idx = 0;
      this.selectedAnswer = "";
      this.correctAnswers = 0;
      this.wrongAnswers = 0;
      this.questions.sort(() => Math.random() - 0.5)
      
    },
  },

}
</script>


questions.json

[
    {
  
      "question": "Questions1",
        "answers":  { "a": "answer1", "b": "answer2", "c": "answer3", "d": "answer4" } ,
        "correctAnswer": "b"
    },
    {

        "question": "Questions2",
        "answers": { "a": "2005", "b": "2008", "c": "2003", "d": "2004" },
        "correctAnswer": "d"
    }
]


В общем, хочется чтобы answers из questions.json каждый раз перемешивался при выводе.
  • Вопрос задан
  • 133 просмотра
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
function shuffle(arr) {
  for (let i = arr.length; i > 1;) {
    const j = Math.random() * (i--) | 0;
    [ arr[i], arr[j] ] = [ arr[j], arr[i] ];
  }

  return arr;
}

computed: {
  shuffledAnswers() {
    return shuffle(Object.entries(this.questions[this.idx].answers));
  },
  ...

<div v-for="[ key, answer ] in shuffledAnswers">
  ...

UPD. Конечно, вопрос был не об этом, но есть ряд замечаний по поводу показанного кода.

"answers": {

Пусть это будет массив. Соответственно, правильный ответ будет обозначаться через индекс.

import usersData from "../questions.json";

Не надо импортировать вопросы в компоненте теста, пусть они передаются в него через props.

selectedAnswer: "",

Всего один? Этого мало, надо запоминать все полученные ответы.

count: 5,

<div v-if="idx < count">

Если завтра добавятся новые вопросы или удалятся какие-то из существующих, побежите изменять значение этого свойства? Оно не нужно, надо смотреть на длину массива с вопросами. Или, можно сделать вычисляемое свойство, представляющее текущий вопрос (просто извлекаем элемент из массива с вопросами по индексу текущего вопроса), и проверять, существует ли он.

computed:{
  randomQuestions () {
    usersData.sort(() => Math.random() - 0.5)

Во-первых, вычисляемые свойства не должны изменять данные. Во-вторых, почему не надо так сортировать. Поскольку массив вопросов никак не изменяется, делать тут вычисляемое свойство смысла нет, пусть оно будет обычным. Перемешанная копия массива с вопросами, у вопросов перемешанные массивы ответов, а также добавлено свойство под ответ, выбранный пользователем.

:disabled="selectedAnswer != ''"

Блокировать выбор ответа не надо - вдруг пользователь случайно не туда нажал. Пусть будет возможность изменить сделанный выбор. Это касается не только ответов на текущий вопрос, а всех, т.е., в дополнение к

<button
  @click="nextQuestion"

надо сделать такую же кнопку для перехода к предыдущему вопросу.

document.querySelectorAll("input").forEach((el) => (el.checked = false));

Не надо лезть в DOM руками. Чтобы при переходе к следующему вопросу сбрасывать выбор, достаточно пересоздавать элементы, назначив их общему предку ключ, зависящий от индекса (:key="индекс_вопроса"). Но это, конечно, костыльное решение. Правильно будет управлять радиокнопками основываясь на данных, через v-model.

@change="answered($event)"

answered(e) {
  this.selectedAnswer = e.target.value;
  if (this.selectedAnswer == this.questions[this.idx].correctAnswer) {
    this.correctAnswers++;

Никаких событий слушать не надо, оформляем подсчёт результатов в виде вычисляемого свойства.

https://jsfiddle.net/0L9ayx1u/1/
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы