Front-end @ Hypersonix
Directives are classes that add additional behavior to elements in your Angular applications.
Attribute Directives
Structural Directives
Attribute directive to add classes based on some condition.
Takes a number as input
Checks if the number is positive or negative
Just adds a class to the host element
Add arrow based on the change
@Directive({
selector: '[deltaColorArrow]',
})
export class DeltaColorArrowDirective implements OnChanges {
@Input() value!: number;
private arrow!: HTMLElement;
@HostBinding('class')
get classes() {
return this.value > 0 ? 'positive' : 'negative';
}
constructor(@Inject(DOCUMENT) private document: Document, private el: ElementRef) {}
ngOnChanges(changes: SimpleChanges): void {
const currentValue = changes.value?.currentValue;
if (currentValue != undefined) {
const el = this.el.nativeElement as HTMLElement;
if (!this.arrow) {
this.arrow = this.getArrowElement(currentValue);
el.appendChild(this.arrow);
} else {
this.arrow.textContent = currentValue > 0 ? '⮝' : '⮟';
}
}
}
getArrowElement(value: number) {
const arrow = this.document.createElement('span');
arrow.style.setProperty('margin-left', '4px');
arrow.textContent = value > 0 ? '⮝' : '⮟';
return arrow;
}
}
<p class="negative">
-5.6%
<span style="margin-left: 4px;">⮟</span>
</p>
<p deltaColorArrow [value]="-5.6">-5.6%</p>
Attribute directive
Exports the directive to be used in a template
Manage state inside the directive
@Directive({
selector: '[appUiFullscreen]',
exportAs: 'fullscreen',
})
export class UiFullscreenDirective {
private isMaximizedSubject = new BehaviorSubject(false);
isMaximized$ = this.isMaximizedSubject.asObservable();
constructor(private el: ElementRef) {}
toggle() {
if (this.isMaximizedSubject?.getValue()) this.minimize();
else this.maximize();
}
maximize() {
if (this.el) {
this.isMaximizedSubject.next(true);
this.nativeElement.classList.add('fullscreen');
if (Fullscreen.isEnabled) {
Fullscreen.request();
}
}
}
minimize() {
if (this.el) {
this.isMaximizedSubject.next(false);
this.nativeElement.classList.remove('fullscreen');
if (Fullscreen.isEnabled) {
Fullscreen.exit();
}
}
}
private get nativeElement() {
return this.el.nativeElement as HTMLElement;
}
}
<div appUiFullscreen #fullscreen="fullscreen">
<header>
<p>Total Sales Report</p>
<div>
<button (click)="fullscreen.minimize()">🗕</button>
<button (click)="fullscreen.maximize()">🗖</button>
</div>
</header>
<div class="body"></div>
</div>
.fullscreen {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
}
Structural directive
Displays content only if authorized
Pass feature and permission as input
Validates the permission and embed the item in view
@Directive({
selector: '[appUiPermissions]',
})
export class UiPermissionsDirective implements OnInit, OnDestroy {
private loggedInUser!: User;
private permission!: Permissions;
private feature!: string;
private subscription!: Subscription;
@Input()
set appUiPermissions(permission: Permissions) {
this.permission = permission;
this.updateView();
}
@Input()
set appUiPermissionsFeature(feature: string) {
this.feature = feature;
this.updateView();
}
constructor(private tpl: TemplateRef<any>,
private vcr: ViewContainerRef,
private authService: AuthService) {}
ngOnInit() {
this.subscription = this.authService.loggedUser$.subscribe((user) => {
this.loggedInUser = user;
this.updateView();
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
private updateView() {
this.vcr.clear();
if (this.hasPermission()) {
this.vcr.createEmbeddedView(this.tpl);
} else {
this.vcr.clear();
}
}
private hasPermission() {
if (!this.loggedInUser) return false;
const featurePermissions = this.loggedInUser.permissions[this.feature];
if (featurePermissions) {
return featurePermissions.includes(this.permission);
}
return false;
}
}
<footer>
<button *appUiPermissions="'READ'; feature: 'product'">
View
</button>
<button *appUiPermissions="'UPDATE'; feature: 'product'">
Edit
</button>
<button *appUiPermissions="'DELETE'; feature: 'product'">
Delete
</button>
</footer>
export enum Permissions {
create = 'CREATE',
read = 'READ',
update = 'UPDATE',
delete = 'DELETE',
}
const permissions = {
product: [
'CREATE',
'READ',
'UPDATE'
'DELETE'
]
}