[Vuejs]-Vuejs – add different classes in elements generated with v-for

1👍

There’s a lot of unknowns about the rest of your code (how the questions are handled and switched through, etc.), but here’s a working example for a single question. So you’ll have to adapt this for having multiple questions in your app, but it should push you in the right direction. I used an inline :style attribute in addition to the static styles already present on the <li>, but you could move that to a function as suggeted in Peter’s answer, if you prefer.

const app = {
  name: 'Survey',
  data() {
    return {
      n: 0,
      questions: [],
      answeredQuestions: [],
      item: {
        questionIndex: 1,
        choices: ['Lorem', 'Ipsum']
      },
      selectedChoice: null
    }
  },
  mounted() {

  },
  computed: {
    questions() {
      return this.$store.getters.survey;
    },
  },
  methods: {
    showNext() {
      if (this.n < this.questions.length) {
        this.n++
      }
    },
    isAnswered(index) {
      return this.n !== index ? 'hide' : '';
    },
    checkAnswer(questionIndex, choice) {
      this.answeredQuestions.push(choice);
      this.showNext();
    }
  }
};
Vue.createApp(app).mount('#app');
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<script src="https://unpkg.com/vue@3.0.11/dist/vue.global.prod.js"></script>
<div id="app">
  <ul class="list-group list-group-flush">
    <li class="list-group-item list-group-item-action" :class="{disabled: answeredQuestions.length, active: answeredQuestions.includes(index)}" v-for="(choice, index) in item.choices" :key="index" @click.prevent="checkAnswer(item.questionIndex, index)">
      <small class="">{{ index }}) {{ choice }}</small>
    </li>
  </ul>
</div>

1👍

You search the internet for vue class binding and it’s the first result that pops up:
https://v2.vuejs.org/v2/guide/class-and-style.html

You can use an plain object, object from your data, a function returning an object or simply a string. You can make any attribute dynamic with v-bind:, or simply :.
Your checkAnswer() function can cause a change in classes by manipulating something in data, for example.

See tutorial above for example code. Keep in mind v-bind:class is the same as :class.

The "best way" changes like every week in Vue, just find a way to do it and learn its advantages and disadvantages.

An example would be:

template: let a function generate the classes

<small
  :class="getChoiceClasses(item, choice, index)"
  @click.prevent="checkAnswer(item.questionIndex, index)"
>{{ index }}) {{ choice }}</small>

script: add method

getChoiceClasses(item, choice, index) {
  let classes = {
    active: choice == 1, // for example
    disabled: false, // default
    even: index % 2 == 0
  };

  if (whateverYouNeedToCheck) {
    classes.disabled = true;
  }
  
  return classes;
}

A method is a little slower than a value from data, but it’s very minor and only becomes a problem when you have 100s of calls.

1👍

If I understand correctly your situation and what you intend to do here I would suggest using the item in the checkAnswer method so that an identifier is used to set a computed property to the current item.questionIndex.

Then you bind the class of each element with a ternary operator condition to check the questionIndex and return the proper classes string: <small :class="questionIndex == item.questionIndex ? ‘disabled active’:’disabled’" …

Leave a comment