Krishna Bhandari

Sometimes we overcomplicate things

How I overthink something and made the complex implementation and later realized that we can handle it in a better and simpler way. I was dealing with the form using Vue 3 and that form needs to be repeated once we click the add new button. First, I thought of creating a parent component called Question and inside that, it will consist of a QuestionItem component which we can use to loop for the repeater. Here I am using the concept of props, events, and watchers. Let’s see how this will work.

// Question.vue
<script setup>
import { ref } from "vue";

const questions = ref([{ text: "", type: "" }]);

const addQuestion = () => {
  questions.value.push({ text: "", type: "" });
};

const updateQuestion = (index, updatedQuestion) => {
    questions.value[index] = updatedQuestion;
    console.log('questions', questions.value);
};

const handleSubmit = () => {
    // perform axios call with all the questions we have inside of questions.value
};
</script>

<template>
  <main>
    <QuestionItem
      v-for="(question, index) in questions"
      :modelValue="question"
      @update:modelValue="updateQuestion(index, $event)"
    />

    <button @click.prevent="addQuestion">Add Question</button>
    <button @click.prevent="handleSubmit">Submit</button>
  </main>
</template>

Here, I am creating a questions property to store all the questions. addQuestion will show a new form as a row, and updateQuestion will apply the changes that are made on the child component called QuestionItem. Here is the code inside the QuestionItem file.

// QuestionItem.vue
<script setup>
import { ref, watch } from 'vue'

const props = defineProps({
    modelValue: Object,
});

const question = ref(props.modelValue.text || '');
const type = ref(props.modelValue.type || '');

const emit = defineEmits(['update:modelValue']);

watch(question, (newVal) => {
    emit('update:modelValue', { ...props.modelValue, text: newVal });
});

watch(type, (newVal) => {
    emit('update:modelValue', { ...props.modelValue, type: newVal });
});
</script>

<template>
    <h3>Question</h3>
    <input type="text" v-model="question" />
    <input type="text" v-model="type" />
</template>

Now, this was working as expected. We can add multiple questions with their types and once we click the submit button, we will have all the questions that are entered. But, wait! I was not happy with this implementation and later realized that we can make this very simple.

— Simple & Better Approach

Note: I was using the add question button and final submit button inside the parent component.

Now, let’s create different components. Let’s say one is Question and another is QuestionForm. Let’s see the code inside the Question component.

// Question.vue
<script setup>
import QuestionForm from "./QuestionForm.vue";
import { ref } from "vue";
</script>

<template>
  <main>
    <QuestionForm />

    // other stuff
  </main>
</template>

Here, we are just importing and using that QuestionForm component. Let’s go inside the QuestionForm component to see the code.

// QuestionForm.vue
<script setup>
import { ref } from "vue";

const questions = ref([{ text: "", type: "" }]);

const addQuestion = () => {
  questions.value.push({ text: "", type: "" });
};

const handleSubmit = () => {
    // perform axios request with all the questions we have.
    console.log(questions.value);
};
</script>

<template>
  <div v-for="(question, index) in questions">
    <h3>Question</h3>
    <input type="text" v-model="question.text" />
    <input type="text" v-model="question.type" />
  </div>
  <button @click.prevent="addQuestion">Add Question</button>
  <button @click.prevent="handleSubmit">Submit</button>
</template>

Here, we are not introducing anything like watchers, props, and emitted events. We are just integrating that question repeater part inside the QuestionForm component itself. And, this is also working as expected, which is a much simpler and easier implementation than the previous one. By just rethinking, questioning our own implementation, and looking at things differently, we can come up with better solutions. I hope you enjoyed this article. Thank you for reading to the end :)