'Vue3: How to get error messages with Bootstrap and VeeValidate 4?

I want to use vue3 together with bootstrap 4/5 with veevalidate 4.

<template>
<Form as="form" @submit.prevent="onFormSubmit" class="needs-validation" :class="{ 'was-validated': wasValidated }"> 
    <div class="form-group">
        <label for="firstNameId">First Name *</label>
        <Field name="firstname" as="input" id="firstNameId" type="text" rules="required|firstname" class="form-control" placeholder="First Name" v-model="firstName" aria-describedby="input-true input-false input-help" aria-invalid="true" />
        <ErrorMessage as="div" name="firstname" v-slot="{ message }">
        {{ message }}
        <div class="invalid-feedback">
            {{ message }}
        </div>
        </ErrorMessage>
        <div class="valid-feedback">Good!</div>
    </div>
<Form>
</template>

<script>
import { Field, Form, ErrorMessage, defineRule } from 'vee-validate';

defineRule('required', value => {
    if (!value || !value.length) {
        return 'This field is required.';
    }
    return true;
});

defineRule("firstname", (value) => {
    if (!/^[a-zA-Z0-9( ),'.:/-]+$/i.test(value)) {
         return "Please use only letters, numbers and the following special characters: ( ),'.:/-";
    }
    return true;
});

export default {
    components: {
    Form,
    Field,
    ErrorMessage
    },
    data () {
        return {
             firstName: "",
             wasValidated: false,
    },
    methods: {
         onFormSubmit(values) {
              alert(JSON.stringify(values, null, 2));
              console.log("Submitted");
              console.log(values);

              var forms = document.getElementsByClassName('needs-validation');
              // Loop over them and prevent submission
              var validation = Array.prototype.filter.call(forms, function(form) {
                  form.addEventListener('submit', function(event) {
                      if (form.checkValidity() === false) {
                          event.preventDefault();
                          event.stopPropagation();
                       }
                       this.wasValidated = true;
                   }, false);
               });
          },
    },
};
</script>

The problem is that I can't activate the div with the class invalid-feedback or valid-feedback.

I could add the class was-validated to the <form>-tag, but I get feedback first after the second click on the submit button.



Solution 1:[1]

<template>
  <Form as="form" 
    @submit="onFormSubmit" 
    class="needs-validation" 
    :validation-schema="schema" 
    v-slot="{ errors }"
  > 
    <div class="form-group">
      <label for="firstNameId">First Name *</label>
      <Field 
        name="firstName"
        type="text"            
        placeholder="First Name" 
        v-model="firstName" 
        aria-describedby="input-true input-false input-help" 
        aria-invalid="true"
        v-slot="{ meta, field }"
      >
        <input
          v-bind="field"
          name="firstName"
          type="text"
          class="form-control"
          :class="{
            'is-valid': meta.valid && meta.touched,
            'is-invalid': !meta.valid && meta.touched,
          }"
        />
      </Field>
      <ErrorMessage as="div" name="firstname" v-slot="{ message }" class="invalid-feedback">
        {{ message }}
      </ErrorMessage>
    <Form>
  </template>

  <script setup>
  import { markRaw } from 'vue';
  import { Field, Form, ErrorMessage} from 'vee-validate';
  import * as yup from 'yup';
  </script>

  <script>
  export default {
    components: {
      Form,
      Field,
      ErrorMessage
    },
    data () {
      return {
         schema: markRaw(yup.object().shape({
           firstName: yup.string().min(0).max(20).label('First Name'),
         })),
      };
   },
   methods: {
     onFormSubmit(values) {
          console.log(JSON.stringify(values, null, 2));
     },
   },
 };
 </script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1