Vuejs watchEffect Filtering and Searching Data

watchEffect automatically filters the user list based on the search query, great for real-time search scenarios.
import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const searchQuery = ref('');
    const users = ref([
      { name: 'Alice', age: 25 },
      { name: 'Bob', age: 30 },
      { name: 'Charlie', age: 35 },
    ]);
    const filteredUsers = ref([]);

    watchEffect(() => {
      if (searchQuery.value) {
        filteredUsers.value = users.value.filter(user =>
          user.name.toLowerCase().includes(searchQuery.value.toLowerCase())
        );
      } else {
        filteredUsers.value = users.value;
      }
    });

    return { searchQuery, filteredUsers };
  }
};

Vuejs Watching Window Resize Event

Use watch to respond to changes in window size, which can be useful for making responsive components.
<template>
  <div>
    <p>Window Width: {{ windowWidth }}</p>
    <p v-if="isMobile">You are on a mobile device!</p>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted, watch } from 'vue';

export default {
  setup() {
    const windowWidth = ref(window.innerWidth);
    const isMobile = ref(windowWidth.value < 768);

    const updateWidth = () => {
      windowWidth.value = window.innerWidth;
    };

    watch(windowWidth, (newWidth) => {
      isMobile.value = newWidth < 768;
    });

    onMounted(() => {
      window.addEventListener('resize', updateWidth);
    });

    onUnmounted(() => {
      window.removeEventListener('resize', updateWidth);
    });

    return { windowWidth, isMobile };
  },
};
</script>

Vuejs Watch to Trigger API

Use watch to trigger API calls when a value changes under specific conditions.
<template>
  <div>
    <input v-model="username" placeholder="Enter username" />
    <p v-if="error">{{ error }}</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';
import axios from 'axios';

export default {
  setup() {
    const username = ref('');
    const error = ref('');

    // Watch the username and make an API call when it changes
    watch(username, async (newUsername) => {
      if (newUsername.length >= 3) {
        try {
          const response = await axios.get(`/api/check-username?username=${newUsername}`);
          if (!response.data.available) {
            error.value = 'Username is already taken';
          } else {
            error.value = '';
          }
        } catch (err) {
          error.value = 'Error checking username';
        }
      } else {
        error.value = 'Username must be at least 3 characters long';
      }
    });

    return { username, error };
  },
};
</script>

Vuejs Watching Route Changes

use watch to monitor changes to the current route and react accordingly (e.g., fetch new data when a different page is navigated to).
<script>
import { watch } from 'vue';
import { useRoute } from 'vue-router';

export default {
  setup() {
    const route = useRoute();

    // Watch for changes in the route params or query
    watch(route, (newRoute, oldRoute) => {
      console.log('Route changed:', newRoute.path);
      // trigger a function or fetch data based on the new route
    });

    return {};
  },
};
</script>

Vuejs Watching Form Input Validation

use watch to monitor form input fields and validate them in real-time.
<template>
  <div>
    <label>Email:</label>
    <input v-model="email" type="email" />
    <span v-if="emailError">{{ emailError }}</span>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const email = ref('');
    const emailError = ref('');

    // Watch email input for changes and validate it
    watch(email, (newEmail) => {
      const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailPattern.test(newEmail)) {
        emailError.value = 'Invalid email format';
      } else {
        emailError.value = '';
      }
    });

    return { email, emailError };
  },
};
</script>

Vuejs Axios Call Api

The best practices for API calls in Vue.js 3 Composition API.
// src/services/apiService.js
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  timeout: 1000,
  headers: { 'Content-Type': 'application/json' },
});

// Define your API methods
export default {
  getUsers() {
    return apiClient.get('/users');
  },
  getUser(id) {
    return apiClient.get(`/users/${id}`);
  },
};


// src/composables/useFetchUsers.js
import { ref } from 'vue';
import apiService from '../services/apiService';

export function useFetchUsers() {
  const data = ref(null);
  const loading = ref(false);
  const error = ref(null);

  const fetchUsers = async () => {
    loading.value = true;
    try {
      const response = await apiService.getUsers();
      data.value = response.data;
    } catch (err) {
      error.value = 'Error fetching users';
    } finally {
      loading.value = false;
    }
  };

  return { data, loading, error, fetchUsers };
}


// src/components/UsersComponent.vue
<template>
  <div>
    <button @click="fetchUsers">Load Users</button>
    <div v-if="loading">Loading...</div>
    <ul v-if="data">
      <li v-for="user in data" :key="user.id">{{ user.name }}</li>
    </ul>
    <div v-if="error">{{ error }}</div>
  </div>
</template>

<script>
import { onMounted } from 'vue';
import { useFetchUsers } from '../composables/useFetchUsers';

export default {
  setup() {
    const { data, loading, error, fetchUsers } = useFetchUsers();
    
    onMounted(() => fetchUsers()); // Fetch users when the component is mounted

    return { data, loading, error, fetchUsers };
  },
};
</script>

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import UsersComponent from './components/UsersComponent.vue';

createApp(App)
  .component('UsersComponent', UsersComponent)
  .mount('#app');
src/
├── composables/
│   └── useFetchUsers.js   # Composable for fetching users
├── services/
│   └── apiService.js      # Centralized API service layer
├── components/
│   └── UsersComponent.vue # Component using the API and composable
└── main.js                # Main entry point for Vue app

Vuejs Manage Document Title

Update the document’s title with reactive data.
<script>
new Vue({
  el: '#app',
  data: () => ({
    documentTitle: document.title
  }),
  watch: {
    documentTitle: {
      immediate: true,
      handler (value) {
        document.title = value
      }
    }
  }
})
</script>

Since the root Vue instance is almost always initialized on a child element of <body>, we don’t have access to elements in <head>. The solution is actually very simple: to watch a data attribute that corresponds to the document title, and then use DOM functions for updating.

This is only a proof of concept. If you want full power over a document’s meta with SSR support, take a look at vue-meta.

Vuejs Methods to computed properties

When you find yourself in need of using an argument for a computed property, consider creating and moving the logic into a child component.
<!-- BEFORE -->
<template>
  <section class="posts">
    <article v-for="post in posts">
      <a :href="getUrl(post)">{{ getTitle(post) }}</a>
    </article>
  </section>
</template>

<script>
export default {
  methods: {
    getUrl (post) { ... },
    getTitle (post) { ... }
  }
}
</script>


<!-- AFTER -->
<!-- 1. PostList.vue -->
<template>
  <section class="posts">
    <PostItem v-for="post in posts" :post="post"/>
  </section>
</template>

<!-- 2. PostItem.vue -->
<template>
  <article v-for="post in posts">
    <a :href="url">{{ title }}</a>
  </article>
</template>

<script>
export default {
  props: ['post'],
  computed: {
    url () { /* compute on this.post */ },
    title () { /* compute on this.post */ }
  }
}
</script>

This results in simpler and decoupled components, structured and moduralized code, plus you have the benefits of computed property’s caching for free.

Vuejs Long press directive

A simple directive to execute a function when the element is long-pressed.
<template>
  <button v-longpress='showOptions'>Hold me for a while</button>
</template>

<script>
const PRESS_TIMEOUT = 1000

Vue.directive('longpress', {
  bind: function (el, { value }, vNode) {
    if (typeof value !== 'function') {
      console.warn(`Expect a function, got ${value}`)
      return
    }

    let pressTimer = null

    const start = e => {
      if (e.type === 'click' && e.button !== 0) {
        return;
      }

      if (pressTimer === null) {
        pressTimer = setTimeout(() => value(e), PRESS_TIMEOUT)
      }
    }

    const cancel = () => {
      if (pressTimer !== null) {
        clearTimeout(pressTimer)
        pressTimer = null
      }
    }

    ;['mousedown', 'touchstart'].forEach(e => el.addEventListener(e, start))
    ;['click', 'mouseout', 'touchend', 'touchcancel'].forEach(e => el.addEventListener(e, cancel))
  }
})
</script>

A more in-depth article with commented code can be found here.