1

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--;
    }

  }
})
4
  • Apparently, you'll need to provide a collection - an array of possible answers - if that's the case. Commented May 11, 2017 at 14:40
  • I don't see why I'll have to provide an array of possible answers? I only need to receive the answers provided by the user input regardless of the type. If invalid answers are attempted, I'll intercept them with validations. Commented May 11, 2017 at 14:53
  • Which answer to 'what is you gender' is invalid? Commented May 11, 2017 at 14:53
  • Radio buttons won't have invalid answers, cos checked is set to true, so the user can proceed to next without changing the currently selected radio button. At the moment there is only one, Male, but ideally there'd be two. I tried to create an array for the labels, but there is still only one radio button as input so they all show at the same. Commented May 11, 2017 at 14:57

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

This is exactly what I was trying to do, but couldn't make it work. I see now from your example where I got this wrong. Now, I have doubts, if I need both input number and radio buttons for a question, how I'd pass the values. e.g. Insert distance: (input number here) radio buttons: miles, yards, km. For the component, I'll put the template for the radio buttons, plus the input number field, but I'm not sure, how to obtain both of the inserted values. My fiddle jsfiddle.net/cgrwe0u8/13 .
@Pitagora You can emit a complex object. In this case, an object that has both the units and the value. jsfiddle.net/cgrwe0u8/14
@Pitagora You could also compose your combined question using the previously defined components. jsfiddle.net/cgrwe0u8/15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.