Vue Watchers

Reacting to data changes in Vue

👁️ What are Watchers?

Watchers observe data changes and perform actions when specific properties change. They're perfect for asynchronous operations, API calls, or complex logic triggered by data updates.


<div id="app">
    <input v-model="question" placeholder="Ask a question">
    <p>{{ answer }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                question: '',
                answer: 'Ask a question!'
            }
        },
        watch: {
            question(newVal) {
                this.answer = 'Thinking...'
            }
        }
    }).mount('#app')
</script>
                                    

Watcher Features

🔍

Observe Changes

Watch specific data properties for changes

⏱️

Async Operations

Perfect for API calls and delays

📊

Deep Watching

Watch nested object properties

🎯

Immediate Execution

Run watcher immediately on mount

🔹 Basic Watcher

Watchers are defined in the watch option. They receive the new value and old value as parameters when the watched property changes.

<div id="app">
    <input v-model="message" placeholder="Type something">
    <p>Message: {{ message }}</p>
    <p>Character count: {{ charCount }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                message: '',
                charCount: 0
            }
        },
        watch: {
            message(newValue, oldValue) {
                console.log('Changed from:', oldValue, 'to:', newValue)
                this.charCount = newValue.length
            }
        }
    }).mount('#app')
</script>

Output:

Message:

Character count: 0

🔹 Watchers vs Computed Properties

While both react to data changes, they serve different purposes. Use computed for synchronous calculations and watchers for asynchronous or expensive operations.

When to Use Each:

  • Computed Properties: Synchronous calculations, derived data, template display
  • Watchers: Asynchronous operations, API calls, side effects, complex logic
<div id="app">
    <input v-model="searchTerm" placeholder="Search...">
    <p>{{ searchStatus }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                searchTerm: '',
                searchStatus: 'Ready to search'
            }
        },
        watch: {
            searchTerm(newTerm) {
                if (newTerm.length > 0) {
                    this.searchStatus = 'Searching...'
                    // Simulate API call
                    setTimeout(() => {
                        this.searchStatus = `Found results for "${newTerm}"`
                    }, 1000)
                } else {
                    this.searchStatus = 'Ready to search'
                }
            }
        }
    }).mount('#app')
</script>

🔹 Deep Watching Objects

By default, watchers only observe direct property changes. Use the deep option to watch nested properties inside objects.

<div id="app">
    <input v-model="user.name" placeholder="Name">
    <input v-model.number="user.age" type="number" placeholder="Age">
    <p>{{ changeLog }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                user: {
                    name: 'John',
                    age: 25
                },
                changeLog: 'No changes yet'
            }
        },
        watch: {
            user: {
                handler(newVal) {
                    this.changeLog = `User updated: ${newVal.name}, ${newVal.age}`
                },
                deep: true
            }
        }
    }).mount('#app')
</script>

Output:

No changes yet

🔹 Immediate Watchers

Use the immediate option to run the watcher immediately when the component is created, not just when the value changes.

<div id="app">
    <p>{{ status }}</p>
    <button @click="count++">Count: {{ count }}</button>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                count: 0,
                status: ''
            }
        },
        watch: {
            count: {
                handler(newVal) {
                    this.status = `Count is now: ${newVal}`
                },
                immediate: true  // Runs on mount
            }
        }
    }).mount('#app')
</script>

Output:

Count is now: 0

🔹 Practical Example: Form Validation

Watchers are great for real-time form validation with debouncing to avoid excessive checks:

<div id="app">
    <label>Username:</label>
    <input v-model="username" placeholder="Enter username">
    <p :style="{ color: validationColor }">{{ validationMessage }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                username: '',
                validationMessage: '',
                validationColor: 'black',
                timeout: null
            }
        },
        watch: {
            username(newVal) {
                // Clear previous timeout
                clearTimeout(this.timeout)
                
                if (newVal.length === 0) {
                    this.validationMessage = ''
                    return
                }
                
                this.validationMessage = 'Checking...'
                this.validationColor = 'orange'
                
                // Debounce validation
                this.timeout = setTimeout(() => {
                    if (newVal.length < 3) {
                        this.validationMessage = 'Too short (min 3 characters)'
                        this.validationColor = 'red'
                    } else if (newVal.length > 15) {
                        this.validationMessage = 'Too long (max 15 characters)'
                        this.validationColor = 'red'
                    } else {
                        this.validationMessage = 'Username available!'
                        this.validationColor = 'green'
                    }
                }, 500)
            }
        }
    }).mount('#app')
</script>

Output:

🔹 Watching Multiple Properties

You can watch multiple properties by defining separate watchers for each, or watch computed properties that depend on multiple values:

<div id="app">
    <input v-model.number="width" type="number" placeholder="Width">
    <input v-model.number="height" type="number" placeholder="Height">
    <p>Area: {{ area }}</p>
    <p>{{ sizeMessage }}</p>
</div>

<script src="https://unpkg.com/vue@3"></script>
<script>
    Vue.createApp({
        data() {
            return {
                width: 10,
                height: 10,
                sizeMessage: ''
            }
        },
        computed: {
            area() {
                return this.width * this.height
            }
        },
        watch: {
            area(newArea) {
                if (newArea < 50) {
                    this.sizeMessage = 'Small area'
                } else if (newArea < 200) {
                    this.sizeMessage = 'Medium area'
                } else {
                    this.sizeMessage = 'Large area'
                }
            }
        }
    }).mount('#app')
</script>

Output:

Area: 100

Medium area

🧠 Test Your Knowledge

When should you use watchers instead of computed properties?