diff --git a/.eslintrc b/.eslintrc index 6fba2b2e7..a08c1397f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -82,6 +82,14 @@ }], "import/no-extraneous-dependencies": "off" } + }, + { + "files": [ + "packages/casl-vue/spec/**/*.{js,ts}" + ], + "env": { + "browser": true + } } ] } diff --git a/packages/casl-vue/README.md b/packages/casl-vue/README.md index bb83e3103..6b708d813 100644 --- a/packages/casl-vue/README.md +++ b/packages/casl-vue/README.md @@ -4,10 +4,22 @@ [![](https://img.shields.io/npm/dm/%40casl%2Fvue.svg)](https://www.npmjs.com/package/%40casl%2Fvue) [![CASL Join the chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/stalniy-casl/casl) -This package allows to integrate `@casl/ability` with [Vue] application. So, you can show or hide UI elements based on user ability to see them. This package provides a Vue plugin which defines `$ability` object and `$can` method for all components. Also package provides functional `Can` component (not included in the plugin), both allow to hide or show UI elements based on the user ability to see them. +This package allows to integrate `@casl/ability` with [Vue 3] application. So, you can show or hide UI elements based on user ability to see them. ## Installation +**For Vue 2.x**: + +```sh +npm install @casl/vue@1.2.1 @casl/ability +# or +yarn add @casl/vue@1.2.1 @casl/ability +# or +pnpm add @casl/vue@1.2.1 @casl/ability +``` + +**For Vue 3.x**: + ```sh npm install @casl/vue @casl/ability # or @@ -18,65 +30,134 @@ pnpm add @casl/vue @casl/ability ## Getting started -If you don't plan to use multiple `Ability` instances across your application (99.9% likelihood that you don't), you can pass `Ability` instance as a 2nd argument to `Vue.use`: +This package provides a Vue plugin which defines `$ability` object and `$can` method for all components, in the same way as it was for Vue 2.x. Additionally, this package provides `useAbility` and `provideAbility` hooks that can be used with new [Vue Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html). -```js @{data-filename="main.js"} -import Vue from 'vue'; +### Vue plugin if you need some backward compatibility + +`abilitiesPlugin` is left for backward compatibility with Vue 2.x and provides global `$ability` and `$can` properties. However,`Ability` instance is now a mandatory argument for plugin: + +```js +import { createApp } from 'vue'; import { abilitiesPlugin } from '@casl/vue'; import ability from './services/ability'; -Vue.use(abilitiesPlugin, ability); +createApp() + .use(abilitiesPlugin, ability) + .mount('#app'); ``` -but if you one from that 0.1%, you need to pass it in `Vue` constructor: +`Can` component is not registered by the plugin, so we can decide whether we want to use component or `v-if` + `$can` method. Also, this helps tree shaking to remove it if we decide to not use it. So, to register component globally just use global API ([read more](#can-component)): -```js @{data-filename="main.js"} -import Vue from 'vue'; -import { abilitiesPlugin } from '@casl/vue'; -import ability from './services/ability'; +```js +import { Can } from '@casl/vue'; -Vue.use(abilitiesPlugin); +createApp() + .use(abilitiesPlugin, ability) + .component(Can.name, Can) + .mount('#app'); +``` + +Later, we can use either `$ability` or `$can` method in any component: -new Vue({ - el: '#app', - ability -}) +```vue + ``` -The difference is that the 1st approach defines `Ability` instance on `Vue.prototype` and 2nd one passes ability from parent to child in component tree. +`globalProperties` is the same concept as a global variables which make life more complicated because any component has access to them (i.e., implicit dependency) and we need to ensure they don't introduce name collisions by prefixing them. So, instead of exposing `$ability` and `$can` as globals, we can use [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html) to get access to `$ability`: -> The 2nd approach potentially may slowdown components creation but you will not notice this ;) +```js +createApp() + .use(abilitiesPlugin, ability, { + defineGlobals: false // disable globalProperties pollution + }) + .mount('#app'); +``` -The plugin doesn't register `Can` component, so you can decide whether to use it or not. In most cases, `$can` function is enough and it's more lightweight than `Can` component. +To inject an `Ability` instance, we can use `ABILITY_TOKEN`: -To use `Can` functional component, you need to import it in a particular component or register it globally: +```vue + -```js -import Vue from 'vue'; -import { Can } from '@casl/vue'; + ``` -> See [CASL guide](https://casl.js.org/v5/en/guide/intro) to learn how to define `Ability` instance. +This is a bit more complicated but allows us to be explicit. This works especially good with new [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html): -## Check permissions in templates +```vue + -To check permissions, you can use `$can` method in any component, it accepts the same arguments as `Ability`'s `can`: + +``` + +### provideAbility hook + +Very rarely, we may need to provide a different `Ability` instance for a sub-tree of components, and to do this we can use `provideAbility` hook: + +```vue + + ``` -## Can component +> See [CASL guide](https://casl.js.org/v5/en/guide/intro) to learn how to define `Ability` instance. -There is an alternative way you can check your permissions in the app by using the `Can` component. Instead of using `v-if="$can(...)"`, we can do this: -```html +### Can component + +There is an alternative way we can check permissions in the app, by using `Can` component: + +```vue