Design interactive Vue form with customized questions based on user response

I am looking to dynamically create a form with conditional fields. The structure of the form is stored in an object called Q. Below is an example of a Vue component that utilizes bootstrap-vue.

<template>
    <div>
        <div v-for="q of Q">
            <br/>
            <template v-if="q.type == 'input'">
                {{ q.question }}
                <em v-if="q.comment"><br />{{ q.comment }}</em>
                <b-form-input v-model="q.value" :type="q.subtype" :placeholder="q.placeholder"></b-form-input>
                Value: {{ q.value }}
            </template>

            <template v-if="q.type == 'radio'">
                {{ q.question }}
                <em v-if="q.comment"><br />{{ q.comment }}</em>
                <b-form-group>
                    <b-form-radio-group buttons
                                        stacked
                                        v-model="q.value"
                                        :options="q.options"/>
                </b-form-group>

                Value: {{ q.value }}
            </template>

        </div>
    </div>
</template>

<script>
    export default {
        name: 'Questionnaire',
        data() {
            return {
                locale: 'en',
                Q: [
                    {
                        name: 'age',
                        value: null,
                        question: 'How old are you?',
                        placeholder: 'Your age...',
                        comment: 'years since you were born',
                        type: 'input',
                        subtype: 'number',
                        range: [18, 99],
                    },
                    {
                        name: 'cq',
                        value: null,
                        question: 'Conditional Question?',
                        type: 'radio',
                        options: [
                            {text: 'Yes', value: '0'},
                            {text: 'No', value: '1'},
                        ],
                        if: [{object: 'age', largerthan: 30}],
                    },
               ]
           };
       },
       methods: {
           onChange: function(){
               alert('test');
           },
       },
   }
</script>

The goal is to display the "Conditional Question" only if the age > 30.

  • Accessing this.Q in the Q object is not possible as it does not exist at this point.
  • Using v-on:change="onChange" might work, but it goes against the Vue philosophy.

I am flexible with the structure of the object and plan to retrieve it through AJAX...

My question: Is there a method to monitor this.Q[0].value? or another way to make the second question available only if the first has a specific value?

Answer №1

To achieve the desired effect, I utilized the "v-if" directive on the second div within your template.

In addition, I initialized an empty array called "Q" and created a simulated AJAX request using setTimeout in the "created()" lifecycle hook.

<template>
  <div>
    <div v-if="!(q.if) || Q[0].value > q.if[0].largerthan" v-for="q of Q">
        <br/>
        <template v-if="q.type == 'input'">
            {{ q.question }}
            <em v-if="q.comment"><br />{{ q.comment }}</em>
            <b-form-input v-model="q.value" :type="q.subtype" :placeholder="q.placeholder"></b-form-input>
            Value: {{ q.value }}
        </template>

        <template v-if="q.type == 'radio'">
            {{ q.question }}
            <em v-if="q.comment"><br />{{ q.comment }}</em>
            <b-form-group>
                <b-form-radio-group buttons
                                    stacked
                                    v-model="q.value"
                                    :options="q.options"/>
            </b-form-group>

            Value: {{ q.value }}
        </template>

    </div>
  </div>
</template>

<script>
  export default {
    name: 'Questionnaire',
    data() {
        return {
            locale: 'en',
            Q: [],
        };
    },
    created() {
      setTimeout( _ => this.Q = [
                {
                    name: 'age',
                    value: null,
                    question: 'How old are you?',
                    placeholder: 'Your age...',
                    comment: 'years since you were born',
                    type: 'input',
                    subtype: 'number',
                    range: [18, 99],
                },
                {
                    name: 'cq',
                    value: null,
                    question: 'Conditional Question?',
                    type: 'radio',
                    options: [
                        {text: 'Yes', value: '0'},
                        {text: 'No', value: '1'},
                    ],
                    if: [{object: 'age', largerthan: 30}],
                },
                {
                    name: 'age',
                    value: null,
                    question: 'How old are you?',
                    placeholder: 'Your age...',
                    comment: 'years since you were born',
                    type: 'input',
                    subtype: 'number',
                    range: [18, 99],
                },
         ], 500)
    },
}

Answer №2

Per @Stephan-v's suggestion, I made the change from an array to a key:value object. The post by @Radovan-Šurlák demonstrates that there is no necessity for a watcher. It's important to note that a computed object can only be initialized in "beforeCreate" and method; variables are not yet set up. Transferring variables from beforeCreate to the object proves to be quite challenging.

Following the foundation laid out by @Radovan-Šurlák and enhancing it slightly yields:

<template>
    <div>
        <div v-for="(q, name) of Q" v-if="doShow( name )">
            <br/>
            <template v-if="q.type == 'input'">
                <b>{{ q.question }}</b>
                <em v-if="q.comment"><br/>{{ q.comment }}</em>
                <b-form-input v-model="q.value" :type="q.subtype" :placeholder="q.placeholder"></b-form-input>
            </template>

            <template v-if="q.type == 'radio'">
                <b>{{ q.question }}</b>
                <em v-if="q.comment"><br/>{{ q.comment }}</em>
                <b-form-group>
                    <b-form-radio-group buttons
                                        stacked
                                        v-model="q.value"
                                        :options="q.options"/>
                </b-form-group>
            </template>

        </div>
    </div>
</template>

<script>
    export default {
        name: 'Questionnaire',
        data() {
            return {
                locale: 'en',
                Q: {
                    age: {
                        value: null,
                        question: 'How old are you?',
                        placeholder: 'Your age...',
                        comment: 'years since you were born',
                        type: 'input',
                        subtype: 'number',
                        range: [18, 99],
                    },
                    cq: {
                        value: null,
                        question: 'Conditional Question?',
                        type: 'radio',
                        options: [
                            {text: 'Yes', value: '0'},
                            {text: 'No', value: '1'},
                        ],
                        if: [{object: 'age', largerthan: 30, smallerthan: 35, equals: 31, notequal: 32}],
                    },
                },
            };
        },
        methods: {
            doShow: function( field ) {
                for( var obj in this.Q[ field ].if )
                {
                    var ifObj = this.Q[ field ].if[ obj ];
                    if( ifObj.equals !== undefined && this.Q[ ifObj.object ].value != ifObj.equals )
                        return false;
                    if( ifObj.notequal !== undefined && this.Q[ ifObj.object ].value == ifObj.notequal )
                        return false;
                    if( ifObj.largerthan !== undefined && this.Q[ ifObj.object ].value <= ifObj.largerthan )
                        return false;
                    if( ifObj.smallerthan !== undefined && this.Q[ ifObj.object ].value >= ifObj.smallerthan )
                        return false;
                }
                return true;
            },
            submit: function () {
                console.log('Submit form, send back data via Axios')
            },
        },
        mounted() {
            // Axios call
        }
    }
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

In JavaScript, merging objects will exclusively result in an identifier being returned

When working with mongoose, I have encountered an issue where combining data from multiple finds only displays the id instead of the entire object. Interestingly, when I use console.log() on the object directly, it shows all the contents. Below are snippe ...

Using a Hook inside a React function is not possible

I encountered an error message known as the "invalid hook call error" while using the useEffect hook in my code. According to the documentation, this issue usually arises due to either a version mismatch or incorrect function calls. Could someone kindly r ...

What is the option for receiving automatic suggestions while formatting components in a react.js file?

When styling elements in our app using app.css or styles.css, VS Code provides auto-suggestions. For example, if we type just "back" for background color, it will suggest completions. However, this feature does not work in react.js or index.js. Why is that ...

Problem arising from animation not commencing at expected starting point

Creating a feature on an app that involves breathing exercises using CSS animations. The challenge I'm facing is ensuring the words "exhale" and "inhale" appear synchronously with the animation of a circle shrinking and enlarging. However, the animati ...

Component fails to re-render after token refresh on subsequent requests

Here is my axios-hoook.js file, utilizing the axios-hooks package. import useAxios from 'axios-hooks'; import axios from 'axios'; import LocalStorageService from './services/local-storage.service'; import refreshToken from &ap ...

Minimizing conditional statements in my JavaScript code

I've just completed the development of a slider and am currently working on optimizing some repetitive if/else statements in the code. The purpose of these conditions is to determine whether the slider has reached the last slide, in which case the nex ...

Display time series data from PHP by utilizing Flot Charts in jQuery

After receiving data from a database, which is formatted using PHP and returned as a JSON response for an Ajax call, I encountered an issue. Everything works fine and the data is plotted except when the X-Axis contains dates, in which case nothing gets plo ...

Preventing Users from Accessing NodeJS Express Routes: A Guide

Currently, I am working on a React application where I am utilizing Express to handle database queries and other functions. When trying to retrieve data for a specific user through the express routes like so: app.get("/u/:id", function(req, res) { ...

Obtain the textfield whenever the user desires

I have added an image upload file field in the form. If the user wants a multi-select field, I want to allow the user to choose the number of files they want to upload. Take a look at my text field below: <div class="form-group col-lg-10"> {! ...

What is the best way to utilize window.find for adjusting CSS styles?

Incorporating both AJAX and PHP technologies, I have placed specific text data within a span element located at the bottom of my webpage. Now, my objective is to search this text for a given string. The page consists of multiple checkboxes, with each check ...

Having trouble getting card animations to slide down using React Spring

I am currently learning React and attempting to create a slide-down animation for my div element using react-spring. However, I am facing an issue where the slide-down effect is not functioning as expected even though I followed a tutorial for implementati ...

Attempting to incorporate Font-Awesome Icons into the navigation bar tabs

As a newcomer to React, I've been attempting to incorporate Font Awesome icons into my secondary navigation bar. Despite using switch-case statements to iterate through each element, all the icons ended up looking the same, indicating that only the de ...

Changing Background Color on Div Click

After spending a considerable amount of time on this, I find myself getting confused and stuck. It seems like I might be overlooking something crucial. Essentially, my code is designed to have the default div background (gamebg), and upon clicking one of t ...

What advantages does NextJS offer that set it apart from other frameworks that also provide Server Side Render solutions?

I'm diving into the world of NextJS and as I explore this topic, one burning question arises: "What is the necessity of utilizing NextJS?" From what I understand, NextJS excels in rendering pages from the server and is heavily reliant on ReactJS. Howe ...

What is the best way to select a cell within the same row using jQuery?

I've successfully implemented a table with a single input field and an AJAX script that triggers when the input field value is changed. Everything is functioning as expected. However, I now face the challenge of adding a dynamic date insertion feature ...

Creating a text file in Node.js using nodepad formatting

Could anyone assist me with formatting text in a Node.js file to be written to Notepad? Here's the code I'm currently using: const fs = require('fs'); fs.writeFile('write.txt', '', err => {}); var text = [ ...

Encountered an Error: Trying to use a function that is undefined - While utilizing Jquery Tabs

Working on implementing Jquery Tabs using the "description" and "reviews" li tags as tabs. Testing it out here . Everything seems to be functioning correctly here Key Points: This is Wordpress Multi-Site setup. The issue occurs in certain folders or "si ...

Click event triggers nested bootstrap collapse

As a beginner in bootstraps and coding, I am currently experimenting with opening the main bootstrap panel using an onclick event that includes nested sub panels. Here is my index.html file containing the panels and the button; <link href="https://m ...

Invoking an *.aspx method from an external HTML file

Greetings, I am a newcomer to the world of web application development. I have successfully created an *aspx page that communicates with a webservice through a method returning a string. My next goal is to invoke this aspx method from a remote HTML 5 page ...

Click the button in Javascript to add new content

I am currently experimenting with JavaScript to dynamically add new content upon clicking a button. Although I have successfully implemented the JavaScript code to work when the button is clicked once, I would like it to produce a new 'hello world&ap ...