# Vee-Validate Plugin

The vee-validate plugin lets you validate your generated fields using vee-validate (opens new window).

# Installation

To install the plugin, simply add it to your package.json via terminal, you also need to add vee-validate.

yarn add vee-validate@next @formvuelate/plugin-vee-validate

# OR

npm i vee-validate@next @formvuelate/plugin-vee-validate

# Usage

To use the plugin, import and pass it to the SchemaFormFactory. This creates a SchemaForm component with validation capabilities.

import { SchemaFormFactory } from 'formvuelate'
import VeeValidatePlugin from '@formvuelate/plugin-vee-validate'

const SchemaFormWithValidation = SchemaFormFactory([
  VeeValidatePlugin({
    // plugin configuration here
  })
])

Now that the component is created, you can register it and use it in your template:

<template>
  <div id="app">
    <SchemaFormWithValidation
      :schema="mySchema"
      v-model="myData"
    />
  </div>
</template>

<script>
export default {
  components: {
    SchemaFormWithValidation
  },
  setup () {
    [...]
  }
}
</script>

# Component Requirements

Your components will receive the validation state via validation prop which contains the error messages and the meta information exposed by vee-validate, the validation prop contains the following properties/methods:

{
  errorMessage, // The first error message for that field
  errors, // All error messages for that field
  meta, // A field meta object
  setTouched, // Sets the meta `touched` flag
}

You can opt-in to any of these properties or to the entire validation object. Here is an example FormText component that accepts the validation object as a prop:

<template>
  <div>
    <input
      :value="modelValue"
      @input="update($event.target.value)"
    >
    <span>{{ validation.errorMessage }}</span>
  </div>
</template>

<script>
export default {
  props: {
    // other props
    modelValue: {
      required: true
    },
    validation: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    update (value) {
      this.$emit('update:modelValue', value)
    }
  }
}
</script>

Whenever the modelValue is updated the field will be validated immediately.

If we want the validations not to be immediate, or lazy, we can show the error message when the field is touched. A field is considered touched after the field loses focus.

In this case, we set it using the validation object's setTouched method as shown in the following example.

<template>
  <div>
    <input
      :value="modelValue"
      @input="update($event.target.value)"
      @blur="onBlur"
    >
    <span v-if="validation.meta.touched">{{ validation.errorMessage }}</span>
  </div>
</template>

<script>
export default {
  props: {
    // ...
  },
  methods: {
    update (value) {
      this.$emit('update:modelValue', value)
    },
    onBlur() {
      this.validation.setTouched(true);
    }
  }
}
</script>

Note that when the form is submitted, all fields will be automatically "touched". Read here (opens new window) for information about form submission behavior in vee-validate.

# Configuration

The VeeValidatePlugin accepts one parameter, a configuration object. Let's look at the properties that we can use.

# mapProps

Important

This refers to Vee-validate's mapProps, and not to the mapProps property exposed by LookupPlugin.

If you are using 3rd party components and cannot modify their definition to accept the validation object, you can use the mapProps configuration to map the validation object to another property or multiple properties that are accepted by your component.

In the following example, the errorMessage is extracted to it's own prop.

const SchemaFormWithValidation = SchemaFormFactory([
  VeeValidatePlugin({
    mapProps(validation) {
      return {
        errorMessage: validation.errorMessage,
      }
    }
  })
])

Now your component definition can accept the errorMessage prop instead of the entire validation object.

You can also map the validation prop based on your schema input type, so if you are using multiple 3rd party components you can provide each with the suitable validation props.

const SchemaFormWithValidation = SchemaFormFactory([
  VeeValidatePlugin({
    mapProps(validation, el) {
      // If the field is the `FormText` component, send the entire validation object
      if (el.component.name === 'FormText') {
        return {
          validation
        }
      }
      // Otherwise send the error message only
      return {
        errorMessage: validation.errorMessage
      }
    }
  })
])

# Defining Validation Rules

Now that your component is configured to receive validation state, let's take a look on how to actually validate them.

There are two approaches to specify validation rules to your schema fields, which are "field-level" and "form-level".

# Field Level Validation

The "field-level" approach allows to you add a validations property to your fields schema, the validation property can be any type of validators that is accepted by vee-validate (opens new window)

Here is an example of a schema that uses all the possible validations value types:

import * as yup from 'yup';

const schema = {
  email: {
    component: FormText,
    label: 'Email',
    // Globally defined rules
    validations: 'required|email'
  },
  password: {
    component: FormText,
    label: 'Password',
    // Validation functions
    validations: (value) => value && value.length > 6
  },
  fullName: {
    component: FormText,
    label: 'Full Name',
    // yup validations
    validations: yup.string().required()
  }
}

Then you can use the schema in your template

<div id="app">
  <SchemaFormWithPlugin
    :schema="schema"
    v-model="myData"
  />
</div>

# Form Level Validation

You can specify validations on the form level by passing a validation-schema prop to the component created by SchemaFormFactory, the validation-schema prop value should be one that accepted by vee-validate's form level validation (opens new window).

This example uses yup to define validation schemas for your forms.

<template>
  <div id="app">
    <SchemaFormWithValidation
      :schema="schema"
      :validation-schema="validationSchema"
      v-model="formData"
    />
  </div>
</template>

<script>
import * as yup from 'yup';

export default {
  components: {
    SchemaFormWithValidation
  },
  setup () {
    const schema = ref([
      // Fields without the `validation` prop
    ])

    const formData = ref({})

    // The validation schema
    const validationSchema = yup.object().shape({
      email: yup.string().email().required(),
      password: yup.string().min(5).required(),
      fullName: yup.string().required(),
    })

    return {
      schema,
      formData,
      validationSchema
    }
  }
}
</script>

# Handling Submit

The VeeValidatePlugin automatically handles SchemaForm submits, and triggers validation before the form is submitted. You don't have to do anything special to trigger validations before submitting.

<template>
  <SchemaForm
    @submit="onSubmit"
    v-model="formData"
    :schema="schema"
  >
    <template #afterForm>
      <button>Submit</button>
    </template>
  </SchemaForm>
</template>

Note that the submit handler will be only executed if the form is valid.

# Initial Form State

You can provide initial validation state to the SchemaForm, to set initial errors you can use the initial-errors prop:

# Initial Errors
<template>
  <SchemaForm
    v-model="formData"
    :schema="schema"
    :initial-errors="initialErrors"
  >
    <template #afterForm>
      <button>Submit</button>
    </template>
  </SchemaForm>
</template>

<script>
export default {
  setup() {
    const schema = ref([
      // schema...
    ])

    const formData = ref({})
    const initialErrors = {
      email: 'This email is already taken',
      password: 'Password must be at least 8 characters long'
    }

    return {
      formData,
      schema,
      initialErrors
    }
  }
};
</script>

# Initial Meta

You can provide initial-touched prop to set the initial touched meta flags for your schema fields:

<template>
  <SchemaForm
    v-model="formData"
    :schema="schema"
    :initial-touched="initialTouched"
  >
    <template #afterForm>
      <button>Submit</button>
    </template>
  </SchemaForm>
</template>

<script>
export default {
  setup() {
    const schema = ref([
      // schema...
    ])

    const formData = ref({})
    const initialTouched = {
      email: true,
      password: false
    }

    return {
      formData,
      schema,
      initialErrors,
      initialTouched,
    }
  }
};
</script>