DEV Community

Christopher Furman
Christopher Furman

Posted on

Streamline Vue 3 Form Management with vue-form-watchers

Have you ever found yourself writing repetitive watcher code for form fields in Vue? Or struggled with debouncing form updates to prevent API spam? Today, I'll introduce you to vue-form-watchers, a lightweight utility that makes form state management in Vue 3 a breeze.

The Problem

In typical Vue applications, managing form state often involves:

  • Setting up individual watchers for each form field
  • Implementing debouncing to prevent excessive API calls
  • Handling programmatic updates without triggering watchers
  • Adding new form fields and their corresponding watchers

This leads to boilerplate code that looks something like this:

const form = ref({
  username: '',
  email: '',
  age: 0
})

// Individual watchers for each field
watch(() => form.value.username, debounce((value) => {
  updateServer('username', value)
}, 500))

watch(() => form.value.email, debounce((value) => {
  updateServer('email', value)
}, 500))

watch(() => form.value.age, debounce((value) => {
  updateServer('age', value)
}, 500))
Enter fullscreen mode Exit fullscreen mode

Enter vue-form-watchers

vue-form-watchers solves these problems by providing a simple, declarative way to handle form state changes. Here's how to get started:

npm install vue-form-watchers
Enter fullscreen mode Exit fullscreen mode

Basic Usage

import { ref } from 'vue'
import { createFormWatchers } from 'vue-form-watchers'

const form = ref({
  username: '',
  email: '',
  age: 0
})

createFormWatchers(
  form.value,
  (key, value) => {
    console.log(`${key} changed to ${value}`)
    // Handle the update (e.g., API calls, validation)
  },
  {
    debounceTime: 300 // Optional: default is 500ms
  }
)
Enter fullscreen mode Exit fullscreen mode

That's it! The library automatically creates debounced watchers for all form fields.

Real-World Examples

1. Auto-Saving Draft Posts

Here's how you can implement an auto-saving draft system for a blog editor:

import { ref } from 'vue'
import { createFormWatchers } from 'vue-form-watchers'

export default {
  setup() {
    const draft = ref({
      title: '',
      content: '',
      tags: []
    })

    const { markUpdateAsExternal } = createFormWatchers(
      draft.value,
      async (key, value) => {
        try {
          await api.saveDraft({ [key]: value })
          toast.success('Draft saved')
        } catch (error) {
          toast.error('Failed to save draft')
        }
      },
      {
        debounceTime: 1000 // Wait for 1 second of inactivity
      }
    )

    // Load existing draft
    const loadDraft = async () => {
      const savedDraft = await api.getDraft()
      markUpdateAsExternal(() => {
        Object.assign(draft.value, savedDraft)
      })
    }

    return { draft, loadDraft }
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Real-Time Form Validation

Here's how to implement real-time validation with error messages:

import { ref } from 'vue'
import { createFormWatchers } from 'vue-form-watchers'

export default {
  setup() {
    const form = ref({
      email: '',
      password: '',
      confirmPassword: ''
    })

    const errors = ref({})

    createFormWatchers(
      form.value,
      async (key, value) => {
        errors.value[key] = await validateField(key, value, form.value)
      },
      {
        debounceTime: 300,
        immediate: true // Validate fields immediately
      }
    )

    async function validateField(key, value, formData) {
      switch (key) {
        case 'email':
          return !value.includes('@') ? 'Invalid email' : null
        case 'password':
          return value.length < 8 ? 'Password too short' : null
        case 'confirmPassword':
          return value !== formData.password ? 'Passwords do not match' : null
      }
    }

    return { form, errors }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Multi-Step Form with API Updates

For complex forms that span multiple steps:

import { ref } from 'vue'
import { createFormWatchers } from 'vue-form-watchers'

export default {
  setup() {
    const userForm = ref({
      // Step 1: Basic Info
      firstName: '',
      lastName: '',
      email: '',

      // Step 2: Address
      street: '',
      city: '',
      country: '',

      // Step 3: Preferences
      notifications: false,
      theme: 'light'
    })

    const { markUpdateAsExternal } = createFormWatchers(
      userForm.value,
      async (key, value, source) => {
        if (source === 'user') {
          await api.updateUserProfile({ [key]: value })
        }
      },
      {
        skipExternalUpdates: true,
        isExternalUpdate: (key, newValue, oldValue) => {
          // Custom logic to determine external updates
          return false
        }
      }
    )

    return { userForm }
  }
}
Enter fullscreen mode Exit fullscreen mode

Advanced Features

1. Handling External Updates

The library provides a markUpdateAsExternal utility for handling programmatic updates without triggering the update callback:

const { markUpdateAsExternal } = createFormWatchers(form.value, updateCallback)

// Later, when updating form programmatically:
markUpdateAsExternal(() => {
  form.value.username = 'John'
  form.value.email = 'john@example.com'
})
Enter fullscreen mode Exit fullscreen mode

2. Debug Mode

Enable debug mode during development to get detailed logs:

createFormWatchers(form.value, updateCallback, {
  debug: process.env.NODE_ENV === 'development'
})
Enter fullscreen mode Exit fullscreen mode

Why Use vue-form-watchers?

  1. Zero Dependencies: Apart from Vue 3, the package has no external dependencies
  2. TypeScript Support: Full TypeScript support with type definitions
  3. Automatic Property Detection: Automatically handles new properties added to the form
  4. Performance: Built-in debouncing prevents excessive updates
  5. Small Bundle Size: Lightweight and optimized for production

Conclusion

vue-form-watchers simplifies form state management in Vue 3 applications by providing a clean, declarative API for handling form updates. It eliminates boilerplate code while adding powerful features like debouncing and external update handling.

Whether you're building a simple contact form or a complex multi-step wizard, this utility can help you write cleaner, more maintainable code.

Give it a try in your next Vue project and let me know how it works for you! The package is open source and available on GitHub.

Happy coding! ๐Ÿš€

Top comments (0)