Refilon
Refilon

Reputation: 3499

Vue directive don't render element if condition is false

I'm creating my own role/rights system because the ones that are available for Vue don't work.

I now hide the element when a user does not have the appropriate role. But what I actually want is to not render the component at all. But I don't know how to achieve that.

I have the following code:

app.directive('hasRole', hasRole)
import {useUserStore} from "@/stores/UserStore.js";

export default {
    // called before bound element's attributes
    // or event listeners are applied
    created(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    },
    // called right before the element is inserted into the DOM.
    async beforeMount(el, binding, vnode, prevVnode) {
        const userStore = useUserStore()
        await userStore.fill()
        console.log(userStore.getUser.roles.includes(binding.value))
        if (!userStore.getUser.roles.includes(binding.value)) {
            // el.style.display = 'none'; <---- this hides the element, so that works.
            vnode = null
            return el = null;
            el.style.display = 'none';
        }
    },
    // called when the bound element's parent component
    // and all its children are mounted.
   mounted(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    },
    // called before the parent component is updated
    beforeUpdate(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    },
    // called after the parent component and
    // all of its children have updated
    updated(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    },
    // called before the parent component is unmounted
    beforeUnmount(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    },
    // called when the parent component is unmounted
    unmounted(el, binding, vnode, prevVnode) {
        //console.log(el, binding, vnode, prevVnode)
    }
}

How can I prevent the render of the component?

Upvotes: 1

Views: 324

Answers (3)

Barthy
Barthy

Reputation: 3231

You already have a user store that can be injected into any component.

I suggest computing often-used keys like isAdmin, isSuperAdmin in the store:


const isAdmin computed(() => hasRole('admin'))

In your components, you can use the following pattern to conditionally render items:

<template>
<div v-if="isAdmin">
  <h1>Super Secret Stuff</h1>
</div>
</template>

<script setup>
const { isAdmin } = useUserStore()
</script>

This circumvents direct DOM manipulation, which is always a pain and often a bad practice.

Upvotes: 1

Here is the corrected code I have.

import {useUserStore} from "@/stores/UserStore.js";

export default {
  inserted(el, binding, vnode) {
    const { value } = binding
    const super_admin = "admin";
    const userStore = useUserStore()
    await userStore.fill()
    const roles = userStore.getUser.roles

    if (value && value instanceof Array && value.length > 0) {
      const roleFlag = value

      const hasRole = roles.some(role => {
        return super_admin === role || roleFlag.includes(role)
      })

      if (!hasRole) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`Missing role value"`)
    }
  }
}

Upvotes: 0

Mario Santini
Mario Santini

Reputation: 3003

I think that using directives for this purpose, is not the right way, in order to avoid component rendering.

To avoid a component to be rendered you should, insted, use the v-if condition.

To share the logic of hasRole between your component, you can use mixins, or just write a small plugin you can add to Vue app.

Upvotes: 1

Related Questions