О маленькой полезной фиче angular
Недавно появилась задача в одном достаточно крупном проекте ограничивать UI и функционал пользователей, в зависимости от их ролей. К этому моменту приложение уже разрослось на 100+ компонент, и это при том что основные базовые компоненты вынесены в отдельную репу и ставятся пакетом. То есть примерно в каждом из 100+, вероятно придется вносить некоторые правки, связанные с правами доступа.
Первой мыслью было пройтись по всем компонентам, заинжектить через DI UserService, прокинуть в шаблон контекст пользователя, и там с этим как-то разбираться. Однако, на счастье, я не успел этим заняться, так как нашел другое, как мне кажется более удачное решение.
exportAs
Я подумал о том, как можно сразу в шаблонах получить userContext, без необходимости инжектировать сервис или токен с пользователем в контроллерах компонент. На счастье вспомнилась весьма удобная штучка: свойство директив (ну и компонент, как наследников, естественно) - exportAs.
Что же дает exportAs?
Вот что нам сообщает документация:
exportAs: Определяет имя, которое может быть использовано в шаблоне для назначения этой директивы переменной.
Как же этим воспользоваться?
Допустим, у нас есть сервис, который авторизовался и хранит данные о пользователе.
@Injectable({
providedIn: 'root',
})
export class UserService {
private user$: User;
set user(user: User) {
this.user$ = user;
}
get user() {
return this.user$;
}
constructor(
private _http: HttpClientWrapperService
) {
}
...
}
И есть класс, описывающий пользователя
export class User {
id: number;
name: string;
claims: Claim[];
get roles() {
return this.claims?.map(_ => _.code);
}
get isAdmin() {
return this.roles?.indexOf('ADMIN') >= 0;
}
get isUser() {
return this.isAdmin || this.roles?.indexOf('USER') >= 0;
}
get isGuest() {
return this.isAdmin || this.isUser || this.roles?.indexOf('GUEST') >= 0;
}
...
}
Что нам нужно - создать директиву, которая будет иметь доступ к пользователю, например такую:
import {Directive} from '@angular/core';
import {UserService} from '../../service/user.service';
@Directive({
selector: '[appUserContext]',
exportAs: 'userContext'
})
export class UserContextDirective {
get user() {
return this._user?.user;
}
constructor(private _user: UserService) {
}
}
И использовать ее по надобности в шаблонах приложения. Например так, если добавлять у удалять объект может только администратор.
<div class="column content-block" appUserContext #user="userContext">
<app-toolbar>
<ng-container *ngIf="user.user?.isAdmin">
<button app-button (click)="add()">
<app-icon [name]="'add'"></teta-icon>
</button>
<button app-button (click)="delete()">
<teta-icon [name]="'delete'"></teta-icon>
</button>
</ng-container>
</app-toolbar>
</div>
Также данная функция может использована для получения доступа к директивам и компонентам в шаблоне, без необходимости использовать ViewChild там, где для этого нет большой необходимости.
Спасибо за внимание, надеюсь эта информация окажется для вас полезной и сэкономит кому-то время.