Pitagora
Pitagora

Reputation: 47

Vue.Js Multiple inputs with different types

I am working on a survey with Vue. I am using an array for all the questions and an index to navigate through them and display them one at a time. I'm using different input types for each question, e.g. number, radio, text etc. For some questions I'll need more than one input. I am using v-bind to pass the type of the question.

Now the problem that I encounter is that I'll need more than one input per question, e.g. when passing radio button I only get one when I need 2+. Same for labels for the buttons. I have also realized that I'm going to need two different input types for some questions (e.g. both input number and radio).

This is my working fiddle, to give you an idea of what I'm trying to accomplish. I would like to know if this is doable with my current approach, or if I need to use components for the questions and use different templates for each of them and how I would go about doing that.

I am writing this for the second time from memory since the first time I got an error, so I apologize if I failed to mention something important. Thanks in advance!

new Vue({
  el: '#quizz',
  data: {
    questions:[
        {question: 'What is your gender?', answer: '', type: 'radio', checked: 'true', label: 'Male'},
        {question:'How old are you?', answer: '', type: 'number', checked: 'false'},
      {question:'How many times do you workout per week?', answer: '', type: 'number', checked: 'false'},
    ],
    index:0
  },
  computed:{
    currentQuestion(){
        return this.questions[this.index]
    }
  },
  methods:{
    next(){
        if(this.index + 1 == this.questions.length)
        this.index = 0;
      else
        this.index++;

    },
    previous(){

        if(this.index - 1 < 0)
        this.index = this.questions.length - 1;
      else
        this.index--;
    }

  }
})

Upvotes: 1

Views: 2302

Answers (1)

Bert
Bert

Reputation: 82469

I would probably handle this by building question "type" components. For example,

const RadioQuestion = {
    props:["value", "question"],
    template:`
        <div>
            <template v-for="label in question.labels">
                <input type="radio" :id="label" :value="label" v-model="picked">
                <label :for="label">{{label}}</label>
                <br>        
            </template>
        </div>
    `,
    data(){
        return {
            picked: this.value
        }
    },
    watch:{
        picked(){
            this.$emit("input", this.picked)
        }
    }
}

const NumericInputQuestion = {
    props:["value", "question"],
    template:`
        <div>
            <input type="number" v-model="internalValue" @input="onInput" :value="value" />
        </div>
    `,
    data(){
        return {
            internalValue: this.value
        }
    },
    methods:{
        onInput(){this.$emit("input", this.internalValue)}
    }

}

Then build your data like this.

data: {
  questions:[
    {question: 'What is your gender?', type: RadioQuestion, labels:["Male", "Female"], answer: null},
    {question:'How old are you?', type: NumericInputQuestion, answer: null},
    {question:'How many times do you workout per week?', type: NumericInputQuestion, answer: null}
  ],
  index:0
}

Modify your template accordingly.

<div id="quizz" class="question">
    <h2>
        {{ currentQuestion.question }}
    </h2>
    <component :key="currentQuestion" :is="currentQuestion.type" :question="currentQuestion" v-model="currentQuestion.answer"></component>
    Current Question Answer: {{ currentQuestion.answer }}
    <div class='button' id='next'><a href='#' @click="next">Next</a></div>
    <div class='button' id='prev'><a href='#' @click="previous">Prev</a></div>
</div>

Here is an updated fiddle demonstrating the technique.

Upvotes: 1

Related Questions