CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vuelidate

Simple, lightweight model-based validation library for Vue.js applications

Pending
Overview
Eval results
Files

validation-state.mddocs/

Validation State and Control

Complete validation state management with reactive properties and control methods for tracking field state, errors, and programmatic validation control.

Capabilities

Validation State Properties

Reactive properties available on all validation objects for tracking validation status.

interface ValidationState {
  /**
   * True if any validation rule is failing
   */
  readonly $invalid: boolean;
  
  /**
   * True if all validation rules are passing (opposite of $invalid)
   */
  readonly $valid: boolean;
  
  /**
   * True if any async validation is currently pending
   */
  readonly $pending: boolean;
  
  /**
   * True if the field has been touched/modified by user interaction
   */
  readonly $dirty: boolean;
  
  /**
   * True if this field or any nested field is dirty
   */
  readonly $anyDirty: boolean;
  
  /**
   * True if field is invalid AND dirty (commonly used for error display)
   */
  readonly $error: boolean;
  
  /**
   * True if this field or any nested field has an error
   */
  readonly $anyError: boolean;
  
  /**
   * Parameter object from validators (contains metadata)
   */
  readonly $params: object | null;
}

Usage:

// Template usage
export default {
  template: `
    <div>
      <input 
        v-model="email"
        :class="{ 'error': $v.email.$error }"
        @blur="$v.email.$touch()"
      />
      
      <!-- Show error only when field is dirty and invalid -->
      <div v-if="$v.email.$error" class="error-message">
        <span v-if="!$v.email.required">Email is required</span>
        <span v-if="!$v.email.email">Email format is invalid</span>
      </div>
      
      <!-- Show loading indicator for async validation -->
      <div v-if="$v.email.$pending" class="loading">
        Validating email...
      </div>
      
      <!-- Overall form state -->
      <button 
        :disabled="$v.$invalid || $v.$pending"
        @click="submitForm"
      >
        Submit
      </button>
    </div>
  `,
  
  computed: {
    formIsReady() {
      return !this.$v.$invalid && !this.$v.$pending
    },
    
    hasAnyErrors() {
      return this.$v.$anyError
    }
  }
}

Validation Control Methods

Methods for programmatically controlling validation state.

interface ValidationControl {
  /**
   * Marks the field as dirty (touched by user)
   * Triggers error display if field is invalid
   */
  $touch(): void;
  
  /**
   * Resets the field to pristine state (not dirty)
   * Hides error display even if field is invalid
   */
  $reset(): void;
  
  /**
   * Flattens nested validation parameters into a flat array structure
   * Useful for programmatic access to all validation metadata
   */
  $flattenParams(): Array<{path: string[], name: string, params: object}>;
  
  /**
   * Getter/setter for the validated model value
   * Setting triggers validation and marks field as dirty
   */
  $model: any;
}

Usage:

export default {
  methods: {
    // Touch all fields to show validation errors
    validateAll() {
      this.$v.$touch()
      if (!this.$v.$invalid) {
        this.submitForm()
      }
    },
    
    // Reset specific field
    resetEmail() {
      this.$v.email.$reset()
      this.email = ''
    },
    
    // Reset entire form
    resetForm() {
      this.$v.$reset()
      this.email = ''
      this.name = ''
      this.password = ''
    },
    
    // Programmatically set and validate value
    setEmailFromAPI(newEmail) {
      // Setting $model triggers validation and marks as dirty
      this.$v.email.$model = newEmail
    },
    
    // Get flattened parameter structure for debugging or error reporting
    debugValidation() {
      const flatParams = this.$v.$flattenParams()
      console.log('All validation parameters:', flatParams)
      
      // Example output:
      // [
      //   { path: ['email'], name: 'required', params: { type: 'required' } },
      //   { path: ['email'], name: 'email', params: { type: 'email' } },
      //   { path: ['password'], name: 'minLength', params: { type: 'minLength', min: 8 } }
      // ]
    },
    
    // Handle form submission
    submitForm() {
      // Touch all fields first
      this.$v.$touch()
      
      // Check if form is valid
      if (this.$v.$invalid) {
        console.log('Form has validation errors')
        return
      }
      
      // Check for pending async validations
      if (this.$v.$pending) {
        console.log('Validation still in progress')
        return
      }
      
      // Form is valid, proceed with submission
      console.log('Submitting form...')
    }
  }
}

Individual Validator State

Each validation rule exposes its own state for fine-grained control.

interface ValidatorState {
  /**
   * True if this specific validator is passing
   */
  readonly [validatorName]: boolean;
  
  /**
   * True if this specific validator is pending (async)
   */
  readonly $pending: boolean;
  
  /**
   * Parameters for this specific validator
   */
  readonly $params: object | null;
}

Usage:

export default {
  validations: {
    password: {
      required,
      minLength: minLength(8),
      strongPassword: customStrongPasswordValidator
    }
  },
  
  computed: {
    passwordErrors() {
      const pw = this.$v.password
      if (!pw.$dirty) return []
      
      const errors = []
      
      // Check each individual validator
      if (!pw.required) {
        errors.push('Password is required')
      }
      if (!pw.minLength) {
        errors.push(`Password must be at least ${pw.minLength.$params.min} characters`)
      }
      if (!pw.strongPassword) {
        errors.push('Password must include uppercase, lowercase, number, and special character')
      }
      
      return errors
    },
    
    passwordStrength() {
      const pw = this.$v.password
      if (!pw.$dirty || !this.password) return 0
      
      let strength = 0
      if (pw.required) strength += 1
      if (pw.minLength) strength += 2
      if (pw.strongPassword) strength += 2
      
      return strength
    }
  }
}

State Management Patterns

Form Validation Workflows

Common patterns for form validation and user experience.

Usage:

export default {
  data() {
    return {
      formSubmitted: false,
      showValidationSummary: false
    }
  },
  
  computed: {
    // Show errors after form submission or when field is dirty
    shouldShowErrors() {
      return (field) => {
        return this.formSubmitted || field.$dirty
      }
    },
    
    // Collect all validation errors
    allErrors() {
      const errors = []
      
      const collectErrors = (validation, path = '') => {
        for (const key in validation) {
          if (key.startsWith('$')) continue
          
          const field = validation[key]
          const fieldPath = path ? `${path}.${key}` : key
          
          if (typeof field === 'object' && field.$error) {
            // Individual field error
            for (const rule in field) {
              if (rule.startsWith('$') || field[rule]) continue
              errors.push({
                field: fieldPath,
                rule: rule,
                message: this.getErrorMessage(fieldPath, rule, field[rule].$params)
              })
            }
          } else if (typeof field === 'object') {
            // Nested validation
            collectErrors(field, fieldPath)
          }
        }
      }
      
      collectErrors(this.$v)
      return errors
    }
  },
  
  methods: {
    async handleSubmit() {
      this.formSubmitted = true
      this.$v.$touch()
      
      // Wait for any pending async validations
      if (this.$v.$pending) {
        await this.waitForValidation()
      }
      
      if (this.$v.$invalid) {
        this.showValidationSummary = true
        return
      }
      
      try {
        await this.submitToAPI()
        this.resetFormAfterSubmit()
      } catch (error) {
        console.error('Submission failed:', error)
      }
    },
    
    waitForValidation() {
      return new Promise((resolve) => {
        const checkPending = () => {
          if (!this.$v.$pending) {
            resolve()
          } else {
            this.$nextTick(checkPending)
          }
        }
        checkPending()
      })
    },
    
    resetFormAfterSubmit() {
      this.formSubmitted = false
      this.showValidationSummary = false
      this.$v.$reset()
      
      // Reset form data
      Object.assign(this.$data, this.$options.data())
    },
    
    getErrorMessage(field, rule, params) {
      const messages = {
        required: `${field} is required`,
        email: `${field} must be a valid email`,
        minLength: `${field} must be at least ${params?.min} characters`,
        // ... add more message mappings
      }
      
      return messages[rule] || `${field} is invalid`
    }
  }
}

Reactive Validation Watching

Advanced patterns for reacting to validation state changes.

Usage:

export default {
  watch: {
    // Watch overall form validity
    '$v.$invalid': {
      handler(isInvalid) {
        this.$emit('validity-changed', !isInvalid)
      },
      immediate: true
    },
    
    // Watch specific field for real-time feedback
    '$v.email.$error': {
      handler(hasError) {
        if (hasError && this.$v.email.$dirty) {
          this.showEmailHelp = true
        }
      }
    },
    
    // Watch for pending state changes
    '$v.$pending'(isPending) {
      this.isValidating = isPending
      
      if (isPending) {
        this.validationStartTime = Date.now()
      } else {
        console.log(`Validation completed in ${Date.now() - this.validationStartTime}ms`)
      }
    },
    
    // Deep watch for nested validation changes
    '$v': {
      handler(newVal, oldVal) {
        // Custom logic for validation state changes
        this.saveValidationStateToLocalStorage()
      },
      deep: true
    }
  },
  
  methods: {
    saveValidationStateToLocalStorage() {
      const state = {
        dirty: this.$v.$dirty,
        invalid: this.$v.$invalid,
        errors: this.allErrors
      }
      localStorage.setItem('formValidationState', JSON.stringify(state))
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-vuelidate

docs

collections.md

custom-validators.md

index.md

integration.md

validation-state.md

validators.md

tile.json