Subscribe on changes!

custom directives on components: Can't access child component in directive hooks.

avatar
Nov 5th 2020

Version

3.0.2

Reproduction link

https://codesandbox.io/s/weathered-resonance-d2f6t?file=/src/App.vue

Steps to reproduce


import { createApp } from "vue";

const App = {
    directives: {
        mydir: {
            mounted(el, binding, vnode, oldVnode) {
                console.log(vnode.component) //vnode.component should be CompB vue instance, but now is null
            }
        }
    },

    render() {
        return (<div>
            <CompB name='a1' v-mydir="1" />
        </div>)
    }
}

let CompB = {
    props: ['name'],
    render() {
        return (<div>{'test: '   this.name}</div>);
    }
}

createApp(App).mount('#app');

What is expected?

vnode.component should be the CompB vue instance

What is actually happening?

vnode.component is null

avatar
Nov 5th 2020

the vnode represents the element, not the component.

The corrent way to access the component ìn Vue 3 is binding.instance

avatar
Nov 6th 2020

the vnode represents the element, not the component.

The corrent way to access the component ìn Vue 3 is binding.instance

I want get CompB component instance in directive, but binding.instance is App component instance, how can i get CompB instance in directive?

avatar
Nov 6th 2020

try getCurrentInstance

avatar
Nov 6th 2020

try getCurrentInstance

getCurrentInstance is return null, not CompB instance

avatar
Nov 6th 2020

I'll reopen this for now to explore this. This is a bit of an edge case where in Vue 2, vnode.context would have given you ComponentB indeed, as the vnode context gives you the instance that the elements belongs to.

binding.instance gives you the instance whose template contains the directive, and I'd think that for many typical custom directive situations, that the more desirable behavior.

The two are usually the same except when using a directive on a child component.

I wouldn't not consider this a bug as it is working as expected. We can either find a way to support the edge case or document this as a breaking change.

avatar
Nov 6th 2020

I'll reopen this for now to explore this. This is a bit of an edge case where in Vue 2, vnode.context would have given you ComponentB indeed, as the vnode context gives you the instance that the elements belongs to.

binding.instance gives you the instance whose template contains the directive, and I'd think that for many typical custom directive situations, that the more desirable behavior.

The two are usually the same except when using a directive on a child component.

I wouldn't not consider this a bug as it is working as expected. We can either find a way to support the edge case or document this as a breaking change.

I think use binding.context is more suitable for this,because the current vnode is the actual dom element point to, not the ComponentB's vnode

avatar
Feb 18th 2022

Is there any further news on this issue?

avatar
Apr 15th 2022

I found binding.instance work normal in development, but it turned out to be VNode in production. I use vite for building.

avatar
Jul 1st 2022

I found binding.instance work normal in development, but it turned out to be VNode in production. I use vite for building.

Finally solved? please

avatar
Jul 3rd 2022

I'm looking for this to resolve as well.

The two are usually the same except when using a directive on a child component.

That's exactly exception I ran into.

avatar
Jul 22nd 2022

I am now facing the exact same situation that needs to access the child component's context (or called instance) in custom directives.

I found that for the mounted arguments ($el, $binding, $vnode, $prevVnode), $vnode.dirs[0].instance sometimes return the child component's context (or instance) and I can manipulate it but not always....

Back to the question itself, I am pretty sure that the child component's context (or instance) is needed to be included in custom directives in some of the use cases and that would be great if the Vue development team could consider adding it back in Vue 3. I'm still looking forward to a solution/workaround.

Cheers!

Vue Version: 3.2.33