Skip to content

Instantly share code, notes, and snippets.

@lehuynhdong1
Last active September 18, 2023 07:17
Show Gist options
  • Save lehuynhdong1/c202119185adc20751597df5a5828b64 to your computer and use it in GitHub Desktop.
Save lehuynhdong1/c202119185adc20751597df5a5828b64 to your computer and use it in GitHub Desktop.
Directive BaseComponent for convenient use of the Angular lifecycle hook
import { AfterViewInit, Directive, HostBinding, OnDestroy, OnInit, inject } from '@angular/core';
import { Subscribable } from './subscribable';
import { ThemeService } from '../../services/theme.service';
import { Theme } from '../../enums/theme.enum';
import { TranslocoService } from '@ngneat/transloco';
@Directive()
export abstract class BaseComponent extends Subscribable implements OnInit, AfterViewInit, OnDestroy {
@HostBinding('style.display') display = 'block';
public ngOnInit(): void {
this.onInit();
}
public ngAfterViewInit(): void {
this.onAfterViewInit();
}
public override ngOnDestroy(): void {
super.ngOnDestroy();
this.onDestroy();
}
protected onInit(): void {
// virtual method
}
protected onAfterViewInit(): void {
// virtual method
}
protected onDestroy(): void {
// virtual method
}
}
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BaseComponent } from '../core/components/base-component/base.component';
import { filter, of, takeUntil } from 'rxjs';
import { NavigationEnd, Router } from '@angular/router';
@Component({
selector: 'app-same-use-case',
standalone: true,
imports: [CommonModule],
template: `<p>same-use-case works!</p>`,
styles: [],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SameUseCaseComponent extends BaseComponent {
private readonly router = inject(Router);
protected override onInit(): void {
// ngOnInit
// ...
// subscribe
this.subscribe(of(true), res => {
console.log(res);
});
// subscribeOne
this.subscribeOne(of(true), res => {
console.log(res);
});
// addSubscription
const subscription$ = of(true).subscribe({
next: res => {
console.log(res);
},
error: err => {
console.log(err);
},
});
this.addSubscription(subscription$);
// onDestroySubject
this.router.events
.pipe(
filter(event => event instanceof NavigationEnd),
takeUntil(this.onDestroySubject)
)
.subscribe(event => {
console.log(event);
});
}
protected override onAfterViewInit(): void {
// ngAfterViewInit
}
protected override onDestroy(): void {
// ngDestroy
}
}
import { Directive, OnDestroy } from '@angular/core';
import { Observable, Subject, Subscription } from 'rxjs';
import { SubscriptionCollection } from './subscription-collection';
import { take } from 'rxjs/operators';
@Directive()
export abstract class Subscribable implements OnDestroy {
protected onDestroySubject: Subject<void> = new Subject();
protected subscriptionCollection = new SubscriptionCollection();
public ngOnDestroy(): void {
// Subject
// using for takeUntil
this.onDestroySubject.next();
this.onDestroySubject.complete();
// Subscription
this.subscriptionCollection.clear();
this.onUnsubscribe();
}
protected subscribe<T>(observable: Observable<T>, handler: (data: T) => void): void {
this.subscriptionCollection.add(observable, handler);
}
protected subscribeOne<T>(observable: Observable<T>, handler: (data: T) => void): void {
observable.pipe(take(1)).subscribe(data => handler(data));
}
protected addSubscription(...subscriptions: Array<Subscription>): void {
this.subscriptionCollection.addItems(subscriptions);
// Add finalize callback here to remove subscription from the subscription collection
// at the moment subscription has changed its status to COMPLETED
subscriptions.forEach(sub => {
const subscriptionRef = sub;
sub.add(() => {
this.subscriptionCollection.removeItem(subscriptionRef);
});
});
}
protected onUnsubscribe(): void {
// virtual method
}
}
import { Observable, Subscription } from 'rxjs';
export class SubscriptionCollection {
private subscriptionRefs: Subscription[] = [];
public clear(): void {
this.subscriptionRefs.forEach(s => !s.closed && s.unsubscribe());
}
public add<T>(observable: Observable<T>, handler: (data: T) => void): void {
const subscription: Subscription = observable.subscribe(data => {
handler(data);
});
this.subscriptionRefs.push(subscription);
}
public addItems(subscriptions: Subscription[]): void {
this.subscriptionRefs.push(...subscriptions);
}
public removeItem(subscriptions: Subscription): void {
const index = this.subscriptionRefs.findIndex(s => s === subscriptions);
if (index >= 0) {
this.subscriptionRefs.splice(index, 1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment