43

I created v-form like this

<v-form ref="form" v-model="valid" lazy-validation>
   <!-- form content -->
   <v-btn
     :disabled="!valid"
     @click="submit"
   >
     Submit
   </v-btn>
</v-form>

script:

if (this.$refs.form.validate()) // Error is in here

If i just console.log(this.$ref.form) the validate() function is available. But why this error is coming while building?

0

6 Answers 6

105

Solutions:

Simple:

(this.$refs.form as Vue & { validate: () => boolean }).validate()

Alternative (use this if you reference this.$refs.form multiple times in your component):

computed: {
  form(): Vue & { validate: () => boolean } {
    return this.$refs.form as Vue & { validate: () => boolean }
  }
} // Use it like so: this.form.validate()

Reusable (use this if you use the v-form component multiple times across your application):

// In a TS file
export type VForm = Vue & { validate: () => boolean }

// In component, import `VForm`
computed: {
  form(): VForm {
    return this.$refs.form as VForm
  }
}

Explanation:

In the Vue template syntax, we can use the ref attribute on a Vue instance or a DOM element. If ref is used in a v-for loop, an array of Vue instances or DOM elements is retreived.

This is why this.$refs can either return Vue | Element | Vue[] | Element[].

In order for TypeScript to know which type is being used, we need to cast the value.

We can either do:

(this.$refs.form as Vue).validate() or (<Vue>this.$refs.form).validate()

We cast it to Vue because v-form is a Vue instance (component) and not an Element.

My personal preference is to create a computed property which returns the Vue instance(s) or DOM element(s) already casted.

ie.

computed: {
  form(): Vue {
    return this.$refs.form as Vue
  }
}

The v-form instance has a validate method that returns a boolean, so we need to use an intersection type literal:

computed: {
  form(): Vue & { validate: () => boolean } {
    return this.$refs.form as Vue & { validate: () => boolean }
  }
}

Then we can use it like so: this.form.validate()


A better solution would be to create a type with the intersection so that it can be reused across multiple components.

export type VForm = Vue & { validate: () => boolean }

Then import it in the component:

computed: {
  form(): VForm {
    return this.$refs.form as VForm
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

This still throwing an error, i dont know why. But if i use (this.$refs.form as any).validate() . if i give Vue as type it is not working but replace with any is working. why it is like that?
The thing is that the Vue interface does not have a validate method. That method is only declared in the v-form vue instance. I do not know what vuetify recommends or if they have types, basically there should be a V-Form interface that extends the Vue instance, letting TypeScript know which methods exist in that particular component.
@Sam An alternative is that you do something like: (this.$refs.form as Vue & { validate: () => boolean }).validate()
Thanks yar, it works. can you explain a bit about this (this.$refs.form as Vue & { validate: () => boolean }).validate() . How it is working?
What is the best way? (this.$refs.form as any).validate() or (this.$refs.form as Vue & { validate: () => boolean }).validate()
18

If you use vue-class-component with vue-property-decorator you can do it like this:

Define in a types.ts a new Type with the vuetify form functions:

export type VForm = Vue & {
  validate: () => boolean;
  resetValidation: () => boolean;
  reset: () => void;
};

Then import in your component:

import { VForm } from "types";
import { Component, Ref} from "vue-property-decorator";

Use @Ref in your component to define the form:

export default class YourComponent extends Vue {
  @Ref("form") readonly form!: VForm;
}

so in your methods you can use it like this:

this.form.validate();
this.form.resetValidation();
this.form.reset();

2 Comments

This deserves more than the +1 I gave you!!
Great to hear that it helped you! If you want, feel free to donate me some coffee ;) ko-fi.com/gander
6
let form: any = this.$refs.form
if(form.validate){}

2 Comments

While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
I'm afraid it is also a terrible suggestion. This just disables the type checks... which is the whole point of TypeScript.
2

None of the answers did it. I was trying to get the validate then promise to work on a form.

As per comment

if you are building your vue component in typescript using

export default Vue.extend({})

then do

import { ValidationObserver, ValidationProvider, extend } from "vee-validate";
import { required } from "vee-validate/dist/rules";
import Vue, { VueConstructor } from "vue";
export default (Vue as VueConstructor<
  Vue & {
    $refs: {
      form: InstanceType<typeof ValidationProvider>;
    };
  }
>).extend({
  methods: {
    saveEntity() {
      if (this.loading) return;
      console.log(this.entity);
      this.$refs.form.validate().then(success => {
        if (!success) {
          console.log("not valid");
          return;
        }
        console.log("valid");
      });
    }
  }
})

This validates the ValidationObserver ref="form" just fine.

Comments

1

Couldn't comment on the accepted solution since I'm new to StackOverflow and wanted to provide my solution to this. I took the same initial step to investigate as OP and did a console.log(this.$ref.form), the output on the console is actually an array of [VueComponent] and validate() function doesn't exist in that context.

I was able to access the validate() function on the form by doing this.$ref.form[0].validate()

Comments

1

For vue 3 with Typescript the following worked for me:

<script setup lang="ts">
import { VForm } from 'vuetify/components'
import { ref } from 'vue';

const form = ref(VForm); // here I just put the type of the form. 

const logUser = async () => {
  const { valid } = await form.value.validate()
  console.log("The form is:", valid);
}
</script>

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.