2

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?

  • could you wrap the code in the mounted and created in an `if` block so if your condition is not met, the code is not executed while the component is destroyed – Ayudh Jun 05 '20 at 12:05
  • 2
    You shouldn't use a directive to do this. Directives cannot prevent a component from being created in the first place (`v-if` isn't a real directive). – Decade Moon Jun 05 '20 at 12:05
  • 1
    Very helpful info. https://michaelnthiessen.com/force-re-render/ I faced the same problem and even forceUpdate didn't solve my problem. In my case key-changing strategy helped) – akim lyubchenko Jun 05 '20 at 12:18
  • @DecadeMoon, ok, I have to do something global which I can apply to every components throughout the application. Do you have an idea how I can do this? – user3910487 Jun 05 '20 at 12:20

1 Answers1

5

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.

tony19
  • 125,647
  • 18
  • 229
  • 307
Decade Moon
  • 32,968
  • 8
  • 81
  • 101