npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-form-watchers

v0.3.0

Published

A utility to dynamically create debounced watchers for reactive form fields in Vue 3.

Readme

vue-form-watchers

A lightweight utility for Vue 3 that creates dynamic, debounced watchers for reactive form fields. This package helps you efficiently manage form state changes and synchronization with minimal boilerplate code.

Features

  • 🔄 Automatic watcher creation for all form fields
  • ⚡ Debounced updates to prevent excessive callbacks
  • 🔍 Dynamic detection of new form properties
  • 🎯 Selective handling of external vs. user updates
  • 📦 TypeScript support
  • 🪶 Zero dependencies (except Vue 3)

Installation

npm install vue-form-watchers

Usage

Basic Usage

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

export default {
    setup() {
        const form = ref({
            name: '',
            email: '',
            age: 0
        })

        // Store the destroy function for cleanup
        const {destroy} = createFormWatchers(form.value, (key, value, source) => {
            console.log(`${key} changed to ${value} (${source})`)
            // Perform validation, API calls, or other operations
        })

        // Clean up watchers when component unmounts
        onUnmounted(() => {
            destroy()
        })

        return {form}
    }
}

Advanced Configuration

const {markUpdateAsExternal, destroy} = createFormWatchers(
    form.value,
    (key, value, source) => {
        if (source === 'user') {
            // Handle user-initiated changes
            validateField(key, value)
        }
    },
    {
        debounceTime: 300,              // Customize debounce delay (default: 500ms)
        immediate: true,                // Trigger on initialization (default: false)
        skipExternalUpdates: true,      // Ignore programmatic updates (default: true)
        debug: true,                    // Enable detailed logging (default: false)
        exclude: ['tempField'],         // Fields to ignore
        isExternalUpdate: (key, newValue, oldValue) => {
            // Custom logic to identify external updates
            return newValue === oldValue
        }
    }
)

Real-time Form Validation With API Integration

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

export default {
    setup() {
        // Form state
        const form = ref({
            email: '',
            password: '',
            tempData: ''  // This field won't trigger validation
        })

        // Validation state
        const errors = ref({})
        const isValidating = ref(false)

        // Create watchers with validation
        const {markUpdateAsExternal, destroy} = createFormWatchers(
            form.value,
            async (key, value) => {
                isValidating.value = true

                try {
                    // Example validation rules
                    switch (key) {
                        case 'email':
                            await api.validateEmail(value)  // Check email format & uniqueness
                            break
                        case 'password':
                            await api.validatePassword(value)  // Check password strength
                            break
                    }

                    // Clear error when validation passes
                    errors.value[key] = null

                } catch (error) {
                    // Store validation error message
                    errors.value[key] = error.message
                } finally {
                    isValidating.value = false
                }
            },
            {
                debounceTime: 500,      // Wait 500ms after typing
                exclude: ['tempData']   // Don't validate this field
            }
        )

        // Load saved form data
        const loadSavedData = async () => {
            try {
                const savedData = await api.getSavedForm()

                // Use markUpdateAsExternal to prevent validation
                // when loading saved data
                markUpdateAsExternal(() => {
                    Object.assign(form.value, savedData)
                })
            } catch (error) {
                console.error('Failed to load saved data:', error)
            }
        }

        // Clean up
        onUnmounted(() => {
            destroy()
        })

        // Template usage
        return {
            form,
            errors,
            isValidating,
            loadSavedData
        }
    }
}

API Reference

createFormWatchers

const {markUpdateAsExternal, destroy} = createFormWatchers(
    form.value,
    (key, value, source) => {
        console.log(`Field ${key} changed to ${value} from ${source}`)
    },
    {
        debounceTime: 300,              // Debounce delay (default: 500ms)
        immediate: true,                // Trigger on initialization (default: false)
        skipExternalUpdates: true,      // Ignore programmatic updates (default: true)
        debug: true,                    // Enable detailed logging (default: false)
        exclude: ['tempField'],         // Fields to ignore
        isExternalUpdate: (key, newValue, oldValue) => {
            // Custom logic to identify external updates
            return someCondition
        }
    }
)

Parameters

  • formObject: The reactive form object to watch
  • updateFunction: Callback function triggered on field changes
  • options: Configuration options object

Returns

  • markUpdateAsExternal: Wrap form updates to prevent triggering watchers
  • destroy: Clean up watchers and event listeners

An object containing the markUpdateAsExternal utility function for handling programmatic updates.

Debug Logging

When debug: true is set, the following events will be logged to the console:

  • Value changes for each field (old value, new value, and source)
  • Skipped external updates
  • Debounced update calls
  • Detection of new properties added to the form

This is useful for debugging and understanding the behavior of your form watchers during development.

Examples

Form Validation

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

const form = ref({
    email: '',
    password: ''
})

const errors = ref({})


createFormWatchers(form.value, async (key, value) => {
    try {
        await validateField(key, value)
        errors.value[key] = null
    } catch (error) {
        errors.value[key] = error.message
    }
}, {
    debounceTime: 300,
    debug: process.env.NODE_ENV === 'development' // Enable debug logging in development
})

API Synchronization

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

const form = ref({
    title: '',
    content: ''
})

const {markUpdateAsExternal} = createFormWatchers(
    form.value,
    async (key, value) => {
        await api.updateDraft({[key]: value})
    },
    {
        debounceTime: 1000,
        debug: process.env.NODE_ENV !== 'production' // Enable debug logging outside production
    }
)
// Load initial data
const loadDraft = async () => {
    const draft = await api.getDraft()
    markUpdateAsExternal(() => {
        form.value.title = draft.title
        form.value.content = draft.content
    })
}

Error Handling

The createFormWatchers function includes error validation to ensure correct usage. Below are common errors that might be thrown and how to handle them:

Common Errors

  • Invalid formObject
    • Error: formObject must be a reactive object
    • Cause: The first parameter must be a Vue reactive object (e.g., created with ref or reactive).
    • Solution: Ensure you pass a properly initialized reactive object.
import {reactive} from 'vue';

const form = reactive({name: '', email: ''});
createFormWatchers(form, updateFunction);
  • Invalid updateFunction
    • Error: updateFunction must be a function
    • Cause: The second parameter must be a function that accepts three arguments (key, value, source).
    • Solution: Pass a valid function that processes field updates.
createFormWatchers(form, (key, value) => {
    console.log(`${key} changed to ${value}`);
});
  • Invalid exclude Option
    • Error: exclude must be an array of strings
    • Cause: The second parameter must be a valid callback function to handle updates.
    • Solution: Pass a valid function that processes field updates.
createFormWatchers(form, updateFunction, { exclude: ['tempField'] });

Best Practices for Error Prevention

  • Reactive Sources: Always use reactive objects ref, reactive, or Pinia's reactive state(this.$state) as the formObject to ensure compatibility
  • Custom Logic Validation: Validate custom logic for isExternalUpdate to ensure proper function definitions and avoid unintended behavior.
  • Type Safety: Use TypeScript to leverage type-checking for configuration options and ensure consistent implementation.
  • State Management: When working with Pinia, initialize watchers in the appropriate lifecycle methods (e.g., onMounted) and clean them up (e.g., onUnmounted) to avoid memory leaks.

Requirements

  • Vue 3.0 or higher
  • Node.js 14.16 or higher

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request