Abstract entities map service
This commit is contained in:
parent
0bab458476
commit
492ac8d7c0
108
src/lib/services/entities-map.service.ts
Normal file
108
src/lib/services/entities-map.service.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { filter, startWith } from 'rxjs/operators';
|
||||
import { Entity } from '../listing';
|
||||
import { RequiredParam, shareLast, Validate } from '../utils';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export abstract class EntitiesMapService<E extends Entity<I>, I> {
|
||||
protected readonly _map = new Map<string, BehaviorSubject<E[]>>();
|
||||
private readonly _entityChanged$ = new Subject<E>();
|
||||
private readonly _entityDeleted$ = new Subject<E>();
|
||||
|
||||
protected constructor(@Inject('ENTITY_PRIMARY_KEY') protected readonly _primaryKey: string) {}
|
||||
|
||||
get$(key: string) {
|
||||
if (!this._map.has(key)) {
|
||||
this._map.set(key, new BehaviorSubject<E[]>([]));
|
||||
}
|
||||
|
||||
return this._getBehaviourSubject(key).asObservable();
|
||||
}
|
||||
|
||||
has(dossierId: string) {
|
||||
return this._map.has(dossierId);
|
||||
}
|
||||
|
||||
get(key: string): E[];
|
||||
get(key: string, id: string): E | undefined;
|
||||
get(key: string, id?: string): E | E[] | undefined {
|
||||
const value = this._getBehaviourSubject(key)?.value;
|
||||
if (!id) {
|
||||
return value ?? [];
|
||||
}
|
||||
return value?.find(item => item.id === id);
|
||||
}
|
||||
|
||||
set(key: string, entities: E[]): void {
|
||||
if (!this._map.has(key)) {
|
||||
this._map.set(key, new BehaviorSubject<E[]>(entities));
|
||||
return entities.forEach(entity => this._entityChanged$.next(entity));
|
||||
}
|
||||
|
||||
const changedEntities: E[] = [];
|
||||
const deletedEntities = this.get(key).filter(oldEntity => !entities.find(newEntity => newEntity.id === oldEntity.id));
|
||||
|
||||
// Keep old object references for unchanged entities
|
||||
const newEntities: E[] = entities.map(newEntity => {
|
||||
const oldEntity: E | undefined = this.get(key, newEntity.id);
|
||||
|
||||
if (oldEntity && newEntity.isEqual(oldEntity)) {
|
||||
return oldEntity;
|
||||
}
|
||||
|
||||
changedEntities.push(newEntity);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
this._getBehaviourSubject(key).next(newEntities);
|
||||
|
||||
// Emit observables only after entities have been updated
|
||||
|
||||
for (const entity of changedEntities) {
|
||||
this._entityChanged$.next(entity);
|
||||
}
|
||||
|
||||
for (const entity of deletedEntities) {
|
||||
this._entityDeleted$.next(entity);
|
||||
}
|
||||
}
|
||||
|
||||
replace(entities: E[]) {
|
||||
const key = this._pluckPrimaryKey(entities[0]);
|
||||
const entityIds = entities.map(entity => entity.id);
|
||||
const existingEntities = this.get(key).filter(entity => entityIds.includes(entity.id));
|
||||
const newEntities = entities.filter(entity => {
|
||||
const old = existingEntities.find(e => e.id === entity.id);
|
||||
return !old || !entity.isEqual(old);
|
||||
});
|
||||
if (newEntities.length) {
|
||||
const all = this.get(key).filter(e => !newEntities.map(entity => entity.id).includes(e.id));
|
||||
this.set(key, [...all, ...newEntities]);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate()
|
||||
watch$(@RequiredParam() key: string, @RequiredParam() entityId: string): Observable<E> {
|
||||
return this._entityChanged$.pipe(
|
||||
filter(entity => entity.id === entityId),
|
||||
startWith(this.get(key, entityId) as E),
|
||||
shareLast(),
|
||||
);
|
||||
}
|
||||
|
||||
watchDeleted$(entityId: string): Observable<E> {
|
||||
return this._entityDeleted$.pipe(filter(entity => entity.id === entityId));
|
||||
}
|
||||
|
||||
private _pluckPrimaryKey(entity: E): string {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return entity[this._primaryKey] as string;
|
||||
}
|
||||
|
||||
private _getBehaviourSubject(key: string): BehaviorSubject<E[]> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this._map.get(key)!;
|
||||
}
|
||||
}
|
||||
@ -4,3 +4,4 @@ export * from './error-message.service';
|
||||
export * from './generic.service';
|
||||
export * from './composite-route.guard';
|
||||
export * from './stats.service';
|
||||
export * from './entities-map.service';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user