I'm creating an app using Laravel Inertia.
I'm using Spatie's Laravel Permission package to handle authorisation and I'm trying to find a way to have a global can method or directive so that I can show or hide elements in the UI based on a user's permissions.
I know that I can send through permissions from my controller or via the HandleInertiaRequest middleware and then check against these in a vue component like this.
<button v-if="$page.props.auth.user.can['view users']">View Users</button>
And this works pretty well. But it would be nice if I can make something that's available globally and a little easier to remember. For example.
<button v-if="can('view users')">View Users</button>
or
<button v-can="'view users'">View Users</button>
However, I'm struggling to figure out what the best practice would be to achieve this.
I've tried to add a method to the globalProperties property in the setup() like so...
myApp.config.globalProperties.can = (permission) => {
return props.initialPage.props.admin.data.can[permission];
};
This half works but the problem with this is that I have to refresh the page whenever the permissions are updated, despite me passing the allowed permissions through via the Inertia middleware.
I've also considered creating a Mixin but I believe this isn't the recommend approach for Vue 3. This lead me to looking at the provide() and inject() features of vue 3 but I'm not sure if I'm barking up the wrong tree with those.
So I guess my question is how can I have a global method or vue directive that reads from shared data in a Laravel Inertia app?
I believe you can do this by implementing a module and import it globally. Something like this for instance should work for what you are trying to achieve.
export default class Auth {
constructor (params) {
this.user = params.user
this.role = params.role
}
/**
* Determines what a user has permission to see.
*
* @param {String} ability
* @returns Boolean
*/
can (ability) {
if (this.store[ability]) {
return this.store[ability]
}
const parts = ability.split(':')
if (parts.length !== 3) {
return false
}
const [ prefix, level, resource ] = parts
if (['create', 'read', 'update', 'delete'].indexOf(level) === -1) {
console.warn(`Incorrect permission method "${level}".`)
return false
}
}
hasRole (roles) {
if (Array.isArray(roles)) {
for(let i=0;i<roles.length;i++) {
if (this.hasRole(roles[i])) {
return true
}
}
}
return this.role === roles
}
You can then import the module in your app.js like so;
import Auth from '@/Modules/Auth'
The use the method the way you wanted, although in this case, I am using v-if
<button v-if="auth.can('view users)">View Users</button>