Vue

Lesson 05

Form v-model boundaries

Keep form drafts local and emit submitted values instead of writing through props while the user types.

Good Code

ReviewTitleForm.vue
<script setup lang="ts">
import { ref } from "vue";

const props = defineProps<{ initialTitle: string }>();
const emit = defineEmits<{ save: [title: string] }>();

// The draft is local until the user submits the form.
const draftTitle = ref(props.initialTitle);
</script>

<template>
  <form @submit.prevent="emit('save', draftTitle.trim())">
    <label>
      Review title
      <input v-model="draftTitle">
    </label>
    <button type="submit">Save</button>
  </form>
</template>

Bad Code

ReviewTitleForm.vue
<script setup>
const props = defineProps(["review"]);

// Typing writes through the prop before the user saves.
</script>

<template>
  <form>
    <input v-model="props.review.title">
    <button>Save</button>
  </form>
</template>

Review Notes

What to review

Good Code

The good version separates draft input from the saved value. The parent receives a single save event with the trimmed title.

Bad Code

The bad version writes to a prop object as the user types. Cancel, validation, and optimistic update behavior become harder to reason about.

Takeaways

  • A form component should own draft input state and send a committed value back through an event.