Reactable Directive
RxJS observables are already integrated with Angular and developers can subscribe to a reactable’s state observable via the subscribe
method or with Angular’s async
pipe.
Alternatively, we can create a structural directive that subscribes to a reactable’s state observable and makes the state and action methods easily accessible in the template.
import {
Directive,
Input,
TemplateRef,
ViewContainerRef,
OnDestroy,
} from '@angular/core';
import { Reactable } from '@reactables/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Directive({
selector: '[reactable]',
standalone: true,
})
export class ReactableDirective<T, S> implements OnDestroy {
private $destroy = new Subject();
state: T | null = null;
actions: S | null = null;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
@Input() set reactable(reactable: Reactable<T, S>) {
const [state$, actions] = reactable;
state$.pipe(takeUntil(this.$destroy)).subscribe((state) => {
this.state = state;
});
this.actions = actions;
this.viewContainer.createEmbeddedView(this.templateRef, this);
}
ngOnDestroy(): void {
this.$destroy.next(true);
this.$destroy.complete();
}
}
Example Usage
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { RxToggle } from './RxToggle';
import { ReactableDirective } from './reactable.directive';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, ReactableDirective],
template: `
<div *reactable="rxToggle; let state = state; let actions = actions;">
<h1>Angular Reactable Toggle: {{ state ? 'on' : 'off' }}</h1>
<button (click)="actions.toggleOn()">Toggle On </button>
<button (click)="actions.toggleOff()">Toggle Off </button>
<button (click)="actions.toggle()">Toggle </button>
</div>
`,
})
export class App {
rxToggle = RxToggle();
}
bootstrapApplication(App);