Skip to content

Validations User Guide

This guide shows you how to use the formatValidationErrors function in real Vue.js applications.

Vue Component Usage

Use the function to display API validation errors in your components:

<template>
  <div>
    <form @submit.prevent="handleSubmit">
      <div>
        <label>Email:</label>
        <input v-model="formData.email" type="email" required />
      </div>

      <div>
        <label>Password:</label>
        <input v-model="formData.password" type="password" required />
      </div>

      <!-- Show formatted validation errors -->
      <div v-if="validationError" class="error-message">
        {{ validationError }}
      </div>

      <button type="submit" :disabled="loading">
        {{ loading ? 'Submitting...' : 'Submit' }}
      </button>
    </form>
  </div>
</template>

<script setup>
import { formatValidationErrors, useFetcher } from 'vue-fastedgy'
import { ref, reactive } from 'vue'

const fetcher = useFetcher()

const formData = reactive({
  email: '',
  password: ''
})

const loading = ref(false)
const validationError = ref('')

const handleSubmit = async () => {
  loading.value = true
  validationError.value = ''

  try {
    const response = await fetcher.post('/auth/login', formData)
    console.log('Success:', response.data)
  } catch (error) {
    // Format and display validation errors
    const errorMessage = formatValidationErrors(error, 'Login failed')
    validationError.value = errorMessage
    console.error('Validation errors:', errorMessage)
  } finally {
    loading.value = false
  }
}
</script>

<style scoped>
.error-message {
  color: red;
  margin: 1rem 0;
  white-space: pre-line; /* Preserve line breaks for multiple errors */
}
</style>

Advanced Error Handling

Handle different types of API errors:

import { formatValidationErrors, HttpError } from 'vue-fastedgy'

const handleApiError = (error) => {
  if (error instanceof HttpError) {
    const status = error.response.status

    switch (status) {
      case 400:
        // Bad Request - usually validation errors
        return formatValidationErrors(error, 'Invalid form data')

      case 401:
        return 'Invalid credentials'

      case 403:
        return 'Access denied'

      case 422:
        // Unprocessable Entity - Pydantic validation errors
        return formatValidationErrors(error, 'Please check your input')

      case 500:
        return 'Server error. Please try again later.'

      default:
        return formatValidationErrors(error, 'Something went wrong')
    }
  }

  // Network or other errors
  return 'Connection error. Please check your internet connection.'
}

// Usage in component
const submitForm = async () => {
  try {
    await fetcher.post('/api/users', userData)
  } catch (error) {
    const errorMessage = handleApiError(error)
    showNotification('error', errorMessage)
  }
}

Form Validation Component

Create a reusable form component with validation error display:

<template>
  <div class="validated-form">
    <slot :errors="fieldErrors" :hasErrors="hasValidationErrors" />

    <!-- Global error display -->
    <div v-if="globalError" class="global-error">
      {{ globalError }}
    </div>
  </div>
</template>

<script setup>
import { formatValidationErrors } from 'vue-fastedgy'
import { ref, computed } from 'vue'

const emit = defineEmits(['submit', 'error'])

const globalError = ref('')
const fieldErrors = ref({})

const hasValidationErrors = computed(() => {
  return Object.keys(fieldErrors.value).length > 0 || !!globalError.value
})

const handleSubmit = async (submitFunction) => {
  clearErrors()

  try {
    const result = await submitFunction()
    emit('submit', result)
  } catch (error) {
    handleError(error)
    emit('error', error)
  }
}

const clearErrors = () => {
  globalError.value = ''
  fieldErrors.value = {}
}

const handleError = (error) => {
  const errorData = error?.data?.detail

  if (!errorData) {
    globalError.value = 'An unexpected error occurred'
    return
  }

  if (typeof errorData === 'string') {
    globalError.value = errorData
    return
  }

  if (Array.isArray(errorData)) {
    // Parse field-specific errors
    errorData.forEach(err => {
      if (err.loc && err.loc.length > 1) {
        const fieldName = err.loc[err.loc.length - 1]
        fieldErrors.value[fieldName] = err.msg
      } else {
        // Global error
        globalError.value = formatValidationErrors(error, 'Validation failed')
      }
    })

    // If no field-specific errors, show as global
    if (Object.keys(fieldErrors.value).length === 0) {
      globalError.value = formatValidationErrors(error, 'Validation failed')
    }
  }
}

defineExpose({
  handleSubmit,
  clearErrors,
  hasValidationErrors
})
</script>

<style scoped>
.global-error {
  color: red;
  margin: 1rem 0;
  padding: 1rem;
  border: 1px solid #ffcdd2;
  background-color: #ffebee;
  border-radius: 4px;
}
</style>

Usage of the validated form component:

<template>
  <ValidatedForm ref="form" @submit="onSuccess" @error="onError">
    <template #default="{ errors }">
      <div>
        <label>Name:</label>
        <input v-model="userData.name" type="text" required />
        <span v-if="errors.name" class="field-error">{{ errors.name }}</span>
      </div>

      <div>
        <label>Email:</label>
        <input v-model="userData.email" type="email" required />
        <span v-if="errors.email" class="field-error">{{ errors.email }}</span>
      </div>

      <button type="button" @click="submitForm">
        Create User
      </button>
    </template>
  </ValidatedForm>
</template>

<script setup>
import { useFetcher } from 'vue-fastedgy'
import { reactive, ref } from 'vue'
import ValidatedForm from './ValidatedForm.vue'

const fetcher = useFetcher()
const form = ref()

const userData = reactive({
  name: '',
  email: ''
})

const submitForm = () => {
  form.value.handleSubmit(() => {
    return fetcher.post('/users', userData)
  })
}

const onSuccess = (result) => {
  console.log('User created:', result)
  // Reset form
  userData.name = ''
  userData.email = ''
}

const onError = (error) => {
  console.error('Form submission failed:', error)
}
</script>

<style scoped>
.field-error {
  color: red;
  font-size: 0.875rem;
  display: block;
  margin-top: 0.25rem;
}
</style>

Error Format Examples

Pydantic Array Errors

const arrayError = {
  data: {
    detail: [
      {
        loc: ['body', 'email'],
        msg: 'field required',
        type: 'value_error.missing'
      },
      {
        loc: ['body', 'age'],
        msg: 'ensure this value is greater than or equal to 0',
        type: 'value_error.number.not_ge',
        ctx: { limit_value: 0 }
      }
    ]
  }
}

const formatted = formatValidationErrors(arrayError)
console.log(formatted)
// Output:
// "• body → email: field required
// • body → age: ensure this value is greater than or equal to 0"

Simple String Errors

const stringError = {
  data: {
    detail: "Username already exists"
  }
}

const formatted = formatValidationErrors(stringError)
console.log(formatted)
// Output: "Username already exists"

Nested Field Errors

const nestedError = {
  data: {
    detail: [{
      loc: ['body', 'address', 'zipcode'],
      msg: 'invalid zip code format'
    }]
  }
}

const formatted = formatValidationErrors(nestedError)
console.log(formatted)
// Output: "body → address → zipcode: invalid zip code format"

With Default Message

const unknownError = {
  data: {} // No detail property
}

const formatted = formatValidationErrors(unknownError, 'Something went wrong')
console.log(formatted)
// Output: "Something went wrong"

// Without default message
const formatted2 = formatValidationErrors(unknownError)
console.log(formatted2)
// Output: "Erreur inconnue"

Integration with Toast Notifications

import { formatValidationErrors } from 'vue-fastedgy'

const showValidationToast = (error) => {
  const message = formatValidationErrors(error, 'Please check your input')

  // With a toast library like vue-toastification
  toast.error(message, {
    timeout: 5000,
    closeOnClick: true,
    pauseOnFocusLoss: false,
    pauseOnHover: true
  })
}

// Usage in form submission
const handleFormSubmit = async () => {
  try {
    await submitData()
    toast.success('Form submitted successfully!')
  } catch (error) {
    showValidationToast(error)
  }
}