vue.jsvuejs3vue-directives

How can I use vnode or binding directive to call a component method in VUE 3?


I created a directive, and I need to make that directive call a function that exists inside the component that has the directive.

// Component with directive, this component has the method 'setDisabled()'
<v-button v-can:edit.disable="[something]" @click="add" />


// Directive created
const can = {
    mounted: async (el, binding, vnode) => {
        let hasAccess = validateAccess()
        if (!hasAccess) {
            // should call v-button setDisabled() in here
        }
    },
}

In VUE 2 that was achievable by using vnode.context, but in VUE 3 it seems like I can only access methods from the parent component with binding.instance.

So, is there a way to use el, binding or vnode, after the component is mounte to call a method? So far just saw the props in vnode, no methods or reactive data.


Solution

  • Hacked solution for Vue3

    Constraints

    1. The Vue Doc clarifies

    Official Docs: Script Setup Components using <script setup> are closed by default - i.e. the public instance of the component, which is retrieved via template refs or $parent chains, will not expose any of the bindings declared inside .

    Official Docs: Custom Directives In general, it is not recommended to use custom directives on components.

    1. The binding.instance argument passed to the directive hook represents

      • The parent component having an element/component that uses the v-directive
      • Not the component that defines the v-directive.
    2. The vnode argument passed to the directive hook represents

      • The element that uses v-directive for Native DOM Element
      • The root element of component that uses v-directive for Custom component with single root
      • The first root element of component that uses v-directive for Custom component with multiple roots

    However

    1. You can access the custom component instance using vnode
    2. And you can access underlying method of the custom component from vnode using
      • Expose vnode.el.__vueParentComponent.exposed
      • Provide vnode.el.__vueParentComponent.provides

    Solution:

    1. Expose the component method using defineExpose
    const setDisabled = () => { /* Your precious method */};
    defineExpose({
      setDisabled
    });
    
    1. Access the exposed method using vnode
    const vCan = {
      mounted: async (el, binding, vnode)=> {
        vnode.el.__vueParentComponent.exposed.setDisabled()
      },
    };
    
    1. Also ensure the directive definition object is defined using camelCase and has v prefix like vCan