Component Communication

Back

Loading concept...

📡 Component Communication in Vue.js

The Art of Components Talking to Each Other


🎭 The Walkie-Talkie Story

Imagine you have two friends with walkie-talkies. One friend is the Parent and the other is the Child.

  • The Parent can talk TO the child anytime (that’s called props)
  • The Child can only talk BACK when something happens (that’s called emitting events)

Think of it like a classroom:

  • Teacher (Parent) → gives instructions to students
  • Student (Child) → raises hand to tell teacher something happened

This is exactly how Vue components communicate!


🎯 What We’ll Learn

  1. Emitting Events - Child says “Hey Parent, something happened!”
  2. emit Function - The magic tool to send messages up
  3. Event Validation - Making sure messages are correct
  4. v-model on Components - Two-way radio communication
  5. Multiple v-model Bindings - Many radios at once
  6. v-model Arguments - Naming your radio channels
  7. Custom Modifier Creation - Special message filters

1️⃣ Emitting Events

What is it?

When a child component wants to tell its parent “Hey! Something happened!”, it emits an event.

Simple Example

Think of a doorbell. When you press it (child), it rings inside the house (parent).

<!-- ChildButton.vue -->
<template>
  <button @click="ringDoorbell">
    Ring Bell! 🔔
  </button>
</template>

<script setup>
const emit = defineEmits(['doorbell-rang'])

function ringDoorbell() {
  emit('doorbell-rang')
}
</script>
<!-- Parent.vue -->
<template>
  <ChildButton @doorbell-rang="answer" />
</template>

<script setup>
function answer() {
  console.log('Someone is at the door!')
}
</script>

How it works:

graph TD A["👶 Child: Button Clicked"] --> B["📤 emit doorbell-rang"] B --> C["👨 Parent: Listens with @doorbell-rang"] C --> D["✅ Parent runs answer function"]

2️⃣ The emit Function

What is it?

emit is your walkie-talkie. It sends messages from child to parent.

Basic Usage

<script setup>
// Step 1: Define what events you can send
const emit = defineEmits(['message-sent'])

// Step 2: Use emit to send
function sendMessage() {
  emit('message-sent', 'Hello Parent!')
}
</script>

Sending Data with Events

You can attach data to your message!

<!-- ScoreCounter.vue -->
<script setup>
const emit = defineEmits(['score-changed'])

function addPoints(points) {
  emit('score-changed', points)
}
</script>

<template>
  <button @click="addPoints(10)">
    +10 Points
  </button>
</template>
<!-- Parent.vue -->
<template>
  <ScoreCounter @score-changed="updateScore" />
  <p>Total: {{ total }}</p>
</template>

<script setup>
import { ref } from 'vue'
const total = ref(0)

function updateScore(points) {
  total.value += points
}
</script>

Multiple Arguments

Send many pieces of info at once!

emit('user-registered', name, email, age)

3️⃣ Event Validation

What is it?

Making sure the messages you send are correct. Like a spell-checker for your walkie-talkie!

Why Validate?

  • Catch mistakes early
  • Know exactly what data to expect
  • Better debugging

How to Validate

<script setup>
const emit = defineEmits({
  // Simple: just name the event
  'click': null,

  // With validation: return true/false
  'submit': (email) => {
    if (!email) {
      console.warn('Email is required!')
      return false
    }
    if (!email.includes('@')) {
      console.warn('Invalid email!')
      return false
    }
    return true
  }
})
</script>

Real Example: Age Validator

<script setup>
const emit = defineEmits({
  'age-updated': (age) => {
    // Must be a number
    if (typeof age !== 'number') {
      console.warn('Age must be a number!')
      return false
    }
    // Must be positive
    if (age < 0 || age > 150) {
      console.warn('Age must be 0-150!')
      return false
    }
    return true
  }
})

function updateAge(newAge) {
  emit('age-updated', newAge)
}
</script>

Validation Flow

graph TD A["emit called"] --> B{Validation Function} B -->|returns true| C["✅ Event Sent"] B -->|returns false| D["⚠️ Warning in Console"] D --> E["Event Still Sent"]

Note: Validation warnings help during development but don’t stop the event!


4️⃣ v-model on Components

What is it?

A two-way street for data. Parent and child stay in sync automatically!

The Problem Without v-model

Normally, you need:

  1. A prop going DOWN ⬇️
  2. An event going UP ⬆️

That’s two things to manage!

The Magic of v-model

v-model does BOTH in one line!

How Components Use v-model

Step 1: Child receives modelValue prop Step 2: Child emits update:modelValue event

<!-- CustomInput.vue -->
<template>
  <input
    :value="modelValue"
    @input="emit('update:modelValue',
      $event.target.value)"
  />
</template>

<script setup>
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<!-- Parent.vue -->
<template>
  <CustomInput v-model="username" />
  <p>Hello, {{ username }}!</p>
</template>

<script setup>
import { ref } from 'vue'
const username = ref('')
</script>

Visual Flow

graph TD A["Parent: username"] -->|modelValue prop| B["Child Input"] B -->|update:modelValue| A A --> C["Both stay in sync!"]

Cleaner with defineModel 🆕

Vue 3.4+ makes it even simpler:

<!-- CustomInput.vue -->
<template>
  <input v-model="model" />
</template>

<script setup>
const model = defineModel()
</script>

That’s it! No props or emit to write!


5️⃣ Multiple v-model Bindings

What is it?

Sometimes you need to sync MANY values. Like having multiple walkie-talkie channels!

Example: User Form

<!-- UserForm.vue -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
const email = defineModel('email')
</script>

<template>
  <input v-model="firstName"
    placeholder="First name" />
  <input v-model="lastName"
    placeholder="Last name" />
  <input v-model="email"
    placeholder="Email" />
</template>
<!-- Parent.vue -->
<template>
  <UserForm
    v-model:firstName="first"
    v-model:lastName="last"
    v-model:email="userEmail"
  />

  <p>{{ first }} {{ last }}</p>
  <p>📧 {{ userEmail }}</p>
</template>

<script setup>
import { ref } from 'vue'
const first = ref('')
const last = ref('')
const userEmail = ref('')
</script>

The Old Way (Still Works)

<script setup>
defineProps({
  firstName: String,
  lastName: String
})
const emit = defineEmits([
  'update:firstName',
  'update:lastName'
])
</script>

<template>
  <input
    :value="firstName"
    @input="emit('update:firstName',
      $event.target.value)"
  />
</template>

6️⃣ v-model Arguments

What is it?

Giving names to your v-model channels. Default is modelValue, but you can use any name!

Default vs Named

<!-- Default (no argument) -->
<CustomInput v-model="text" />
<!-- equals: v-model:modelValue="text" -->

<!-- Named argument -->
<CustomInput v-model:title="pageTitle" />

Child Component with Named v-model

<!-- BookEditor.vue -->
<script setup>
const title = defineModel('title')
const author = defineModel('author')
</script>

<template>
  <div>
    <label>Title:</label>
    <input v-model="title" />

    <label>Author:</label>
    <input v-model="author" />
  </div>
</template>
<!-- Parent.vue -->
<template>
  <BookEditor
    v-model:title="book.title"
    v-model:author="book.author"
  />
</template>

Why Use Arguments?

  • Clarity: Know exactly what each v-model controls
  • Multiple bindings: Each has its own name
  • Self-documenting: Code explains itself

7️⃣ Custom Modifier Creation

What is it?

Modifiers are like filters for your data. You already know some:

  • .trim - removes extra spaces
  • .number - converts to number
  • .lazy - updates on blur, not input

Now you can make YOUR OWN!

How Modifiers Work

When you write v-model.capitalize="text":

  • Vue passes modelModifiers: { capitalize: true }
  • Your component checks for this and transforms data

Example: Capitalize Modifier

<!-- CustomInput.vue -->
<script setup>
const model = defineModel({
  set(value) {
    if (props.modelModifiers?.capitalize) {
      return value.charAt(0).toUpperCase()
        + value.slice(1)
    }
    return value
  }
})

const props = defineProps({
  modelModifiers: { default: () => ({}) }
})
</script>

<template>
  <input v-model="model" />
</template>
<!-- Parent.vue -->
<template>
  <CustomInput v-model.capitalize="name" />
  <!-- Types "john" → becomes "John" -->
</template>

Multiple Modifiers

<CustomInput v-model.trim.capitalize="name" />
<script setup>
const model = defineModel({
  set(value) {
    let result = value

    if (props.modelModifiers?.trim) {
      result = result.trim()
    }
    if (props.modelModifiers?.capitalize) {
      result = result.charAt(0).toUpperCase()
        + result.slice(1)
    }

    return result
  }
})
</script>

Modifiers with Arguments

For named v-models like v-model:title.capitalize:

<script setup>
const title = defineModel('title', {
  set(value) {
    if (props.titleModifiers?.capitalize) {
      return value.toUpperCase()
    }
    return value
  }
})

const props = defineProps({
  titleModifiers: { default: () => ({}) }
})
</script>

🎯 Quick Reference

Concept Purpose Key Syntax
Emitting Events Child → Parent messages emit('event-name')
emit Function Send events up defineEmits([...])
Validation Check event data defineEmits({ event: validator })
v-model Two-way binding defineModel()
Multiple v-model Many bindings v-model:name="val"
Arguments Named channels defineModel('name')
Modifiers Transform data modelModifiers prop

🏆 You Did It!

You now understand how Vue components talk to each other:

  1. Events go UP (child to parent)
  2. Props go DOWN (parent to child)
  3. v-model makes two-way binding easy
  4. Modifiers let you transform data

Remember: Components are like a family. Parents give instructions (props), children report back (emit), and sometimes they need two-way conversations (v-model)!

Happy coding! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.