Skip to content

Commit

Permalink
chore(deps): updates angular to v12 (#516)
Browse files Browse the repository at this point in the history
Closes #514, #512

BREAKING CHANGE: there are 2 important changes:

  * deprecated CanPipe was removed, use AblePipe instead
  * library is compiled by Ivy and no longer support ViewEngine
  • Loading branch information
stalniy authored May 31, 2021
1 parent 02343a5 commit ff4212c
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 284 deletions.
16 changes: 4 additions & 12 deletions packages/casl-angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
[![](https://img.shields.io/npm/dm/%40casl%2Fangular.svg)](https://www.npmjs.com/package/%40casl%2Fangular)
[![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 [Angular] application. It provides `AblePipe` and **deprecated** `CanPipe` to Angular templates, so you can show or hide components, buttons, etc based on user ability to see them.
This package allows to integrate `@casl/ability` with [Angular] application. It provides `AblePipe` and `AblePurePipe` to Angular templates, so you can show or hide components, buttons, etc based on user ability to see them.

## Installation

The latest version of this package is compiled by Ivy, so **apps that do not use Ivy are no longer supported.**

```sh
npm install @casl/angular @casl/ability
# or
Expand Down Expand Up @@ -39,7 +41,7 @@ import { Ability, PureAbility } from '@casl/ability';
export class AppModule {}
```

The 2nd provider provides instance of `PureAbility`, so `CanPipe` and `AblePipe` can inject it later. This pipes inject `PureAbility` (not `Ability`) because this allows an application developer to decide how to configure actions, subjects and conditions. Also this is the only way to get maximum from tree shaking (e.g., if you don't need conditions you can use `PureAbility` and get rid of `sift` library).
The 2nd provider provides instance of `PureAbility`, so pipes can inject it later. This pipes inject `PureAbility` (not `Ability`) because this allows an application developer to decide how to configure actions, subjects and conditions. Also this is the only way to get maximum from tree shaking (e.g., if you don't need conditions you can use `PureAbility` and shrink @casl/ability size).

> Read [CASL and TypeScript](https://casl.js.org/v5/en/advanced/typescript) to get more details about `Ability` type configuration.
Expand Down Expand Up @@ -131,16 +133,6 @@ To check permissions in any template you can use `AblePipe`:

> You can read the expression in `ngIf` as "if creatable Post"
Or with **deprecated** `CanPipe`:

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

`CanPipe` was deprecated because it is less readable and it was harder to integrate it with all type definitions supported by `Ability`'s `can` method. That's why `CanPipe` has weaker typings than `AblePipe`.

## Why pipe and not directive?

Directive cannot be used to pass values into inputs of other components. For example, we need to enable or disable a button based on user's ability to create a post. With directive we cannot do this but we can do this with pipe:
Expand Down
31 changes: 16 additions & 15 deletions packages/casl-angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"description": "Angular module for CASL which makes it easy to add permissions in any Angular app",
"main": "dist/umd/index.js",
"module": "dist/es5m/index.js",
"es2015": "dist/es6m/index.js",
"es2015": "dist/es6m/index.mjs",
"typings": "dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/es6m/index.js",
"import": "./dist/es6m/index.mjs",
"require": "./dist/umd/index.js"
}
},
Expand All @@ -26,9 +26,9 @@
"build.es5m": "TARGET=es5 BUILD=es5m npm run rollup",
"build.es6": "TARGET=es2015 BUILD=es6m npm run rollup",
"build.umd": "TARGET=es5 BUILD=umd npm run rollup",
"build.types": "ngc -p tsconfig.types.json",
"build.types": "ngc -p tsconfig.types.json && rm -rf dist/types/*.js",
"prerollup": "ngc -p tsconfig.build.json --target $TARGET --outDir dist/$BUILD/tmp",
"rollup": "IGNORE_SUBPATH=1 LIB_MINIFY=false BUILD_TYPES=$BUILD ES_TRANSFORM=false ES6M_EXT=.js dx rollup -i dist/$BUILD/tmp/index.js -n casl.ng -g @angular/core:ng.core,@casl/ability:casl,tslib:tslib,rxjs:rxjs",
"rollup": "IGNORE_SUBPATH=1 LIB_MINIFY=false BUILD_TYPES=$BUILD ES_TRANSFORM=false dx rollup -i dist/$BUILD/tmp/index.js -n casl.ng -g @angular/core:ng.core,@casl/ability:casl,tslib:tslib,rxjs:rxjs",
"postrollup": "rm -rf dist/$BUILD/tmp",
"test": "dx jest --config ./jest.config.js",
"lint": "dx eslint src/ spec/",
Expand All @@ -45,24 +45,25 @@
"author": "Sergii Stotskyi <[email protected]>",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^9.0.0 || ^10.0.0 || ^11.0.0",
"@angular/core": "^12.0.0",
"@casl/ability": "^3.0.0 || ^4.0.0 || ^5.1.0",
"rxjs": "^6.0.0",
"rxjs": "^6.5.3",
"tslib": "^2.0.0"
},
"devDependencies": {
"@abraham/reflection": "^0.7.0",
"@angular/common": "^11.0.0",
"@angular/compiler": "^11.0.0",
"@angular/compiler-cli": "^11.0.0",
"@angular/core": "^11.0.0",
"@angular/platform-browser": "^11.0.0",
"@angular/platform-browser-dynamic": "^11.0.0",
"@abraham/reflection": "^0.8.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/compiler-cli": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@casl/ability": "^5.1.0",
"@casl/dx": "workspace:^1.0.0",
"rxjs": "^6.0.0",
"rxjs": "^6.5.3",
"tslib": "^2.0.0",
"zone.js": "^0.10.2"
"typescript": "~4.2.3",
"zone.js": "~0.11.4"
},
"files": [
"dist",
Expand Down
9 changes: 2 additions & 7 deletions packages/casl-angular/spec/AbilityModule.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PureAbility } from '@casl/ability'
import { TestBed } from '@angular/core/testing'
import { createApp, createComponent, configureTestingModule, Post } from './spec_helper'

const AppWithCanPipe = createApp('{{ post | can: \'read\' }}')
const AppWithAblePipe = createApp('{{ \'read\' | able: post }}')
const AppWithAblePurePipe = createApp('{{ \'read\' | ablePure: post | async }}')

Expand All @@ -19,8 +18,8 @@ describe('Ability pipes', () => {

describe('module', () => {
it('provides deprecated impure `can` pipe', () => {
configureTestingModule([AppWithCanPipe])
fixture = createComponent(AppWithCanPipe)
configureTestingModule([AppWithAblePurePipe])
fixture = createComponent(AppWithAblePurePipe)
expect(fixture.nativeElement.textContent).toBe('false')
})

Expand All @@ -31,10 +30,6 @@ describe('Ability pipes', () => {
})
})

describe('`can` pipe', () => {
behavesLikeAbilityPipe(AppWithCanPipe)
})

describe('`able` pipe', () => {
behavesLikeAbilityPipe(AppWithAblePipe)
})
Expand Down
26 changes: 1 addition & 25 deletions packages/casl-angular/spec/AblePipe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { ChangeDetectorRef } from '@angular/core'
import { defineAbility, Ability } from '@casl/ability'
import { AblePipe } from '../src/public'

describe('Can pipe', () => {
let ability: Ability
let pipe: AblePipe<any>
let changeDetectorRef: ChangeDetectorRef

beforeEach(() => {
ability = defineAbility<Ability>(can => can('read', 'all'))
changeDetectorRef = { markForCheck: jest.fn() } as unknown as ChangeDetectorRef
pipe = new AblePipe(ability, changeDetectorRef)
pipe = new AblePipe(ability)
})

it('calls underlying `ability` `can` method', () => {
Expand All @@ -19,25 +16,4 @@ describe('Can pipe', () => {

expect(can).toHaveBeenCalledWith('read', 'Post')
})

it('marks change detector as dirty when ability updates', () => {
pipe.transform('read', 'Post')
ability.update([])

expect(changeDetectorRef.markForCheck).toHaveBeenCalled()
})

it('does not subscribes to ability updates if it has not been executed', () => {
ability.update([])

expect(changeDetectorRef.markForCheck).not.toHaveBeenCalled()
})

it('unsubscribes from ability when destroyed', () => {
pipe.transform('read', 'Post')
pipe.ngOnDestroy()
ability.update([])

expect(changeDetectorRef.markForCheck).not.toHaveBeenCalled()
})
})
4 changes: 1 addition & 3 deletions packages/casl-angular/src/AbilityModule.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { NgModule } from '@angular/core';
import { CanPipe, AblePipe, AblePurePipe } from './pipes';
import { AblePipe, AblePurePipe } from './pipes';

@NgModule({
declarations: [
CanPipe,
AblePipe,
AblePurePipe,
],
exports: [
CanPipe,
AblePipe,
AblePurePipe,
],
Expand Down
56 changes: 4 additions & 52 deletions packages/casl-angular/src/pipes.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,19 @@
import { Pipe, ChangeDetectorRef, Inject, PipeTransform } from '@angular/core';
import { Pipe, Inject, PipeTransform } from '@angular/core';
import { PureAbility, Unsubscribe, AnyAbility } from '@casl/ability';
import { Observable } from 'rxjs';

class AbilityPipe<T extends AnyAbility> {
@Pipe({ name: 'able', pure: false })
export class AblePipe<T extends AnyAbility> implements PipeTransform {
protected _unsubscribeFromAbility?: Unsubscribe;
private _ability: T;
private _cd: ChangeDetectorRef;

constructor(ability: T, cd: ChangeDetectorRef) {
constructor(@Inject(PureAbility) ability: T) {
this._ability = ability;
this._cd = cd;
}

transform(...args: Parameters<T['can']>): boolean {
if (!this._unsubscribeFromAbility) {
this._unsubscribeFromAbility = this._ability.on('updated', () => this._cd.markForCheck());
}
return this._ability.can(...args);
}

ngOnDestroy() {
if (this._unsubscribeFromAbility) {
this._unsubscribeFromAbility();
}
}
}

@Pipe({ name: 'can', pure: false })
export class CanPipe<T extends AnyAbility> implements PipeTransform {
protected pipe: AbilityPipe<T>;

constructor(@Inject(PureAbility) ability: T, cd: ChangeDetectorRef) {
this.pipe = new AbilityPipe(ability, cd);
}

transform(
subject: Parameters<T['can']>[1],
action: Parameters<T['can']>[0],
field?: string
): boolean {
return (this.pipe as any).transform(action, subject, field);
}

ngOnDestroy() {
this.pipe.ngOnDestroy();
}
}

@Pipe({ name: 'able', pure: false })
export class AblePipe<T extends AnyAbility> implements PipeTransform {
protected pipe: AbilityPipe<T>;

constructor(@Inject(PureAbility) ability: T, cd: ChangeDetectorRef) {
this.pipe = new AbilityPipe(ability, cd);
}

transform(...args: Parameters<T['can']>): boolean {
return this.pipe.transform(...args);
}

ngOnDestroy() {
this.pipe.ngOnDestroy();
}
}

@Pipe({ name: 'ablePure' })
Expand Down
4 changes: 1 addition & 3 deletions packages/casl-angular/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
"spec/*"
],
"angularCompilerOptions": {
"enableIvy": false,
"enableIvy": true,
"skipMetadataEmit": true,
"skipTemplateCodegen": true,
"strictInjectionParameters": true,
"flatModuleOutFile": "index.js",
"flatModuleId": "@casl/angular"
}
Expand Down
1 change: 0 additions & 1 deletion packages/casl-angular/tsconfig.types.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "dist/types"
},
Expand Down
Loading

0 comments on commit ff4212c

Please sign in to comment.