Skip to content
Snippets Groups Projects
Commit a366c184 authored by Florent Poittevin's avatar Florent Poittevin
Browse files

Add subscription system

parent 740cb79f
No related branches found
No related tags found
1 merge request!24Fpo/refacto generic crud
Showing
with 358 additions and 9 deletions
import {CoreAbstractAngularElement} from "@app/core/models/angular/core-abstract-angular-element.model";
export class CoreAbstractComponent extends CoreAbstractAngularElement {
}
import {CoreAbstractAngularElement} from "@app/core/models/angular/core-abstract-angular-element.model";
export class CoreAbstractDirective extends CoreAbstractAngularElement {
}
import {
AfterContentChecked,
AfterContentInit,
AfterViewChecked,
AfterViewInit,
ChangeDetectorRef,
DoCheck,
EventEmitter,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
} from "@angular/core";
import {SubscriptionManager} from "@app/core/models/managers/subscription-manager.model";
import {CounterSubject} from "@app/core/models/observables/counter-subject.model";
import {isFunction, isTruthyObject} from "@app/core/tools/is/is.tool";
import {Observable, Subscription} from "rxjs";
export class CoreAbstractAngularElement implements OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
/* Subscription manager */
protected readonly _subscriptionManager: SubscriptionManager = new SubscriptionManager();
/* Angular state */
get changed(): boolean {
return this._ngOnChangesCS.hasCount();
}
get changesCount(): number {
return this._ngOnChangesCS.getValue();
}
private _initialized: boolean = false;
get initialized(): boolean {
return this._initialized;
}
get checked(): boolean {
return this._ngDoCheckCS.hasCount();
}
get checksCount(): number {
return this._ngDoCheckCS.getValue();
}
private _contentInitialized: boolean = false;
get contentInitialized(): boolean {
return this._contentInitialized;
}
get contentChecked(): boolean {
return this._ngAfterContentCheckedCS.hasCount();
}
get contentChecksCount(): number {
return this._ngAfterContentCheckedCS.getValue();
}
private _viewInitialized: boolean = false;
get viewInitialized(): boolean {
return this._viewInitialized;
}
get viewChecked(): boolean {
return this._ngAfterViewCheckedCS.hasCount();
}
get viewChecksCount(): number {
return this._ngAfterViewCheckedCS.getValue();
}
private _destroyed: boolean = false;
get destroyed(): boolean {
return this._destroyed;
}
/* Angular frequent lifecycle hooks counter subjects */
private readonly _ngOnChangesCS: CounterSubject = new CounterSubject();
private readonly _ngDoCheckCS: CounterSubject = new CounterSubject();
private readonly _ngAfterContentCheckedCS: CounterSubject = new CounterSubject();
private readonly _ngAfterViewCheckedCS: CounterSubject = new CounterSubject();
/* Angular occasional lifecycle hooks event emitters */
private readonly _ngOnInitEventEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
private readonly _ngAfterContentInitEventEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
private readonly _ngAfterViewInitEventEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
private readonly _ngOnDestroyEventEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
/* Angular lifecycle hooks value event emitters */
private readonly _ngOnChangesValueEventEmitter: EventEmitter<SimpleChanges> = new EventEmitter<SimpleChanges>();
/* Angular lifecycle hooks base implementation */
ngOnChanges(changes: SimpleChanges): void {
// Fix ngOnInit
if (this.changed && !this.initialized) {
this.ngOnInit();
}
this._ngOnChangesValueEventEmitter.emit(changes);
this._ngOnChangesCS.increment();
}
ngOnInit(): void {
if (!this._initialized) {
this._initialized = true;
this._ngOnInitEventEmitter.emit(true);
} else {
this._ngOnInitEventEmitter.emit(false);
}
}
ngDoCheck(): void {
this._ngDoCheckCS.increment();
}
ngAfterContentInit(): void {
if (!this._contentInitialized) {
this._contentInitialized = true;
this._ngAfterContentInitEventEmitter.emit(true);
} else {
this._ngAfterContentInitEventEmitter.emit(false);
}
}
ngAfterContentChecked(): void {
this._ngAfterContentCheckedCS.increment();
}
ngAfterViewInit(): void {
if (!this._viewInitialized) {
this._viewInitialized = true;
this._ngAfterViewInitEventEmitter.emit(true);
} else {
this._ngAfterViewInitEventEmitter.emit(false);
}
}
ngAfterViewChecked(): void {
this._ngAfterViewCheckedCS.increment();
}
ngOnDestroy(): void {
if (!this._destroyed) {
this._destroyed = true;
this._ngOnDestroyEventEmitter.emit(true);
} else {
this._ngOnDestroyEventEmitter.emit(false);
}
this._subscriptionManager.clearAndUnsubscribeAll();
}
/* Angular lifecycle hooks observables */
readonly ngOnChangesObs: Observable<number> = this._ngOnChangesCS.asObservable();
readonly ngOnChangesValueObs: Observable<SimpleChanges> = this._ngOnChangesValueEventEmitter.asObservable();
readonly ngOnInitObs: Observable<boolean> = this._ngOnInitEventEmitter.asObservable();
readonly ngDoCheckObs: Observable<number> = this._ngDoCheckCS.asObservable();
readonly ngAfterContentInitObs: Observable<boolean> = this._ngAfterContentInitEventEmitter.asObservable();
readonly ngAfterContentCheckedObs: Observable<number> = this._ngAfterContentCheckedCS.asObservable();
readonly ngAfterViewInitObs: Observable<boolean> = this._ngAfterViewInitEventEmitter.asObservable();
readonly ngAfterViewCheckedObs: Observable<number> = this._ngAfterViewCheckedCS.asObservable();
readonly ngOnDestroyObs: Observable<boolean> = this._ngOnDestroyEventEmitter.asObservable();
/* Detect changes */
detectChanges(changeDetectorRef: ChangeDetectorRef): boolean {
if (this.initialized
&& !this.destroyed
&& isTruthyObject(changeDetectorRef)
&& isFunction(changeDetectorRef.detectChanges)) {
changeDetectorRef.detectChanges();
return true;
}
return false;
}
/* Subscribe */
subscribe<T>(observable: Observable<T>,
onNext?: (value: T) => void,
onError?: (error: Error) => void,
onComplete?: () => void): Subscription {
return this._subscriptionManager.subscribe(observable, onNext, onError, onComplete);
}
}
import {SubscriptionUtil} from "@app/core/utils/subscription.util";
import {Observable, Subscription} from "rxjs";
export class SubscriptionManager {
private readonly _subscriptions: Subscription[] = [];
geSubscriptions(): Subscription[] {
return this._subscriptions.slice();
}
subscribe<T>(observable: Observable<T>,
onNext?: (value: T) => void,
onError?: (error: Error) => void,
onComplete?: () => void): Subscription {
const subscription = observable.subscribe(onNext, onError, onComplete);
this.add(subscription);
return subscription;
}
add(subscription: Subscription): boolean {
if (SubscriptionUtil.isUnsubscribable(subscription)) {
this._subscriptions.push(subscription);
return true;
}
return false;
}
addMultiple(...subscriptions: Subscription[]): number {
return subscriptions.reduce((seed, next) => seed + (this.add(next) ? 1 : 0), 0);
}
remove(subscription: Subscription): boolean {
const index = this._subscriptions.indexOf(subscription);
if (index < 0) {
return false;
}
this._subscriptions.splice(index, 1);
return true;
}
clear(): boolean {
if (this._subscriptions.length === 0) {
return false;
}
this._subscriptions.splice(0);
return true;
}
removeAndUnsubscribe(subscription: Subscription): boolean {
return this.remove(subscription) && SubscriptionUtil.unsubscribe(subscription);
}
clearAndUnsubscribeAll(): number {
return this._subscriptions.splice(0).reduce((seed, next) => seed + (SubscriptionUtil.unsubscribe(next) ? 1 : 0), 0);
}
}
import {isNumberReal} from "@app/core/tools/is/is.tool";
import {BehaviorSubject} from "rxjs";
export class CounterSubject extends BehaviorSubject<number> {
private static _toSafeValue(value: any): number {
return isNumberReal(value) && value >= 0 ? value : 0;
}
constructor(initialCount: number = 0) {
super(CounterSubject._toSafeValue(initialCount));
}
next(count: any): void {
super.next(CounterSubject._toSafeValue(parseFloat(count)));
}
hasCount(): boolean {
return this.getValue() > 0;
}
increment(increment: number = 1): number {
const result = this.getValue() + parseFloat(increment as any);
this.next(result);
return result;
}
decrement(decrement: number = 1): number {
const result = this.getValue() - parseFloat(decrement as any);
this.next(result);
return result;
}
}
import {CoreAbstractAngularElement} from "@app/core/models/angular/core-abstract-angular-element.model";
export class CoreAbstractPipe extends CoreAbstractAngularElement {
}
File moved
import {isFunction, isReference, isTruthy} from "@app/core/tools/is/is.tool";
import {Subscription} from "rxjs";
export class SubscriptionUtil {
static isUnsubscribable(subscription: Subscription): boolean {
return isReference(subscription) && isFunction(subscription.unsubscribe) && !subscription.closed;
}
static unsubscribe(subscription: Subscription): boolean {
if (this.isUnsubscribable(subscription)) {
subscription.unsubscribe();
return isTruthy(subscription.closed);
}
return false;
}
}
import {CoreAbstractComponent} from "@app/core/components/core-abstract/core-abstract.component";
export class AbstractComponent extends CoreAbstractComponent {
}
import {CoreAbstractDirective} from "@app/core/directives/core-abstract/core-abstract.directive";
export class AbstractDirective extends CoreAbstractDirective {
}
......@@ -16,7 +16,7 @@ export abstract class BaseDirective implements OnDestroy {
});
}
addToSubscription(subscription: Subscription) {
addToSubscription(subscription: Subscription): void {
this.listSubscription.push(subscription);
}
}
import {CoreAbstractPipe} from "@app/core/pipes/core-abstract/core-abstract.pipe";
export class AbstractPipe extends CoreAbstractPipe {
}
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean} from "@app/shared/tools/is.tool";
import {isBoolean} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isBoolean",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isFalse} from "@app/shared/tools/is.tool";
import {isFalse} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isFalse",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isFunction} from "@app/shared/tools/is.tool";
import {isBoolean, isFunction} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isFunction",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isNumberFinite} from "@app/shared/tools/is.tool";
import {isBoolean, isNumberFinite} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isNumberFinite",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isNumberInvalid} from "@app/shared/tools/is.tool";
import {isBoolean, isNumberInvalid} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isNumberInvalid",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isNumberValid} from "@app/shared/tools/is.tool";
import {isBoolean, isNumberValid} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isNumberValid",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isNumber} from "@app/shared/tools/is.tool";
import {isBoolean, isNumber} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isNumber",
......
import {Pipe, PipeTransform} from "@angular/core";
import {isBoolean, isTrue} from "@app/shared/tools/is.tool";
import {isBoolean, isTrue} from "@app/core/tools/is/is.tool";
@Pipe({
name: "isTrue",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment