mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-13 13:03:04 +00:00
More refactoring + reusing the server's store on the client
This commit is contained in:
@@ -10,13 +10,6 @@ import { AppComponent } from './app.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||
|
||||
import { StoreModule } from "@ngrx/store";
|
||||
import { RouterStoreModule } from "@ngrx/router-store";
|
||||
import { StoreDevtoolsModule } from "@ngrx/store-devtools";
|
||||
|
||||
import { rootReducer } from './app.reducers';
|
||||
import { effects } from './app.effects';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
@@ -28,34 +21,6 @@ import { effects } from './app.effects';
|
||||
HomeModule,
|
||||
CoreModule.forRoot(),
|
||||
AppRoutingModule,
|
||||
/**
|
||||
* StoreModule.provideStore is imported once in the root module, accepting a reducer
|
||||
* function or object map of reducer functions. If passed an object of
|
||||
* reducers, combineReducers will be run creating your application
|
||||
* meta-reducer. This returns all providers for an @ngrx/store
|
||||
* based application.
|
||||
*/
|
||||
StoreModule.provideStore(rootReducer),
|
||||
|
||||
/**
|
||||
* @ngrx/router-store keeps router state up-to-date in the store and uses
|
||||
* the store as the single source of truth for the router's state.
|
||||
*/
|
||||
RouterStoreModule.connectRouter(),
|
||||
|
||||
/**
|
||||
* Store devtools instrument the store retaining past versions of state
|
||||
* and recalculating new states. This enables powerful time-travel
|
||||
* debugging.
|
||||
*
|
||||
* To use the debugger, install the Redux Devtools extension for either
|
||||
* Chrome or Firefox
|
||||
*
|
||||
* See: https://github.com/zalmoxisus/redux-devtools-extension
|
||||
*/
|
||||
StoreDevtoolsModule.instrumentOnlyWithExtension(),
|
||||
|
||||
effects
|
||||
],
|
||||
providers: [
|
||||
]
|
||||
|
@@ -3,6 +3,7 @@ import { routerReducer, RouterState } from "@ngrx/router-store";
|
||||
import { headerReducer, HeaderState } from './header/header.reducer';
|
||||
import { hostWindowReducer, HostWindowState } from "./shared/host-window.reducer";
|
||||
import { CoreState, coreReducer } from "./core/core.reducers";
|
||||
import { StoreActionTypes } from "./store.actions";
|
||||
|
||||
export interface AppState {
|
||||
core: CoreState;
|
||||
@@ -19,5 +20,10 @@ export const reducers = {
|
||||
};
|
||||
|
||||
export function rootReducer(state: any, action: any) {
|
||||
if (action.type === StoreActionTypes.REHYDRATE) {
|
||||
state = action.payload;
|
||||
}
|
||||
return combineReducers(reducers)(state, action);
|
||||
}
|
||||
|
||||
export const NGRX_CACHE_KEY = "NGRX_STORE";
|
||||
|
4
src/app/core/cache/cache-entry.ts
vendored
Normal file
4
src/app/core/cache/cache-entry.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface CacheEntry {
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
}
|
5
src/app/core/cache/object-cache.actions.ts
vendored
5
src/app/core/cache/object-cache.actions.ts
vendored
@@ -11,11 +11,12 @@ export class AddToObjectCacheAction implements Action {
|
||||
type = ObjectCacheActionTypes.ADD;
|
||||
payload: {
|
||||
objectToCache: CacheableObject;
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
};
|
||||
|
||||
constructor(objectToCache: CacheableObject, msToLive: number) {
|
||||
this.payload = { objectToCache, msToLive };
|
||||
constructor(objectToCache: CacheableObject, timeAdded: number, msToLive: number) {
|
||||
this.payload = { objectToCache, timeAdded, msToLive };
|
||||
}
|
||||
}
|
||||
|
||||
|
5
src/app/core/cache/object-cache.reducer.ts
vendored
5
src/app/core/cache/object-cache.reducer.ts
vendored
@@ -1,11 +1,12 @@
|
||||
import { ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
|
||||
import { hasValue } from "../../shared/empty.util";
|
||||
import { CacheEntry } from "./cache-entry";
|
||||
|
||||
export interface CacheableObject {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export interface ObjectCacheEntry {
|
||||
export class ObjectCacheEntry implements CacheEntry {
|
||||
data: CacheableObject;
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
@@ -39,7 +40,7 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio
|
||||
return Object.assign({}, state, {
|
||||
[action.payload.objectToCache.uuid]: {
|
||||
data: action.payload.objectToCache,
|
||||
timeAdded: new Date().getTime(),
|
||||
timeAdded: action.payload.timeAdded,
|
||||
msToLive: action.payload.msToLive
|
||||
}
|
||||
});
|
||||
|
12
src/app/core/cache/object-cache.service.ts
vendored
12
src/app/core/cache/object-cache.service.ts
vendored
@@ -4,6 +4,7 @@ import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from "./object-ca
|
||||
import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
|
||||
import { Observable } from "rxjs";
|
||||
import { hasNoValue } from "../../shared/empty.util";
|
||||
import { GenericConstructor } from "../shared/generic-constructor";
|
||||
|
||||
@Injectable()
|
||||
export class ObjectCacheService {
|
||||
@@ -12,22 +13,23 @@ export class ObjectCacheService {
|
||||
) {}
|
||||
|
||||
add(objectToCache: CacheableObject, msToLive: number): void {
|
||||
this.store.dispatch(new AddToObjectCacheAction(objectToCache, msToLive));
|
||||
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive));
|
||||
}
|
||||
|
||||
remove(uuid: string): void {
|
||||
this.store.dispatch(new RemoveFromObjectCacheAction(uuid));
|
||||
}
|
||||
|
||||
get<T extends CacheableObject>(uuid: string): Observable<T> {
|
||||
get<T extends CacheableObject>(uuid: string, ctor: GenericConstructor<T>): Observable<T> {
|
||||
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
|
||||
.filter(entry => this.isValid(entry))
|
||||
.map((entry: ObjectCacheEntry) => <T> entry.data);
|
||||
.distinctUntilChanged()
|
||||
.map((entry: ObjectCacheEntry) => <T> Object.assign(new ctor(), entry.data));
|
||||
}
|
||||
|
||||
getList<T extends CacheableObject>(uuids: Array<string>): Observable<Array<T>> {
|
||||
getList<T extends CacheableObject>(uuids: Array<string>, ctor: GenericConstructor<T>): Observable<Array<T>> {
|
||||
return Observable.combineLatest(
|
||||
uuids.map((id: string) => this.get<T>(id))
|
||||
uuids.map((id: string) => this.get<T>(id, ctor))
|
||||
);
|
||||
}
|
||||
|
||||
|
35
src/app/core/cache/request-cache.actions.ts
vendored
35
src/app/core/cache/request-cache.actions.ts
vendored
@@ -5,14 +5,15 @@ import { PaginationOptions } from "../shared/pagination-options.model";
|
||||
import { SortOptions } from "../shared/sort-options.model";
|
||||
|
||||
export const RequestCacheActionTypes = {
|
||||
FIND_BY_ID_REQUEST: type('dspace/core/cache/request/FIND_BY_ID_REQUEST'),
|
||||
FIND_ALL_REQUEST: type('dspace/core/cache/request/FIND_ALL_REQUEST'),
|
||||
FIND_BY_ID: type('dspace/core/cache/request/FIND_BY_ID'),
|
||||
FIND_ALL: type('dspace/core/cache/request/FIND_ALL'),
|
||||
SUCCESS: type('dspace/core/cache/request/SUCCESS'),
|
||||
ERROR: type('dspace/core/cache/request/ERROR')
|
||||
ERROR: type('dspace/core/cache/request/ERROR'),
|
||||
REMOVE: type('dspace/core/cache/request/REMOVE')
|
||||
};
|
||||
|
||||
export class FindAllRequestCacheAction implements Action {
|
||||
type = RequestCacheActionTypes.FIND_ALL_REQUEST;
|
||||
export class RequestCacheFindAllAction implements Action {
|
||||
type = RequestCacheActionTypes.FIND_ALL;
|
||||
payload: {
|
||||
key: string,
|
||||
service: OpaqueToken,
|
||||
@@ -38,8 +39,8 @@ export class FindAllRequestCacheAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class FindByIDRequestCacheAction implements Action {
|
||||
type = RequestCacheActionTypes.FIND_BY_ID_REQUEST;
|
||||
export class RequestCacheFindByIDAction implements Action {
|
||||
type = RequestCacheActionTypes.FIND_BY_ID;
|
||||
payload: {
|
||||
key: string,
|
||||
service: OpaqueToken,
|
||||
@@ -64,13 +65,15 @@ export class RequestCacheSuccessAction implements Action {
|
||||
payload: {
|
||||
key: string,
|
||||
resourceUUIDs: Array<string>,
|
||||
timeAdded: number,
|
||||
msToLive: number
|
||||
};
|
||||
|
||||
constructor(key: string, resourceUUIDs: Array<string>, msToLive: number) {
|
||||
constructor(key: string, resourceUUIDs: Array<string>, timeAdded, msToLive: number) {
|
||||
this.payload = {
|
||||
key,
|
||||
resourceUUIDs,
|
||||
timeAdded,
|
||||
msToLive
|
||||
};
|
||||
}
|
||||
@@ -91,8 +94,18 @@ export class RequestCacheErrorAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestCacheRemoveAction implements Action {
|
||||
type = RequestCacheActionTypes.REMOVE;
|
||||
payload: string;
|
||||
|
||||
constructor(key: string) {
|
||||
this.payload = key;
|
||||
}
|
||||
}
|
||||
|
||||
export type RequestCacheAction
|
||||
= FindAllRequestCacheAction
|
||||
| FindByIDRequestCacheAction
|
||||
= RequestCacheFindAllAction
|
||||
| RequestCacheFindByIDAction
|
||||
| RequestCacheSuccessAction
|
||||
| RequestCacheErrorAction;
|
||||
| RequestCacheErrorAction
|
||||
| RequestCacheRemoveAction;
|
||||
|
48
src/app/core/cache/request-cache.reducer.ts
vendored
48
src/app/core/cache/request-cache.reducer.ts
vendored
@@ -1,13 +1,17 @@
|
||||
import { PaginationOptions } from "../shared/pagination-options.model";
|
||||
import { SortOptions } from "../shared/sort-options.model";
|
||||
import {
|
||||
RequestCacheAction, RequestCacheActionTypes, FindAllRequestCacheAction,
|
||||
RequestCacheSuccessAction, RequestCacheErrorAction, FindByIDRequestCacheAction
|
||||
RequestCacheAction, RequestCacheActionTypes, RequestCacheFindAllAction,
|
||||
RequestCacheSuccessAction, RequestCacheErrorAction, RequestCacheFindByIDAction,
|
||||
RequestCacheRemoveAction
|
||||
} from "./request-cache.actions";
|
||||
import { OpaqueToken } from "@angular/core";
|
||||
import { CacheEntry } from "./cache-entry";
|
||||
import { hasValue } from "../../shared/empty.util";
|
||||
|
||||
export interface CachedRequest {
|
||||
service: OpaqueToken
|
||||
export class RequestCacheEntry implements CacheEntry {
|
||||
service: OpaqueToken;
|
||||
key: string;
|
||||
scopeID: string;
|
||||
resourceID: string;
|
||||
resourceUUIDs: Array<String>;
|
||||
@@ -21,7 +25,7 @@ export interface CachedRequest {
|
||||
}
|
||||
|
||||
export interface RequestCacheState {
|
||||
[key: string]: CachedRequest
|
||||
[key: string]: RequestCacheEntry
|
||||
}
|
||||
|
||||
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
|
||||
@@ -30,12 +34,12 @@ const initialState = Object.create(null);
|
||||
export const requestCacheReducer = (state = initialState, action: RequestCacheAction): RequestCacheState => {
|
||||
switch (action.type) {
|
||||
|
||||
case RequestCacheActionTypes.FIND_ALL_REQUEST: {
|
||||
return findAllRequest(state, <FindAllRequestCacheAction> action);
|
||||
case RequestCacheActionTypes.FIND_ALL: {
|
||||
return findAllRequest(state, <RequestCacheFindAllAction> action);
|
||||
}
|
||||
|
||||
case RequestCacheActionTypes.FIND_BY_ID_REQUEST: {
|
||||
return findByIDRequest(state, <FindByIDRequestCacheAction> action);
|
||||
case RequestCacheActionTypes.FIND_BY_ID: {
|
||||
return findByIDRequest(state, <RequestCacheFindByIDAction> action);
|
||||
}
|
||||
|
||||
case RequestCacheActionTypes.SUCCESS: {
|
||||
@@ -46,15 +50,21 @@ export const requestCacheReducer = (state = initialState, action: RequestCacheAc
|
||||
return error(state, <RequestCacheErrorAction> action);
|
||||
}
|
||||
|
||||
case RequestCacheActionTypes.REMOVE: {
|
||||
return removeFromCache(state, <RequestCacheRemoveAction> action);
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function findAllRequest(state: RequestCacheState, action: FindAllRequestCacheAction): RequestCacheState {
|
||||
function findAllRequest(state: RequestCacheState, action: RequestCacheFindAllAction): RequestCacheState {
|
||||
console.log('break here', state);
|
||||
return Object.assign({}, state, {
|
||||
[action.payload.key]: {
|
||||
key: action.payload.key,
|
||||
service: action.payload.service,
|
||||
scopeID: action.payload.scopeID,
|
||||
resourceUUIDs: [],
|
||||
@@ -66,9 +76,10 @@ function findAllRequest(state: RequestCacheState, action: FindAllRequestCacheAct
|
||||
});
|
||||
}
|
||||
|
||||
function findByIDRequest(state: RequestCacheState, action: FindByIDRequestCacheAction): RequestCacheState {
|
||||
function findByIDRequest(state: RequestCacheState, action: RequestCacheFindByIDAction): RequestCacheState {
|
||||
return Object.assign({}, state, {
|
||||
[action.payload.key]: {
|
||||
key: action.payload.key,
|
||||
service: action.payload.service,
|
||||
resourceID: action.payload.resourceID,
|
||||
resourceUUIDs: [],
|
||||
@@ -84,7 +95,7 @@ function success(state: RequestCacheState, action: RequestCacheSuccessAction): R
|
||||
isLoading: false,
|
||||
resourceUUIDs: action.payload.resourceUUIDs,
|
||||
errorMessage: undefined,
|
||||
timeAdded: new Date().getTime(),
|
||||
timeAdded: action.payload.timeAdded,
|
||||
msToLive: action.payload.msToLive
|
||||
})
|
||||
});
|
||||
@@ -99,4 +110,17 @@ function error(state: RequestCacheState, action: RequestCacheErrorAction): Reque
|
||||
});
|
||||
}
|
||||
|
||||
function removeFromCache(state: RequestCacheState, action: RequestCacheRemoveAction): RequestCacheState {
|
||||
if (hasValue(state[action.payload])) {
|
||||
let newCache = Object.assign({}, state);
|
||||
delete newCache[action.payload];
|
||||
|
||||
return newCache;
|
||||
}
|
||||
else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
73
src/app/core/cache/request-cache.service.ts
vendored
Normal file
73
src/app/core/cache/request-cache.service.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Injectable, OpaqueToken } from "@angular/core";
|
||||
import { Store } from "@ngrx/store";
|
||||
import { RequestCacheState, RequestCacheEntry } from "./request-cache.reducer";
|
||||
import { Observable } from "rxjs";
|
||||
import { hasNoValue } from "../../shared/empty.util";
|
||||
import {
|
||||
RequestCacheRemoveAction, RequestCacheFindAllAction,
|
||||
RequestCacheFindByIDAction
|
||||
} from "./request-cache.actions";
|
||||
import { SortOptions } from "../shared/sort-options.model";
|
||||
import { PaginationOptions } from "../shared/pagination-options.model";
|
||||
|
||||
@Injectable()
|
||||
export class RequestCacheService {
|
||||
constructor(
|
||||
private store: Store<RequestCacheState>
|
||||
) {}
|
||||
|
||||
findAll(
|
||||
key: string,
|
||||
service: OpaqueToken,
|
||||
scopeID?: string,
|
||||
paginationOptions?: PaginationOptions,
|
||||
sortOptions?: SortOptions
|
||||
): Observable<RequestCacheEntry> {
|
||||
if (!this.has(key)) {
|
||||
this.store.dispatch(new RequestCacheFindAllAction(key, service, scopeID, paginationOptions, sortOptions));
|
||||
}
|
||||
return this.get(key);
|
||||
}
|
||||
|
||||
findById(
|
||||
key: string,
|
||||
service: OpaqueToken,
|
||||
resourceID: string
|
||||
): Observable<RequestCacheEntry> {
|
||||
if (!this.has(key)) {
|
||||
this.store.dispatch(new RequestCacheFindByIDAction(key, service, resourceID));
|
||||
}
|
||||
return this.get(key);
|
||||
}
|
||||
|
||||
get(key: string): Observable<RequestCacheEntry> {
|
||||
return this.store.select<RequestCacheEntry>('core', 'cache', 'request', key)
|
||||
.filter(entry => this.isValid(entry))
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
let result: boolean;
|
||||
|
||||
this.store.select<RequestCacheEntry>('core', 'cache', 'request', key)
|
||||
.take(1)
|
||||
.subscribe(entry => result = this.isValid(entry));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private isValid(entry: RequestCacheEntry): boolean {
|
||||
if (hasNoValue(entry)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
const timeOutdated = entry.timeAdded + entry.msToLive;
|
||||
const isOutDated = new Date().getTime() > timeOutdated;
|
||||
if (isOutDated) {
|
||||
this.store.dispatch(new RequestCacheRemoveAction(entry.key));
|
||||
}
|
||||
return !isOutDated;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -5,6 +5,7 @@ import { isNotEmpty } from "../shared/empty.util";
|
||||
import { FooterComponent } from "./footer/footer.component";
|
||||
import { DSpaceRESTv2Service } from "./dspace-rest-v2/dspace-rest-v2.service";
|
||||
import { ObjectCacheService } from "./cache/object-cache.service";
|
||||
import { RequestCacheService } from "./cache/request-cache.service";
|
||||
import { CollectionDataService } from "./data-services/collection-data.service";
|
||||
import { ItemDataService } from "./data-services/item-data.service";
|
||||
|
||||
@@ -25,7 +26,8 @@ const PROVIDERS = [
|
||||
CollectionDataService,
|
||||
ItemDataService,
|
||||
DSpaceRESTv2Service,
|
||||
ObjectCacheService
|
||||
ObjectCacheService,
|
||||
RequestCacheService
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -6,7 +6,7 @@ import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.seriali
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
|
||||
import { Actions, Effect } from "@ngrx/effects";
|
||||
import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
|
||||
import { RequestCacheFindAllAction, RequestCacheFindByIDAction } from "../cache/request-cache.actions";
|
||||
import { CollectionDataService } from "./collection-data.service";
|
||||
|
||||
@Injectable()
|
||||
@@ -20,11 +20,11 @@ export class CollectionDataEffects extends DataEffects<Collection> {
|
||||
super(actions$, restApi, cache, dataService);
|
||||
}
|
||||
|
||||
protected getFindAllEndpoint(action: FindAllRequestCacheAction): string {
|
||||
protected getFindAllEndpoint(action: RequestCacheFindAllAction): string {
|
||||
return '/collections';
|
||||
}
|
||||
|
||||
protected getFindByIdEndpoint(action: FindByIDRequestCacheAction): string {
|
||||
protected getFindByIdEndpoint(action: RequestCacheFindByIDAction): string {
|
||||
return `/collections/${action.payload.resourceID}`;
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,18 @@
|
||||
import { Injectable, OpaqueToken } from "@angular/core";
|
||||
import { Store } from "@ngrx/store";
|
||||
import { DataService } from "./data.service";
|
||||
import { Collection } from "../shared/collection.model";
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { RequestCacheState } from "../cache/request-cache.reducer";
|
||||
import { RequestCacheService } from "../cache/request-cache.service";
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataService extends DataService<Collection> {
|
||||
name = new OpaqueToken('CollectionDataService');
|
||||
serviceName = new OpaqueToken('CollectionDataService');
|
||||
|
||||
constructor(
|
||||
store: Store<RequestCacheState>,
|
||||
cache: ObjectCacheService
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected requestCache: RequestCacheService,
|
||||
) {
|
||||
super(store, cache);
|
||||
super(Collection);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Actions, Effect } from "@ngrx/effects";
|
||||
import { Actions } from "@ngrx/effects";
|
||||
import { Observable } from "rxjs";
|
||||
import { DSpaceRESTV2Response } from "../dspace-rest-v2/dspace-rest-v2-response.model";
|
||||
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
|
||||
@@ -7,14 +7,14 @@ import { GlobalConfig } from "../../../config";
|
||||
import { CacheableObject } from "../cache/object-cache.reducer";
|
||||
import { Serializer } from "../serializer";
|
||||
import {
|
||||
RequestCacheActionTypes, FindAllRequestCacheAction, RequestCacheSuccessAction,
|
||||
RequestCacheErrorAction, FindByIDRequestCacheAction
|
||||
RequestCacheActionTypes, RequestCacheFindAllAction, RequestCacheSuccessAction,
|
||||
RequestCacheErrorAction, RequestCacheFindByIDAction
|
||||
} from "../cache/request-cache.actions";
|
||||
import { DataService } from "./data.service";
|
||||
|
||||
export abstract class DataEffects<T extends CacheableObject> {
|
||||
protected abstract getFindAllEndpoint(action: FindAllRequestCacheAction): string;
|
||||
protected abstract getFindByIdEndpoint(action: FindByIDRequestCacheAction): string;
|
||||
protected abstract getFindAllEndpoint(action: RequestCacheFindAllAction): string;
|
||||
protected abstract getFindByIdEndpoint(action: RequestCacheFindByIDAction): string;
|
||||
protected abstract getSerializer(): Serializer<T>;
|
||||
|
||||
constructor(
|
||||
@@ -24,13 +24,11 @@ export abstract class DataEffects<T extends CacheableObject> {
|
||||
private dataService: DataService<T>
|
||||
) {}
|
||||
|
||||
// TODO, results of a findall aren't retrieved from cache for now,
|
||||
// because currently the cache is more of an object store. We need to move
|
||||
// more towards memoization for things like this.
|
||||
// TODO, results of a findall aren't retrieved from cache yet
|
||||
protected findAll = this.actions$
|
||||
.ofType(RequestCacheActionTypes.FIND_ALL_REQUEST)
|
||||
.filter((action: FindAllRequestCacheAction) => action.payload.service === this.dataService.name)
|
||||
.flatMap((action: FindAllRequestCacheAction) => {
|
||||
.ofType(RequestCacheActionTypes.FIND_ALL)
|
||||
.filter((action: RequestCacheFindAllAction) => action.payload.service === this.dataService.serviceName)
|
||||
.flatMap((action: RequestCacheFindAllAction) => {
|
||||
//TODO scope, pagination, sorting -> when we know how that works in rest
|
||||
return this.restApi.get(this.getFindAllEndpoint(action))
|
||||
.map((data: DSpaceRESTV2Response) => this.getSerializer().deserializeArray(data))
|
||||
@@ -40,20 +38,20 @@ export abstract class DataEffects<T extends CacheableObject> {
|
||||
});
|
||||
})
|
||||
.map((ts: Array<T>) => ts.map(t => t.uuid))
|
||||
.map((ids: Array<string>) => new RequestCacheSuccessAction(action.payload.key, ids, GlobalConfig.cache.msToLive))
|
||||
.map((ids: Array<string>) => new RequestCacheSuccessAction(action.payload.key, ids, new Date().getTime(), GlobalConfig.cache.msToLive))
|
||||
.catch((errorMsg: string) => Observable.of(new RequestCacheErrorAction(action.payload.key, errorMsg)));
|
||||
});
|
||||
|
||||
protected findById = this.actions$
|
||||
.ofType(RequestCacheActionTypes.FIND_BY_ID_REQUEST)
|
||||
.filter((action: FindAllRequestCacheAction) => action.payload.service === this.dataService.name)
|
||||
.flatMap((action: FindByIDRequestCacheAction) => {
|
||||
.ofType(RequestCacheActionTypes.FIND_BY_ID)
|
||||
.filter((action: RequestCacheFindAllAction) => action.payload.service === this.dataService.serviceName)
|
||||
.flatMap((action: RequestCacheFindByIDAction) => {
|
||||
return this.restApi.get(this.getFindByIdEndpoint(action))
|
||||
.map((data: DSpaceRESTV2Response) => this.getSerializer().deserialize(data))
|
||||
.do((t: T) => {
|
||||
this.objectCache.add(t, GlobalConfig.cache.msToLive);
|
||||
})
|
||||
.map((t: T) => new RequestCacheSuccessAction(action.payload.key, [t.uuid], GlobalConfig.cache.msToLive))
|
||||
.map((t: T) => new RequestCacheSuccessAction(action.payload.key, [t.uuid], new Date().getTime(), GlobalConfig.cache.msToLive))
|
||||
.catch((errorMsg: string) => Observable.of(new RequestCacheErrorAction(action.payload.key, errorMsg)));
|
||||
});
|
||||
|
||||
|
@@ -1,39 +1,39 @@
|
||||
import { OpaqueToken } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
import { Store } from "@ngrx/store";
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { RequestCacheService } from "../cache/request-cache.service";
|
||||
import { CacheableObject } from "../cache/object-cache.reducer";
|
||||
import { RequestCacheState } from "../cache/request-cache.reducer";
|
||||
import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
|
||||
import { ParamHash } from "../shared/param-hash";
|
||||
import { isNotEmpty } from "../../shared/empty.util";
|
||||
import { GenericConstructor } from "../shared/generic-constructor";
|
||||
|
||||
export abstract class DataService<T extends CacheableObject> {
|
||||
abstract name: OpaqueToken;
|
||||
abstract serviceName: OpaqueToken;
|
||||
protected abstract objectCache: ObjectCacheService;
|
||||
protected abstract requestCache: RequestCacheService;
|
||||
|
||||
constructor(
|
||||
private store: Store<RequestCacheState>,
|
||||
private objectCache: ObjectCacheService
|
||||
) { }
|
||||
constructor(private modelType: GenericConstructor<T>) {
|
||||
|
||||
}
|
||||
|
||||
findAll(scopeID?: string): Observable<Array<T>> {
|
||||
const key = new ParamHash(this.name, 'findAll', scopeID).toString();
|
||||
this.store.dispatch(new FindAllRequestCacheAction(key, this.name, scopeID));
|
||||
//get an observable of the IDs from the store
|
||||
return this.store.select<Array<string>>('core', 'cache', 'request', key, 'resourceUUIDs')
|
||||
const key = new ParamHash(this.serviceName, 'findAll', scopeID).toString();
|
||||
return this.requestCache.findAll(key, this.serviceName, scopeID)
|
||||
//get an observable of the IDs from the RequestCache
|
||||
.map(entry => entry.resourceUUIDs)
|
||||
.flatMap((resourceUUIDs: Array<string>) => {
|
||||
// use those IDs to fetch the actual objects from the cache
|
||||
return this.objectCache.getList<T>(resourceUUIDs);
|
||||
// use those IDs to fetch the actual objects from the ObjectCache
|
||||
return this.objectCache.getList<T>(resourceUUIDs, this.modelType);
|
||||
});
|
||||
}
|
||||
|
||||
findById(id: string): Observable<T> {
|
||||
const key = new ParamHash(this.name, 'findById', id).toString();
|
||||
this.store.dispatch(new FindByIDRequestCacheAction(key, this.name, id));
|
||||
return this.store.select<Array<string>>('core', 'cache', 'request', key, 'resourceUUIDs')
|
||||
const key = new ParamHash(this.serviceName, 'findById', id).toString();
|
||||
return this.requestCache.findById(key, this.serviceName, id)
|
||||
.map(entry => entry.resourceUUIDs)
|
||||
.flatMap((resourceUUIDs: Array<string>) => {
|
||||
if(isNotEmpty(resourceUUIDs)) {
|
||||
return this.objectCache.get<T>(resourceUUIDs[0]);
|
||||
return this.objectCache.get<T>(resourceUUIDs[0], this.modelType);
|
||||
}
|
||||
else {
|
||||
return Observable.of(undefined);
|
||||
|
@@ -6,7 +6,7 @@ import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.seriali
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
|
||||
import { Actions, Effect } from "@ngrx/effects";
|
||||
import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
|
||||
import { RequestCacheFindAllAction, RequestCacheFindByIDAction } from "../cache/request-cache.actions";
|
||||
import { ItemDataService } from "./item-data.service";
|
||||
|
||||
@Injectable()
|
||||
@@ -20,11 +20,11 @@ export class ItemDataEffects extends DataEffects<Item> {
|
||||
super(actions$, restApi, cache, dataService);
|
||||
}
|
||||
|
||||
protected getFindAllEndpoint(action: FindAllRequestCacheAction): string {
|
||||
protected getFindAllEndpoint(action: RequestCacheFindAllAction): string {
|
||||
return '/items';
|
||||
}
|
||||
|
||||
protected getFindByIdEndpoint(action: FindByIDRequestCacheAction): string {
|
||||
protected getFindByIdEndpoint(action: RequestCacheFindByIDAction): string {
|
||||
return `/items/${action.payload.resourceID}`;
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,18 @@
|
||||
import { Injectable, OpaqueToken } from "@angular/core";
|
||||
import { Store } from "@ngrx/store";
|
||||
import { DataService } from "./data.service";
|
||||
import { Item } from "../shared/item.model";
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { RequestCacheState } from "../cache/request-cache.reducer";
|
||||
import { RequestCacheService } from "../cache/request-cache.service";
|
||||
|
||||
@Injectable()
|
||||
export class ItemDataService extends DataService<Item> {
|
||||
name = new OpaqueToken('ItemDataService');
|
||||
serviceName = new OpaqueToken('ItemDataService');
|
||||
|
||||
constructor(
|
||||
store: Store<RequestCacheState>,
|
||||
cache: ObjectCacheService
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected requestCache: RequestCacheService,
|
||||
) {
|
||||
super(store, cache);
|
||||
super(Item);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,13 +2,7 @@ import { Serialize, Deserialize } from "cerialize";
|
||||
import { Serializer } from "../serializer";
|
||||
import { DSpaceRESTV2Response } from "./dspace-rest-v2-response.model";
|
||||
import { DSpaceRESTv2Validator } from "./dspace-rest-v2.validator";
|
||||
|
||||
/**
|
||||
* ensures we can use 'typeof T' as a type
|
||||
* more details:
|
||||
* https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306
|
||||
*/
|
||||
type Constructor<T> = { new (...args: any[]): T } | ((...args: any[]) => T) | Function;
|
||||
import { GenericConstructor } from "../shared/generic-constructor";
|
||||
|
||||
/**
|
||||
* This Serializer turns responses from v2 of DSpace's REST API
|
||||
@@ -22,7 +16,7 @@ export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
|
||||
* @param modelType a class or interface to indicate
|
||||
* the kind of model this serializer should work with
|
||||
*/
|
||||
constructor(private modelType: Constructor<T>) {
|
||||
constructor(private modelType: GenericConstructor<T>) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -19,4 +19,5 @@ export class Bundle extends DSpaceObject {
|
||||
* The Item that owns this Bundle
|
||||
*/
|
||||
owner: Item;
|
||||
|
||||
}
|
||||
|
7
src/app/core/shared/generic-constructor.ts
Normal file
7
src/app/core/shared/generic-constructor.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* ensures we can use 'typeof T' as a type
|
||||
* more details:
|
||||
* https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306
|
||||
*/
|
||||
export type GenericConstructor<T> = { new (...args: any[]): T };
|
||||
|
@@ -35,4 +35,5 @@ export class Item extends DSpaceObject {
|
||||
* The Collection that owns this Item
|
||||
*/
|
||||
owner: Collection;
|
||||
|
||||
}
|
||||
|
@@ -1,88 +0,0 @@
|
||||
import { Inject, Injectable, isDevMode } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class DemoCacheService {
|
||||
static KEY = 'DemoCacheService';
|
||||
|
||||
constructor( @Inject('LRU') public _cache: Map<string, any>) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* check if there is a value in our store
|
||||
*/
|
||||
has(key: string | number): boolean {
|
||||
let _key = this.normalizeKey(key);
|
||||
return this._cache.has(_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* store our state
|
||||
*/
|
||||
set(key: string | number, value: any): void {
|
||||
let _key = this.normalizeKey(key);
|
||||
this._cache.set(_key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* get our cached value
|
||||
*/
|
||||
get(key: string | number): any {
|
||||
let _key = this.normalizeKey(key);
|
||||
return this._cache.get(_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* release memory refs
|
||||
*/
|
||||
clear(): void {
|
||||
this._cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert to json for the client
|
||||
*/
|
||||
dehydrate(): any {
|
||||
let json = {};
|
||||
this._cache.forEach((value: any, key: string) => json[key] = value);
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert server json into out initial state
|
||||
*/
|
||||
rehydrate(json: any): void {
|
||||
Object.keys(json).forEach((key: string) => {
|
||||
let _key = this.normalizeKey(key);
|
||||
let value = json[_key];
|
||||
this._cache.set(_key, value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* allow JSON.stringify to work
|
||||
*/
|
||||
toJSON(): any {
|
||||
return this.dehydrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert numbers into strings
|
||||
*/
|
||||
normalizeKey(key: string | number): string {
|
||||
if (isDevMode() && this._isInvalidValue(key)) {
|
||||
throw new Error('Please provide a valid key to save in the DemoCacheService');
|
||||
}
|
||||
|
||||
return key + '';
|
||||
}
|
||||
|
||||
_isInvalidValue(key): boolean {
|
||||
return key === null ||
|
||||
key === undefined ||
|
||||
key === 0 ||
|
||||
key === '' ||
|
||||
typeof key === 'boolean' ||
|
||||
Number.isNaN(<number>key);
|
||||
}
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/share';
|
||||
|
||||
import { DemoCacheService } from '../demo-cache.service';
|
||||
import { ApiService } from '../api.service';
|
||||
|
||||
export function hashCodeString(str: string): string {
|
||||
let hash = 0;
|
||||
if (str.length === 0) {
|
||||
return hash + '';
|
||||
}
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
let char = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return hash + '';
|
||||
}
|
||||
|
||||
// domain/feature service
|
||||
@Injectable()
|
||||
export class ModelService {
|
||||
// This is only one example of one Model depending on your domain
|
||||
constructor(public _api: ApiService, public _cache: DemoCacheService) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* whatever domain/feature method name
|
||||
*/
|
||||
get(url) {
|
||||
// you want to return the cache if there is a response in it.
|
||||
// This would cache the first response so if your API isn't idempotent
|
||||
// you probably want to remove the item from the cache after you use it. LRU of 10
|
||||
// you can use also hashCodeString here
|
||||
let key = url;
|
||||
|
||||
if (this._cache.has(key)) {
|
||||
return Observable.of(this._cache.get(key));
|
||||
}
|
||||
// you probably shouldn't .share() and you should write the correct logic
|
||||
return this._api.get(url)
|
||||
.do(json => {
|
||||
this._cache.set(key, json);
|
||||
})
|
||||
.share();
|
||||
}
|
||||
// don't cache here since we're creating
|
||||
create() {
|
||||
// TODO
|
||||
}
|
||||
}
|
@@ -7,7 +7,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateModule } from 'ng2-translate/ng2-translate';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
import { ModelService } from './model/model.service';
|
||||
|
||||
const MODULES = [
|
||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||
@@ -28,7 +27,6 @@ const COMPONENTS = [
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
ModelService,
|
||||
ApiService
|
||||
];
|
||||
|
||||
|
16
src/app/store.actions.ts
Normal file
16
src/app/store.actions.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { type } from "./shared/ngrx/type";
|
||||
import { Action } from "@ngrx/store";
|
||||
import { AppState } from "./app.reducers";
|
||||
|
||||
export const StoreActionTypes = {
|
||||
REHYDRATE: type('dspace/ngrx/rehydrate')
|
||||
};
|
||||
|
||||
export class RehydrateStoreAction implements Action {
|
||||
type = StoreActionTypes.REHYDRATE;
|
||||
|
||||
constructor(public payload: AppState) {}
|
||||
}
|
||||
|
||||
export type StoreAction
|
||||
= RehydrateStoreAction;
|
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UniversalModule, isBrowser, isNode, AUTO_PREBOOT } from 'angular2-universal/browser'; // for AoT we need to manually split universal packages
|
||||
import { UniversalModule, isBrowser, isNode } from 'angular2-universal/browser'; // for AoT we need to manually split universal packages
|
||||
import { IdlePreload, IdlePreloadModule } from '@angularclass/idle-preload';
|
||||
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
@@ -10,12 +10,18 @@ import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-tra
|
||||
|
||||
import { AppModule, AppComponent } from './app/app.module';
|
||||
import { SharedModule } from './app/shared/shared.module';
|
||||
import { DemoCacheService } from './app/shared/demo-cache.service';
|
||||
import { CoreModule } from "./app/core/core.module";
|
||||
|
||||
import { StoreModule, Store } from "@ngrx/store";
|
||||
import { RouterStoreModule } from "@ngrx/router-store";
|
||||
import { StoreDevtoolsModule } from "@ngrx/store-devtools";
|
||||
import { rootReducer, NGRX_CACHE_KEY, AppState } from './app/app.reducers';
|
||||
import { effects } from './app/app.effects';
|
||||
|
||||
// Will be merged into @angular/platform-browser in a later release
|
||||
// see https://github.com/angular/angular/pull/12322
|
||||
import { Meta } from './angular2-meta';
|
||||
import { RehydrateStoreAction } from "./app/store.actions";
|
||||
|
||||
// import * as LRU from 'modern-lru';
|
||||
|
||||
@@ -38,7 +44,6 @@ export function getResponse() {
|
||||
}
|
||||
|
||||
|
||||
// TODO(gdi2290): refactor into Universal
|
||||
export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
|
||||
@NgModule({
|
||||
@@ -60,6 +65,10 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
CoreModule.forRoot(),
|
||||
SharedModule,
|
||||
AppModule,
|
||||
StoreModule.provideStore(rootReducer),
|
||||
RouterStoreModule.connectRouter(),
|
||||
StoreDevtoolsModule.instrumentOnlyWithExtension(),
|
||||
effects
|
||||
],
|
||||
providers: [
|
||||
{ provide: 'isBrowser', useValue: isBrowser },
|
||||
@@ -70,23 +79,21 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
|
||||
{ provide: 'LRU', useFactory: getLRU, deps: [] },
|
||||
|
||||
DemoCacheService,
|
||||
|
||||
Meta,
|
||||
|
||||
// { provide: AUTO_PREBOOT, useValue: false } // turn off auto preboot complete
|
||||
]
|
||||
})
|
||||
export class MainModule {
|
||||
constructor(public cache: DemoCacheService) {
|
||||
constructor(public store: Store<AppState>) {
|
||||
// TODO(gdi2290): refactor into a lifecycle hook
|
||||
this.doRehydrate();
|
||||
}
|
||||
|
||||
doRehydrate() {
|
||||
let defaultValue = {};
|
||||
let serverCache = this._getCacheValue(DemoCacheService.KEY, defaultValue);
|
||||
this.cache.rehydrate(serverCache);
|
||||
let serverCache = this._getCacheValue(NGRX_CACHE_KEY, defaultValue);
|
||||
this.store.dispatch(new RehydrateStoreAction(serverCache));
|
||||
}
|
||||
|
||||
_getCacheValue(key: string, defaultValue: any): any {
|
||||
@@ -95,7 +102,7 @@ export class MainModule {
|
||||
if (win[UNIVERSAL_KEY] && win[UNIVERSAL_KEY][key]) {
|
||||
let serverCache = defaultValue;
|
||||
try {
|
||||
serverCache = JSON.parse(win[UNIVERSAL_KEY][key]);
|
||||
serverCache = win[UNIVERSAL_KEY][key];
|
||||
if (typeof serverCache !== typeof defaultValue) {
|
||||
console.log('Angular Universal: The type of data from the server is different from the default value type');
|
||||
serverCache = defaultValue;
|
||||
|
@@ -9,9 +9,14 @@ import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-tra
|
||||
|
||||
import { AppModule, AppComponent } from './app/app.module';
|
||||
import { SharedModule } from './app/shared/shared.module';
|
||||
import { DemoCacheService } from './app/shared/demo-cache.service';
|
||||
import { CoreModule } from "./app/core/core.module";
|
||||
|
||||
import { StoreModule, Store } from "@ngrx/store";
|
||||
import { RouterStoreModule } from "@ngrx/router-store";
|
||||
import { StoreDevtoolsModule } from "@ngrx/store-devtools";
|
||||
import { rootReducer, AppState, NGRX_CACHE_KEY } from './app/app.reducers';
|
||||
import { effects } from './app/app.effects';
|
||||
|
||||
// Will be merged into @angular/platform-browser in a later release
|
||||
// see https://github.com/angular/angular/pull/12322
|
||||
import { Meta } from './angular2-meta';
|
||||
@@ -30,7 +35,6 @@ export function getResponse() {
|
||||
return Zone.current.get('res') || {};
|
||||
}
|
||||
|
||||
// TODO(gdi2290): refactor into Universal
|
||||
export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
|
||||
@NgModule({
|
||||
@@ -51,6 +55,10 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
CoreModule.forRoot(),
|
||||
SharedModule,
|
||||
AppModule,
|
||||
StoreModule.provideStore(rootReducer),
|
||||
RouterStoreModule.connectRouter(),
|
||||
StoreDevtoolsModule.instrumentOnlyWithExtension(),
|
||||
effects
|
||||
],
|
||||
providers: [
|
||||
{ provide: 'isBrowser', useValue: isBrowser },
|
||||
@@ -61,13 +69,11 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
||||
|
||||
{ provide: 'LRU', useFactory: getLRU, deps: [] },
|
||||
|
||||
DemoCacheService,
|
||||
|
||||
Meta,
|
||||
]
|
||||
})
|
||||
export class MainModule {
|
||||
constructor(public cache: DemoCacheService) {
|
||||
constructor(public store: Store<AppState>) {
|
||||
|
||||
}
|
||||
|
||||
@@ -76,14 +82,17 @@ export class MainModule {
|
||||
* in Universal for now until it's fixed
|
||||
*/
|
||||
universalDoDehydrate = (universalCache) => {
|
||||
universalCache[DemoCacheService.KEY] = JSON.stringify(this.cache.dehydrate());
|
||||
}
|
||||
this.store.take(1).subscribe(state => {
|
||||
universalCache[NGRX_CACHE_KEY] = state;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the cache after it's rendered
|
||||
*/
|
||||
universalAfterDehydrate = () => {
|
||||
// comment out if LRU provided at platform level to be shared between each user
|
||||
this.cache.clear();
|
||||
// this.cache.clear();
|
||||
//TODO is this necessary in dspace's case?
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user