Typescript in vue - Property 'validate' do

2020-05-20 08:12发布

问题:

I created v-form like this

<v-form ref="form" v-model="valid" lazy-validation>
  ...
  <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?

回答1:

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
  }
}


回答2:

let form: any = this.$refs.form
if(form.validate){}


回答3:

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()



回答4:

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.