Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
spec
src
.npmignore
.npmrc
CHANGELOG.md
LICENSE
README.md
index.d.ts
package-lock.json
package.json
tsconfig.json

README.md

CASL Angular @casl/angular NPM version CASL Documentation CASL Join the chat at https://gitter.im/stalniy-casl/casl

This package allows to integrate @casl/ability into Angular application. So, you can show or hide some components, buttons, etc based on user ability to see them.

Installation

npm install @casl/angular @casl/ability

Getting Started

1. Including module

This package provides AbilityModule module which adds CanPipe to templates and Ability instance to dependency injection container

// app.module.ts

import { NgModule } from '@angular/core'
import { AbilityModule } from '@casl/angular'
// ...

@NgModule({
  imports: [
    ...,
    AbilityModule.forRoot()
  ],
  declarations: [...],
  bootstrap: [...],
})
export class AppModule {}

Note: make sure that you use AbilityModule.forRoot() in your main module (usually it's AppModule) and AbilityModule in children modules (including lazy loaded ones).

2. Defining Abilities

This module provides an empty Ability instance, so you either need to provide your own or update existing one. In case if you want to provide your own, just define it using AbilityBuilder (or whatever way you prefer):

// ability.ts
import { AbilityBuilder } from '@casl/ability'

export const ability = AbilityBuilder.define(can => {
  can('read', 'all')
})

Later in your AppModule add additional provider:

import { AbilityModule } from ....
....
import { Ability } from '@casl/ability'
import { ability } from './ability'

@NgModule({
  imports: [
    ...,
    AbilityModule.forRoot()
  ],
  declarations: [...],
  providers: [
    { provide: Ability, useValue: ability }
  ],
  bootstrap: [...],
})
export class AppModule {}

Alternatively, you can just inject existing instance and update rules. Imagine that we have a Session service which is responsible for user login/logout functionality. Whenever user login, we need to update ability rules with rules which server returns and reset them back on logout. Lets do this:

// session.ts
import { Ability } from '@casl/ability'

export class Session {
  private token: string

  constructor(private ability: Ability) {}

  login(details) {
    return fetch('path/to/api/login', { methods: 'POST', body: JSON.stringify(details) })
      .then(response => response.json())
      .then(session => {
        this.ability.update(session.rules)
        this.token = session.token
       })
  }

  logout() {
    this.token = null
    this.ability.update([])
    // or this.ability.update([{ actions: 'read', subject: 'all' }]) to make everything to be readonly
  }
}

See @casl/ability package for more information on how to define abilities.

3. Check permissions in templates

To check permissions in any template you can use CanPipe:

<div *ngIf="'Post' | can: 'create'">
  <a (click)="createPost()">Add Post</a>
</div>

Performance considerations

Due to open feature in Angular, CanPipe was designed to be impure. This should work pretty fine if you have simple list of rules but may become a bottleneck when you have a lot of them. Don't worry, as there are several strategies which you can pick to make it faster:

  • use memoization (either on Ability#can or on CanPipe#can methods)
  • use immutable objects and overwrite existing pipe to be pure
  • use ChangeDectionStrategy.OnPush on your components whenever possible

To memoize results of CanPipe, you will need to create your own one and change its can method to cache results (this method was specifically designed to be overloaded by child class). Also you will need to clear all memoized results when corresponding Ability instance is updated (see update ability for details). The similar strategy can be applied to Ability class. Don't forget to provide new pipe or Ability class in Dependency injection! For example

import { MemoizedAbility } from './ability'
import { Ability } from '@casl/ability'

@NgModule({
  ...,
  providers: [
    { provide: Ability, useClass: MemoizedAbility }
  ]
})
export class AppModule {}

or if you want to provide custom pipe:

// pure-can.pipe.ts
import { CanPipe } from '@casl/angular'

@Pipe({ name: 'can' })
export class MyCanPipe extends CanPipe {}

// app.module.ts
import { MyCanPipe } from './pure-can.pipe'

@NgModule({
  ...,
  declarations: [
    MyCanPipe
  ]
})
export class AppModule {}

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing

License

MIT License

You can’t perform that action at this time.