rxStyle or rxClass as an alternative of style and class directives
Problem Solved By The Feature
Using [style.css-property]="value" or [class.className]="value" in Angular requires change detection to apply the changes. When we want to change a style of only one html element it's a lot of unnecessary code to execute.
Solution
Would be great to have rxStyle and rxClass directives. It can be implemented using Renderer2 instance.
For example (very dirty, but it shows the basic usage):
import {Directive, ElementRef, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges} from "@angular/core";
import {Observable, Subject, takeUntil} from "rxjs";
@Directive({
selector: '[rxClass]'
})
export class RxClassDirective implements OnDestroy, OnChanges {
@Input()
cssClass!: string = '';
@Input()
stream$!: Observable<void>;
private readonly destroy$: Subject<void> = new Subject<void>();
constructor(private readonly renderer: Renderer2,
private readonly elementRef: ElementRef) {
}
ngOnChanges(changes: SimpleChanges) {
if (changes.stream) {
this.destroy();
if (changes.stream.currentValue) {
this.onStreamChanges();
} else {
this.renderer.addClass(this.elementRef.nativeElement, this.cssClass);
}
}
}
ngOnDestroy() {
this.destroy();
}
private onStreamChanges() {
this.stream$
.pipe(takeUntil(this.destroy$))
.subscribe(value => {
if (value) {
this.renderer.addClass(this.elementRef.nativeElement, this.cssClass);
} else {
this.renderer.removeClass(this.elementRef.nativeElement, this.cssClass);
}
});
}
private destroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Final version should have nicer API, but the main idea is the same.
Another problem is changing styles of a host component. The possible implementation is to use a service provided in a component instead of @HostBinding decorator. From Angular 14 there's another, (in my opinion) cooler way of doing it. It's possible to create hook-like function which will provide object containing current value and a setter. I described it here: https://galczo5.github.io/mythical-angular/posts/togglers/.
Both service and hook-like function for me are better than triggering unnecessary change detection.
Alternatives Considered
We can always use Renderer2 in component body. Not a very clever way of implementing it.
Additional Context
A lot of developers are still using the Angular directives and triggers change detection, when they want to change only one style/class on one element. Having it in lib like rxAngular would be great start for them to migrate to more efficient code.
Hi @galczo5, there is already an opened PR for a rxClass directive (#1041 and #1048).
Nice! Thx for info. I see that it's opened since 28 Oct 2021. Do you need any help with it?
Yeah, there is some stuff waiting in the queue, our priority is to work on the template and cdk first stable release before introducing new stuff, but any help is always appreciated!
RxClass and RxStyle are both in development now. just added the RxStyle PR: https://github.com/rx-angular/rx-angular/pull/1465
with the new directive composition API this is gonna be glorious