#vue #javascript #frontend

Day 5: Computed Properties & Watchers

Welcome to Day 5! We have data, and we have inputs. Often, we need to display a derived version of that data (e.g., filtering a list, formatting a price).

Computed Properties

A Computed Property is a piece of state that depends on other state.

Why not just a function?

You could use a function like getFullName().

  • Function: Runs every time the component re-renders.
  • Computed: Cached. It only re-runs if its dependencies (the refs inside it) change.
<script setup lang="ts">
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

// Read-only computed
const fullName = computed(() => {
  return firstName.value + ' ' + lastName.value
})

// Use it just like a ref in template (no .value needed in template)
</script>

<template>
  <p>Full Name: {{ fullName }}</p>
</template>

Writable Computed

Rarely, you might need a โ€œsetterโ€.

const fullName = computed({
  get() { return firstName.value + ' ' + lastName.value },
  set(newValue) {
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})

Watchers

Computed properties are for calculating values. Watchers are for side effects (fetching data, changing the DOM manually, logging).

<script setup lang="ts">
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Ask a question...')

// Watch 'question' ref.
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      const json = await res.json()
      answer.value = json.answer
    } catch (e) {
      answer.value = 'Error! could not reach the API.'
    }
  }
})
</script>

<template>
  <p>Ask a Yes/No question:</p>
  <input v-model="question" />
  <p>{{ answer }}</p>
</template>

watchEffect

If you want the watcher to run immediately and automatically track dependencies without listing them:

import { watchEffect } from 'vue'

watchEffect(() => {
  console.log(`Current question is: ${question.value}`)
})

Challenge for Day 5

  1. Create a search input (text).
  2. Create a list of 5 items (e.g., fruits).
  3. Create a computed property filteredList that returns items matching the search.
  4. Display the filteredList.

Solution:

<script setup lang="ts">
import { ref, computed } from 'vue'

const search = ref('')
const fruits = ref(['Apple', 'Banana', 'Orange', 'Mango', 'Pear'])

const filteredFruits = computed(() => {
  return fruits.value.filter(fruit => 
    fruit.toLowerCase().includes(search.value.toLowerCase())
  )
})
</script>

<template>
  <input v-model="search" placeholder="Search fruits..." />
  <ul>
    <li v-for="fruit in filteredFruits" :key="fruit">{{ fruit }}</li>
  </ul>
</template>

Mastering computed is the key to clean Vue code. If your template looks complex, turn it into a computed property. Tomorrow, we peek under the hood at Lifecycle Hooks.