user3910487
user3910487

Reputation: 49

Vuejs - Issue when removing a component via directives and the mounted/created event is being executed

I wanted my directive to work as v-if since in my directive I have to check access rights and destroy the element if it does not have access.

Here is my code

Vue.directive('access',  {
    inserted: function(el, binding, vnode){

        //check access

        if(hasAccess){
           vnode.elm.parentElement.removeChild(vnode.elm);
        }
    },

});

vue file

<my-component v-access='{param: 'param'}'>

The issue is that i'm applying this directive to a component, it's removing the component but not the execution of functions called by the created/mounted hook.

In the component(my-component) there are functions in mounted/created hook. The execution of these functions are done and I don't want these functions to be executed. Is there a way to stop execution of the mounted/created events?

Upvotes: 2

Views: 1002

Answers (1)

Decade Moon
Decade Moon

Reputation: 34286

It is impossible to replicate the behavior of v-if in a custom directive. Directives cannot control how vnodes are rendered, they only have an effect on the DOM element it is attached to. (v-if is special, it's not actually a directive but instead generates conditional rendering code when the template is compiled.)

Though I would avoid doing any of the following suggestions if possible, I'll provide them anyway since it's close to what you want to do.

1. Extend the Vue prototype to add a global method

You definitely need to use v-if to do the conditional rendering. So all we have to do is come up with a global helper method which calculates the access permission.

Vue.prototype.$access = function (param) {
  // Calculate access however you need to
  // ("this" is the component instance you are calling the function on)
  return ...
}

Now in your templates you can do this:

<my-component v-if="$access({ param: 'param' })">

2. Define global method in the root component

This is basically the same as #1 except instead of polluting the Vue prototype with garbage, you define the method only on the root instance:

new Vue({
  el: '#app',
  render: h => h(App),
  methods: {
    access(param) {
      return ...
    }
  }
})

Now in your templates you can do this:

<my-component v-if="$root.access({ param: 'param' })">

Now it's clearer where the method is defined.

3. Use a global mixin

This may not be ideal, but for what it's worth you can investigate the viability of a global mixin.

4. Use a custom component

You can create a custom component (ideally functional but it needn't be) that can calculate access for specific regions in your template:

Vue.component('access', {
  functional: true,
  props: ['param'],
  render(h, ctx) {
    // Calculate access using props as input
    const access = calculateAccess(ctx.props.param)

    // Pass the access to the default scoped slot
    return ctx.scopedSlots.default(access)
  }
})

In your templates you can do this:

<access :param="param" v-slot="access">
  <!-- You can use `access` anywhere in this section -->
  <div>
    <my-component v-if="access"></my-component>
  </div>
</access>

Since <access> is a functional component, it won't actually render it's own component instance. Think of it more like a function than a component.

A bit overkill for your situation, but interesting nonetheless if you ever have a more complicated scenario.

Upvotes: 5

Related Questions