vue.jsvuetify.jsvee-validate

vee-validate + vuetify text input field → how to migrate to new versions?


I have a legacy Vue2 code for login page. A part of it that is checking for username input field:

...
<validation-observer ref="ref-id" v-slot="{ invalid }">
  <v-form>
    <v-card-text>
      <validation-provider
        v-slot="{ errors }"
        rules="rulename"
        name="username"
      >
        <v-text-field
          v-model="input.username"
          label="username"
          prepend-icon="mdi-account-circle"
          :error-messages="errors"
        ></v-text-field>
      </validation-provider>
      ...
    </v-card-text>
  </v-form>
</validation-observer>
...

Now I'm trying to create same code using new versions Vuetify and vee-validate (3 and 4)

I'm able to create v-text-field with new version of Vuetify:

<vee-validation-form as="v-form">
  <v-card-text>
    <!-- <vee-validation-field as="v-text-field" -->
    <v-text-field
      v-model="input.username"
      label="username"
      prepend-icon="mdi-account-circle"
      :error-messages="errors"
    ></v-text-field>
    <!-- ></vee-validation-field> -->
  </v-card-text>
</vee-validation-form>

Where

And this code is at least displaying username input element.

But when I'm trying to use <vee-validation-field as="v-text-field" instead of <v-text-field it shows me error:

ERROR
Cannot read properties of undefined (reading 'split')
TypeError: Cannot read properties of undefined (reading 'split')
    at normalizeFormPath (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:100:26)
    at ReactiveEffect.eval [as fn] (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:1603:70)
    at ReactiveEffect.run (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:217:19)
    at get value [as value] (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:1178:33)
    at unref (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:1057:29)
    at _useFieldValue (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:1141:70)
    at useFieldState (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:1044:54)
    at _useField (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:1618:72)
    at useField (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:1597:12)
    at setup (webpack-internal:///./node_modules/vee-validate/dist/vee-validate.esm.js:2038:164)

The question is: how to use v-text-field component with vee-validate? As I understand correctly here is no ValidationObserver and ValidationPbserver components in new vee-validate v4.


Solution

  • The Field component expects a name attribute, the error is thrown when it tries to call split() on the value. Just set a name and the error is gone:

    <Field as="v-text-field" 
      name="username"
      ...
    ></Field>
    

    However, label is a prop of the Field component, but it only passes attrs (i.e. non-props) to the custom input component (see code), so you cannot pass a label to the v-text-field. Also, you would have to get the error messages through the form.

    Looks like using :as is more trouble than solution at the moment. Probably better to stick with the slot:

    <vee-validation-field
      v-slot="{ field, errors }"
      name="username"
      rules="required|needsL"
      v-model="username"
    >
      <v-text-field
        v-model="field.value"
        v-bind="field"
        label="username"
        prepend-icon="mdi-account-circle"
        :error-messages="errors"
      />
    </vee-validation-field>
    

    (the somewhat duplicated v-bind and v-model on the VTextField is necessary for them moment due to an issue in vee-validate)

    const { createApp, ref } = Vue;
    const { createVuetify } = Vuetify
    const vuetify = createVuetify()
    
    VeeValidate.defineRule('required', VeeValidateRules.required)
    VeeValidate.defineRule('needsL', v => v.toLowerCase().includes('L') || 'Input something with an `l`')
    
    const app = {
      components: {
        'vee-validation-form': VeeValidate.Form,
        'vee-validation-field': VeeValidate.Field,
      },
      setup(){
        return {
          username1: ref('user 1'),
          username2: ref('user 2'),
        }
      }
    
    }
    createApp(app).use(vuetify).mount('#app')
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
    <div id="app">
      <v-app>
        <v-main>
          <vee-validation-form as="v-form" v-slot="{errors}">
            <v-card-text>
            Field from :as
              <vee-validation-field as="v-text-field" 
                name="username1"
                v-model="username1"
                prepend-icon="mdi-account-circle"
                :error-messages="errors.username1"
                rules="required|needsL"
              ></vee-validation-field>
              
              Field in slot
              <vee-validation-field
                v-slot="{ field, errors }"
                v-model="username2"
                name="username2"
                rules="required|needsL"
              >
                <v-text-field
                  v-model="field.value"
                  v-bind="field"
                  label="username"
                  prepend-icon="mdi-account-circle"
                  :error-messages="errors"
                />
              </vee-validation-field>
              
            </v-card-text>
          </vee-validation-form>
    
        </v-main>
      </v-app>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vee-validate/4.10.8/vee-validate.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@vee-validate/rules@4.11.0/dist/vee-validate-rules.min.js"></script>