[Vuejs]-Conditional Styling of Radio Buttons not working(VueJs)

0👍

There are a few issues here. Firstly, with a radio list you need to bind v-model to one underlying variable so Vue selects the correct option, in your example you are binding your radio buttons to an array index, but you need to do:

<input type="radio" value="1" v-model="selected">
<input type="radio" value="2" v-model="selected">

The problem is that you have given each of your radio buttons a value of either true or null based on their correct value, so you need to set the value to something unique, such as index. Also, because you are going to be reusing this piece of code you should wrap it in it’s own component and pass the question as a prop so:

Vue.component('question', {
  template: '#question',
  props: ['question', 'number'],
  methods: {
    hideOption: function() {
      this.disabled = true;
    }
  },
  data() {
    return {
      selected: ''
    }
  }
})

Then your markup would be (I’ve removed some properties for clarity):

<template id="question">
  <div>
    <h5>{{question.instruction}} {{selected}}</h5>
    <h5>{{number}}. {{question.text}}</h5>
    <ul >
      <li v-for="(option, optionindex) in question.options">
        <div :class="{correct: option['correct'],  incorrect: !option['correct']}">
          <input type="radio" @click="hideOption" :value="optionindex" :name="number" v-model="selected">
          <label>{{option.text}}</label>
        </div>
      </li>
    </ul>
  </div>
</template>

Then in you main Vue instance you can do:

<div id="app">
  <div class="col-sm-8 gpquiz">
    <question :question="question" :number="index+1" :key="index" v-for="(question, index) in questions"></question>
  </div>
</div>

Here’s the JSFiddle for that: https://jsfiddle.net/8ocmun6s/

You now have a second problem, your user can select a correct or incorrect answer but your main Vue instance doesn’t know anything about what was selected, to do that we need to $emit that event back to the parent and we can do that by using a watcher to watch our selected value and $emit the event when it changes:

  watch: {
    selected(answer) {
      let correct = this.question.options[answer].correct
      // Emit the question number and whether it was correct back to the parent
      this.$emit('user-selected', this.number, correct)
    }
  },

And, we can now listen to that on the component and trigger a method (in this case selectAnswer):

<question :question="question" :number="index+1" :key="index" @user-selected="selectAnswer" v-for="(question, index) in quiz.questions"></question>

Now we just need to add that method:

methods: {
  selectAnswer(number, correct) {
   // use $set here otherwise view won't detect the change
   this.$set(this.userResponses, number-1, correct)
  }
},

Now we have an array of true or false answers we can present the result, I’m just going to use a computed:

computed:{
  correctAnswers(){
    // Return the number of correct answers
    return this.userResponses.filter(x => x).length
  }
},

Now we can just show and hide that when the user submits the form, so we can add the following to methods:

submitAnswers() {
  this.submitted = true;
}

And also make sure you declare submitted in data, then you can bind that to the button and add the conditional to your markup, which ends up as:

<div id="app">
  <h1>{{quiz.title}}</h1>
  <hr>
  <div class="col-sm-8 gpquiz" v-if="!submitted">
    <question :question="question" :number="index+1" :key="index" @user-selected="selectAnswer" v-for="(question, index) in quiz.questions"></question>

    <button @click="submitAnswers">
      Submit
    </button>
  </div>
  <h4 v-else>
    You got {{correctAnswers}} out of {{userResponses.length}} Correct!
  </h4>
</div>

And here’s the final JSFiddle: https://jsfiddle.net/fvk4L326/

Leave a comment