Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions modules/data/src/entity-data-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { InjectionToken } from '@angular/core';
import { MetaReducer } from '@ngrx/store';
import { EntityCache } from './reducers/entity-cache';
import { EntityAction } from './actions/entity-action';
import { EntityMetadataMap } from './entity-metadata/entity-metadata';
import { EntityCollection } from './reducers/entity-collection';

export interface EntityDataModuleConfig {
entityMetadata?: EntityMetadataMap;
entityCacheMetaReducers?: (
| MetaReducer<EntityCache>
| InjectionToken<MetaReducer<EntityCache>>
)[];
entityCollectionMetaReducers?: MetaReducer<EntityCollection, EntityAction>[];
// Initial EntityCache state or a function that returns that state
initialEntityCacheState?: EntityCache | (() => EntityCache);
pluralNames?: { [name: string]: string };
}
164 changes: 11 additions & 153 deletions modules/data/src/entity-data-without-effects.module.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,10 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { EntityDataModuleConfig } from './entity-data-config';
import {
ModuleWithProviders,
NgModule,
Inject,
Injector,
InjectionToken,
Optional,
OnDestroy,
} from '@angular/core';

import {
Action,
combineReducers,
MetaReducer,
ReducerManager,
StoreModule,
} from '@ngrx/store';

import { CorrelationIdGenerator } from './utils/correlation-id-generator';
import { EntityDispatcherDefaultOptions } from './dispatchers/entity-dispatcher-default-options';
import { EntityAction } from './actions/entity-action';
import { EntityActionFactory } from './actions/entity-action-factory';
import { EntityCache } from './reducers/entity-cache';
import { EntityCacheDispatcher } from './dispatchers/entity-cache-dispatcher';
import { entityCacheSelectorProvider } from './selectors/entity-cache-selector';
import { EntityCollectionServiceElementsFactory } from './entity-services/entity-collection-service-elements-factory';
import { EntityCollectionServiceFactory } from './entity-services/entity-collection-service-factory';
import { EntityServices } from './entity-services/entity-services';
import { EntityCollection } from './reducers/entity-collection';
import { EntityCollectionCreator } from './reducers/entity-collection-creator';
import { EntityCollectionReducerFactory } from './reducers/entity-collection-reducer';
import { EntityCollectionReducerMethodsFactory } from './reducers/entity-collection-reducer-methods';
import { EntityCollectionReducerRegistry } from './reducers/entity-collection-reducer-registry';
import { EntityDispatcherFactory } from './dispatchers/entity-dispatcher-factory';
import { EntityDefinitionService } from './entity-metadata/entity-definition.service';
import { EntityMetadataMap } from './entity-metadata/entity-metadata';
import { EntityCacheReducerFactory } from './reducers/entity-cache-reducer';
import {
ENTITY_CACHE_NAME,
ENTITY_CACHE_NAME_TOKEN,
ENTITY_CACHE_META_REDUCERS,
ENTITY_COLLECTION_META_REDUCERS,
INITIAL_ENTITY_CACHE_STATE,
} from './reducers/constants';

import { DefaultLogger } from './utils/default-logger';
import { EntitySelectorsFactory } from './selectors/entity-selectors';
import { EntitySelectors$Factory } from './selectors/entity-selectors$';
import { EntityServicesBase } from './entity-services/entity-services-base';
import { EntityServicesElements } from './entity-services/entity-services-elements';
import { Logger, PLURAL_NAMES_TOKEN } from './utils/interfaces';

export interface EntityDataModuleConfig {
entityMetadata?: EntityMetadataMap;
entityCacheMetaReducers?: (
| MetaReducer<EntityCache, Action>
| InjectionToken<MetaReducer<EntityCache, Action>>
)[];
entityCollectionMetaReducers?: MetaReducer<EntityCollection, EntityAction>[];
// Initial EntityCache state or a function that returns that state
initialEntityCacheState?: EntityCache | (() => EntityCache);
pluralNames?: { [name: string]: string };
}
provideRootEntityDataWithoutEffects,
ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS,
initializeEntityDataWithoutEffects,
} from './provide-entity-data';

/**
* Module without effects or dataservices which means no HTTP calls
Expand All @@ -69,105 +13,19 @@ export interface EntityDataModuleConfig {
* therefore opt-out of @ngrx/effects for entities
*/
@NgModule({
imports: [
StoreModule, // rely on Store feature providers rather than Store.forFeature()
],
providers: [
CorrelationIdGenerator,
EntityDispatcherDefaultOptions,
EntityActionFactory,
EntityCacheDispatcher,
EntityCacheReducerFactory,
entityCacheSelectorProvider,
EntityCollectionCreator,
EntityCollectionReducerFactory,
EntityCollectionReducerMethodsFactory,
EntityCollectionReducerRegistry,
EntityCollectionServiceElementsFactory,
EntityCollectionServiceFactory,
EntityDefinitionService,
EntityDispatcherFactory,
EntitySelectorsFactory,
EntitySelectors$Factory,
EntityServicesElements,
{ provide: ENTITY_CACHE_NAME_TOKEN, useValue: ENTITY_CACHE_NAME },
{ provide: EntityServices, useClass: EntityServicesBase },
{ provide: Logger, useClass: DefaultLogger },
],
providers: [ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS],
})
export class EntityDataModuleWithoutEffects implements OnDestroy {
private entityCacheFeature: any;

export class EntityDataModuleWithoutEffects {
static forRoot(
config: EntityDataModuleConfig
): ModuleWithProviders<EntityDataModuleWithoutEffects> {
return {
ngModule: EntityDataModuleWithoutEffects,
providers: [
{
provide: ENTITY_CACHE_META_REDUCERS,
useValue: config.entityCacheMetaReducers
? config.entityCacheMetaReducers
: [],
},
{
provide: ENTITY_COLLECTION_META_REDUCERS,
useValue: config.entityCollectionMetaReducers
? config.entityCollectionMetaReducers
: [],
},
{
provide: PLURAL_NAMES_TOKEN,
multi: true,
useValue: config.pluralNames ? config.pluralNames : {},
},
],
};
}

constructor(
private reducerManager: ReducerManager,
entityCacheReducerFactory: EntityCacheReducerFactory,
private injector: Injector,
// optional params
@Optional()
@Inject(ENTITY_CACHE_NAME_TOKEN)
private entityCacheName: string,
@Optional()
@Inject(INITIAL_ENTITY_CACHE_STATE)
private initialState: any,
@Optional()
@Inject(ENTITY_CACHE_META_REDUCERS)
private metaReducers: (
| MetaReducer<EntityCache, Action>
| InjectionToken<MetaReducer<EntityCache, Action>>
)[]
) {
// Add the @ngrx/data feature to the Store's features
// as Store.forFeature does for StoreFeatureModule
const key = entityCacheName || ENTITY_CACHE_NAME;

initialState =
typeof initialState === 'function' ? initialState() : initialState;

const reducers: MetaReducer<EntityCache, Action>[] = (
metaReducers || []
).map((mr) => {
return mr instanceof InjectionToken ? injector.get(mr) : mr;
});

this.entityCacheFeature = {
key,
reducers: entityCacheReducerFactory.create(),
reducerFactory: combineReducers,
initialState: initialState || {},
metaReducers: reducers,
providers: [provideRootEntityDataWithoutEffects(config)],
};
reducerManager.addFeature(this.entityCacheFeature);
}

// eslint-disable-next-line @angular-eslint/contextual-lifecycle
ngOnDestroy() {
this.reducerManager.removeFeature(this.entityCacheFeature);
constructor() {
initializeEntityDataWithoutEffects();
}
}
105 changes: 12 additions & 93 deletions modules/data/src/entity-data.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,25 @@ import { ModuleWithProviders, NgModule } from '@angular/core';

import { EffectsModule, EffectSources } from '@ngrx/effects';

import { DefaultDataServiceFactory } from './dataservices/default-data.service';

import {
DefaultPersistenceResultHandler,
PersistenceResultHandler,
} from './dataservices/persistence-result-handler.service';

import {
DefaultHttpUrlGenerator,
HttpUrlGenerator,
} from './dataservices/http-url-generator';

import { EntityCacheDataService } from './dataservices/entity-cache-data.service';
import { EntityCacheEffects } from './effects/entity-cache-effects';
import { EntityDataService } from './dataservices/entity-data.service';
import { EntityEffects } from './effects/entity-effects';

import { ENTITY_METADATA_TOKEN } from './entity-metadata/entity-metadata';

import {
ENTITY_CACHE_META_REDUCERS,
ENTITY_COLLECTION_META_REDUCERS,
} from './reducers/constants';
import { Pluralizer, PLURAL_NAMES_TOKEN } from './utils/interfaces';
import { DefaultPluralizer } from './utils/default-pluralizer';

import { EntityDataModuleConfig } from './entity-data-config';
import {
EntityDataModuleConfig,
EntityDataModuleWithoutEffects,
} from './entity-data-without-effects.module';
ENTITY_DATA_PROVIDERS,
ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS,
initializeEntityData,
provideRootEntityData,
provideRootEntityDataWithoutEffects,
} from './provide-entity-data';

/**
* entity-data main module includes effects and HTTP data services
* Configure with `forRoot`.
* No `forFeature` yet.
*/
@NgModule({
imports: [
EntityDataModuleWithoutEffects,
EffectsModule, // do not supply effects because can't replace later
],
providers: [
DefaultDataServiceFactory,
EntityCacheDataService,
EntityDataService,
EntityCacheEffects,
EntityEffects,
{ provide: HttpUrlGenerator, useClass: DefaultHttpUrlGenerator },
{
provide: PersistenceResultHandler,
useClass: DefaultPersistenceResultHandler,
},
{ provide: Pluralizer, useClass: DefaultPluralizer },
],
providers: [ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS, ENTITY_DATA_PROVIDERS],
})
export class EntityDataModule {
static forRoot(
Expand All @@ -64,59 +29,13 @@ export class EntityDataModule {
return {
ngModule: EntityDataModule,
providers: [
// TODO: Moved these effects classes up to EntityDataModule itself
// Remove this comment if that was a mistake.
// EntityCacheEffects,
// EntityEffects,
{
provide: ENTITY_METADATA_TOKEN,
multi: true,
useValue: config.entityMetadata ? config.entityMetadata : [],
},
{
provide: ENTITY_CACHE_META_REDUCERS,
useValue: config.entityCacheMetaReducers
? config.entityCacheMetaReducers
: [],
},
{
provide: ENTITY_COLLECTION_META_REDUCERS,
useValue: config.entityCollectionMetaReducers
? config.entityCollectionMetaReducers
: [],
},
{
provide: PLURAL_NAMES_TOKEN,
multi: true,
useValue: config.pluralNames ? config.pluralNames : {},
},
provideRootEntityDataWithoutEffects(config),
provideRootEntityData(config),
],
};
}

constructor(
private effectSources: EffectSources,
entityCacheEffects: EntityCacheEffects,
entityEffects: EntityEffects
) {
// We can't use `forFeature()` because, if we did, the developer could not
// replace the entity-data `EntityEffects` with a custom alternative.
// Replacing that class is an extensibility point we need.
//
// The FEATURE_EFFECTS token is not exposed, so can't use that technique.
// Warning: this alternative approach relies on an undocumented API
// to add effect directly rather than through `forFeature()`.
// The danger is that EffectsModule.forFeature evolves and we no longer perform a crucial step.
this.addEffects(entityCacheEffects);
this.addEffects(entityEffects);
}

/**
* Add another class instance that contains effects.
* @param effectSourceInstance a class instance that implements effects.
* Warning: undocumented @ngrx/effects API
*/
addEffects(effectSourceInstance: any) {
this.effectSources.addEffects(effectSourceInstance);
constructor() {
initializeEntityData();
}
}
14 changes: 10 additions & 4 deletions modules/data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,15 @@ export {
toUpdateFactory,
} from './utils/utilities';

// // EntityDataConfig
export { EntityDataModuleConfig } from './entity-data-config';

// // EntityDataModule
export {
EntityDataModuleConfig,
EntityDataModuleWithoutEffects,
} from './entity-data-without-effects.module';
export { EntityDataModuleWithoutEffects } from './entity-data-without-effects.module';
export { EntityDataModule } from './entity-data.module';

// // Standalone APIs
export {
provideEntityData,
provideEntityDataWithoutEffects,
} from './provide-entity-data';
Loading