基于Vue.js+Bootstrap实现带嵌套问题的动态调研表单求助
Hey there! Let's tackle your two problems one by one with practical Vue + Bootstrap + Kendo UI solutions that fit your dynamic survey form needs.
1. Implement Input/Date Picker Toggle & Build the Question Array with v-for Logic
First, we'll structure a flexible question array that defines both main Yes/No questions and their nested conditional fields. This array will drive the entire form via v-for, and we'll use conditional rendering to show/hide nested inputs based on the main question's answer.
Step 1: Define the Question Structure in Vue Data
Each question object will include:
id: Unique identifier for tracking responsestext: The question labeltype: Fixed asyesnofor main questionsnested: Array of conditional sub-questions (with their owntype,showOnrule, and label)
data() { return { surveyResponses: {}, // Stores all form responses (main + nested) questions: [ { id: "q1", text: "Question 1: Have you completed onboarding?", type: "yesno", nested: [ { id: "q1_1", text: "Please provide your onboarding completion date:", type: "date", showOn: "yes" // Show only if main question is answered "Yes" } ] }, { id: "q2", text: "Question 2: Do you need to submit a text explanation?", type: "yesno", nested: [ { id: "q2_1", text: "Enter your explanation:", type: "input", showOn: "yes" }, { id: "q2_2", text: "Enter your reference ID:", type: "input", showOn: "no" // Show only if main question is answered "No" } ] } ] }; }
Step 2: Render the Form with v-for & Conditional Toggles
Use Bootstrap for styling, v-for to loop through questions, and conditional directives (v-if) to show/hide nested fields based on the main Yes/No selection. For Kendo UI's date picker, we'll directly use its Vue component.
<template> <div class="container mt-5"> <!-- Loop through main questions --> <div v-for="question in questions" :key="question.id" class="mb-4"> <div class="card"> <div class="card-body"> <h5 class="card-title">{{ question.text }}</h5> <!-- Yes/No Radio Buttons --> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" :id="`${question.id}_yes`" :value="'yes'" v-model="surveyResponses[question.id]" /> <label class="form-check-label" :for="`${question.id}_yes`">Yes</label> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" :id="`${question.id}_no`" :value="'no'" v-model="surveyResponses[question.id]" /> <label class="form-check-label" :for="`${question.id}_no`">No</label> </div> </div> </div> <!-- Render nested questions conditionally --> <div v-for="nestedQ in question.nested" :key="nestedQ.id" class="ml-5 mt-3" v-if="surveyResponses[question.id] === nestedQ.showOn" > <div class="form-group"> <label>{{ nestedQ.text }}</label> <!-- Text Input --> <input v-if="nestedQ.type === 'input'" class="form-control" v-model="surveyResponses[nestedQ.id]" /> <!-- Kendo UI Date Picker --> <kendo-datepicker v-else-if="nestedQ.type === 'date'" v-model="surveyResponses[nestedQ.id]" format="yyyy-MM-dd" ></kendo-datepicker> <!-- Dropdown Example (if needed) --> <select v-else-if="nestedQ.type === 'select'" class="form-control" v-model="surveyResponses[nestedQ.id]" > <option value="">Select an option</option> <option value="option1">Option 1</option> <option value="option2">Option 2</option> </select> </div> </div> </div> <!-- Preview All Responses (for testing) --> <div class="mt-5"> <h4>Current Responses:</h4> <pre class="bg-light p-3">{{ surveyResponses }}</pre> </div> </div> </template>
How This Works:
- The
surveyResponsesobject dynamically stores answers using question IDs as keys (e.g.,surveyResponses.q1 = "yes"). - Nested fields only render when the main question's answer matches the
showOnvalue in the nested question config. - We use
v-ifwith component types to toggle between input fields, Kendo date pickers, or dropdowns.
2. Bind All Radio Button Values to an Array/List
If you want a dedicated collection of just the main Yes/No radio responses (separate from nested fields), you can either:
Option 1: Use an Object (Simplest)
We already use surveyResponses to store radio values (e.g., surveyResponses.q1 = "yes"). To extract just radio answers, you can filter the object:
computed: { radioAnswers() { return Object.fromEntries( Object.entries(this.surveyResponses).filter(([key]) => this.questions.some(q => q.id === key) ) ); } }
Option 2: Use an Array (For Ordered Responses)
If you prefer an array of objects with question IDs and values, update your data and add a method to sync selections:
data() { return { radioAnswers: [], // Stores [{ qid: "q1", value: "yes" }, ...] // ... rest of your data }; }, methods: { updateRadioAnswer(qid, value) { // Find existing entry for the question const existingEntry = this.radioAnswers.find(item => item.qid === qid); if (existingEntry) { existingEntry.value = value; } else { this.radioAnswers.push({ qid, value }); } } }
Then update the radio button bindings to use the @change event instead of v-model:
<input class="form-check-input" type="radio" :id="`${question.id}_yes`" @change="updateRadioAnswer(question.id, 'yes')" /> <input class="form-check-input" type="radio" :id="`${question.id}_no`" @change="updateRadioAnswer(question.id, 'no')" />
Bonus: Add Input Validation
To implement validation via input props (as you mentioned), you can use Bootstrap's form validation classes alongside Vue's reactivity. For example, mark a nested input as required only when it's visible:
<input v-if="nestedQ.type === 'input'" class="form-control" v-model="surveyResponses[nestedQ.id]" :required="surveyResponses[question.id] === nestedQ.showOn" />
You can also integrate Vue's built-in validation or libraries like VeeValidate for more complex rules tied to your question config.
内容的提问来源于stack exchange,提问作者Rasik

