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

Unable to adjust the height of an MP4 video to properly display within a Material Box in both landscape and portrait orientations

While I have been learning JavaScript React, I encountered an issue with positioning an MP4 movie. You can view the code in this Codesandbox If you check out the FileContentRenderer.jsx file, you will see that the html5-video is used for the MP4. The g ...

Is there an issue with the JSON data?

"stocksdata" :[{"id":7,"SCRIP":"ASIANPAINT","LTP":3341,"OHL":"BUY","ORB15":"BREAKOUT","ORB30":"NT","PRB":"NA","CAMARILLA& ...

Building an HTTP request and response directly from an active socket in a Node.js environment

My current project involves passing HTTP requests to a child process in Node.js. I am struggling with passing the active Socket using child.send('socket', req.socket). Once inside the child process, I need to recreate the HTTP request and respons ...

Exploring Deeply Nested Routing in Angular

I've been exploring the use of multiple router outlets and encountered an issue. When using the navigateBy function of the router, I am unable to view my child route and encounter an error. However, when I access it via the routerLink in HTML, I get ...

PHP question about maintaining data continuously

So, I've created this interesting JavaScript 'thing' with the help of jQuery and AJAX. The main concept behind it is that a div can be edited (contenteditable=true), which sparked the idea to develop a chatroom-like feature. It's pretty ...

Having difficulty validating the field accurately with Angular.js

In order to validate the input field in accordance with the user's needs using AngularJS, I have shared my code below: <div ng-class="{ 'myError': billdata.longitude.$touched && billdata.longitude.$invalid }"> <input type ...

What could be causing the error "styled is not defined as a function" while creating my component library using Rollup?

Currently, I am facing an issue with my component library which is built using React, styled-components, framer-motion, Rollup, and Storybook. The library is being consumed by a NextJS website, but when trying to use it, I keep encountering the following e ...

When selecting an old list, only the most recently created ID is displayed, rather than the ID of the specific list being

I've set up a dashboard where I can create lists and have them displayed on the screen, but there seems to be an issue when trying to open any list as only the last created one opens. Can anyone point out what mistake I might be making? The technologi ...

Leveraging the useEffect hook in conjunction with the "async" keyword

Looking for a way to clean up react request in react hooks. I've heard about using AbortController in my hook, but I'm not sure how to implement it. I am currently working with next.js. What are some effective approaches to solve this issue? Also ...

The specified method is not recognized as a function in the ReactJS library

Within my reactJS application, there is a function that calls upon a helper function to retrieve the result of a calculation. The component looks like this: import React, { Component } from "react"; import * as d3 from "d3"; class DrawImage extends Comp ...

Looping through a sequence of asynchronous tasks, utilizing the async/await syntax, writing data to

I'm currently working on a script to download pdf files from a specified URL and save them to my local disk. However, I have encountered an issue where I need each file to be downloaded one at a time with a one-second delay in between (to ensure that ...

Is it true that document.execCommand only works with buttons and not links?

Below is the code snippet I am working with: <div contenteditable="true" style="height:100px;width:100px;border:1px solid; " class="editor"></div> <a class='bold' style="height:10px;width:10px;">B</a> $(function(){ ...

Locate the nearest index within the array

I'm currently working with an array of "events" where the key assigned to each event corresponds to the Unix Timestamp of that particular event. To illustrate, consider the following array of event objects in JS: var MyEventsArray=[]; MyEventsArray[1 ...

Enhance the functionality of Woocommerce email notifications by incorporating a customized VAT field

I have exhausted all options and tried various email hooks without success. I inherited an outdated PHP code that was developed by someone else, which I updated for new woocommerce hooks (since the code is 4 years old). Everything is functioning smoothly e ...

What is the process for linking an HTML document to another HTML document within a div using jQuery?

Check out my HTML code: <!DOCTYPE html> <html> <head> <title>Hilarious Jokes!</title> <meta charset="utf-8"> <link href="final%20project.css" rel="stylesheet"> <script src=" ...

JQuery selector is successfully working while vanilla JavaScript is not functioning as expected

This problem is unique because I am experiencing issues with querySelector and querySelectorAll in my JavaScript code. While JQuery works fine, vanilla JS does not work as expected. I would appreciate any insights on why this might be happening. Thank you ...

Vue3-compatible UML diagram collections

Looking for suggestions on UML diagram libraries that are compatible with Vue 3. Any recommendations? Thank you! ...

A guide on enabling or disabling a combobox in vuejs3

In VueJs 3, I am looking for a way to toggle the Disable/Enable status of a combo-box based on a specific property. Below is my code snippet: <template> <div class="combobox"> <label for={{selector_name}}> <p> ...

What is the correct way to adjust the style.top attribute of an element using JavaScript?

In order to correct a JavaScript source code, I need to adjust the style.top property of a div element based on an integer parameter from a function called index. Here is the current implementation: div.style.top = (index * 22 + 2)+"px"; However, for lar ...

Setting up a Node.js project in your local environment and making it

I need help installing my custom project globally so I can access it from anywhere in my computer using the command line. However, I've been struggling to make it work. I attempted the following command: npm install -g . and some others that I can&ap ...