Skip to content

Commit

Permalink
feat(data): add withEffects feature for provideEntityData (#3656)
Browse files Browse the repository at this point in the history
  • Loading branch information
markostanimirovic authored Nov 9, 2022
1 parent aa7ed66 commit a6959e8
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 88 deletions.
13 changes: 4 additions & 9 deletions modules/data/src/entity-data-without-effects.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { EntityDataModuleConfig } from './entity-data-config';
import {
provideRootEntityDataWithoutEffects,
ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS,
initializeEntityDataWithoutEffects,
BASE_ENTITY_DATA_PROVIDERS,
provideEntityDataConfig,
} from './provide-entity-data';

/**
Expand All @@ -13,19 +12,15 @@ import {
* therefore opt-out of @ngrx/effects for entities
*/
@NgModule({
providers: [ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS],
providers: [BASE_ENTITY_DATA_PROVIDERS],
})
export class EntityDataModuleWithoutEffects {
static forRoot(
config: EntityDataModuleConfig
): ModuleWithProviders<EntityDataModuleWithoutEffects> {
return {
ngModule: EntityDataModuleWithoutEffects,
providers: [provideRootEntityDataWithoutEffects(config)],
providers: [provideEntityDataConfig(config)],
};
}

constructor() {
initializeEntityDataWithoutEffects();
}
}
18 changes: 4 additions & 14 deletions modules/data/src/entity-data.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { ModuleWithProviders, NgModule } from '@angular/core';

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

/**
Expand All @@ -16,22 +13,15 @@ import {
*/
@NgModule({
imports: [EntityDataModuleWithoutEffects],
providers: [ENTITY_DATA_PROVIDERS],
providers: [ENTITY_DATA_EFFECTS_PROVIDERS],
})
export class EntityDataModule {
static forRoot(
config: EntityDataModuleConfig
): ModuleWithProviders<EntityDataModule> {
return {
ngModule: EntityDataModule,
providers: [
provideRootEntityDataWithoutEffects(config),
provideRootEntityData(config),
],
providers: [provideEntityDataConfig(config)],
};
}

constructor() {
initializeEntityData();
}
}
5 changes: 1 addition & 4 deletions modules/data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,4 @@ export { EntityDataModuleWithoutEffects } from './entity-data-without-effects.mo
export { EntityDataModule } from './entity-data.module';

// // Standalone APIs
export {
provideEntityData,
provideEntityDataWithoutEffects,
} from './provide-entity-data';
export { provideEntityData, withEffects } from './provide-entity-data';
178 changes: 117 additions & 61 deletions modules/data/src/provide-entity-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import { EntityEffects } from './effects/entity-effects';
import { DefaultPluralizer } from './utils/default-pluralizer';
import { EntityDataModuleConfig } from './entity-data-config';

export const ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS: Provider[] = [
export const BASE_ENTITY_DATA_PROVIDERS: Provider[] = [
CorrelationIdGenerator,
EntityDispatcherDefaultOptions,
EntityActionFactory,
Expand All @@ -80,15 +80,14 @@ export const ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS: Provider[] = [
{ provide: ENTITY_CACHE_NAME_TOKEN, useValue: ENTITY_CACHE_NAME },
{ provide: EntityServices, useClass: EntityServicesBase },
{ provide: Logger, useClass: DefaultLogger },
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => initializeBaseEntityData(),
},
];

const ENTITY_DATA_WITHOUT_EFFECTS_ENV_PROVIDER: Provider = {
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => initializeEntityDataWithoutEffects(),
};

export function initializeEntityDataWithoutEffects(): void {
function initializeBaseEntityData(): void {
const reducerManager = inject(ReducerManager);
const entityCacheReducerFactory = inject(EntityCacheReducerFactory);
const entityCacheName = inject(ENTITY_CACHE_NAME_TOKEN, {
Expand Down Expand Up @@ -116,48 +115,14 @@ export function initializeEntityDataWithoutEffects(): void {
const entityCacheFeature = {
key,
reducers: entityCacheReducerFactory.create(),
reducerFactory: combineReducers as ActionReducerFactory<unknown>,
reducerFactory: combineReducers as ActionReducerFactory<EntityCache>,
initialState: initialState || {},
metaReducers: metaReducers,
};
reducerManager.addFeature(entityCacheFeature);
}

export function provideRootEntityDataWithoutEffects(
config: EntityDataModuleConfig
): Provider[] {
return [
{
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 : {},
},
];
}

export function provideEntityDataWithoutEffects(
config: EntityDataModuleConfig
): EnvironmentProviders {
return makeEnvironmentProviders([
ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS,
provideRootEntityDataWithoutEffects(config),
ENTITY_DATA_WITHOUT_EFFECTS_ENV_PROVIDER,
]);
}

export const ENTITY_DATA_PROVIDERS: Provider[] = [
export const ENTITY_DATA_EFFECTS_PROVIDERS: Provider[] = [
DefaultDataServiceFactory,
EntityCacheDataService,
EntityDataService,
Expand All @@ -169,15 +134,14 @@ export const ENTITY_DATA_PROVIDERS: Provider[] = [
useClass: DefaultPersistenceResultHandler,
},
{ provide: Pluralizer, useClass: DefaultPluralizer },
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => initializeEntityDataEffects(),
},
];

const ENTITY_DATA_ENV_PROVIDER: Provider = {
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => initializeEntityData(),
};

export function initializeEntityData(): void {
function initializeEntityDataEffects(): void {
const effectsSources = inject(EffectSources);
const entityCacheEffects = inject(EntityCacheEffects);
const entityEffects = inject(EntityEffects);
Expand All @@ -186,10 +150,27 @@ export function initializeEntityData(): void {
effectsSources.addEffects(entityEffects);
}

export function provideRootEntityData(
export function provideEntityDataConfig(
config: EntityDataModuleConfig
): Provider[] {
return [
{
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 : {},
},
{
provide: ENTITY_METADATA_TOKEN,
multi: true,
Expand All @@ -198,17 +179,92 @@ export function provideRootEntityData(
];
}

/**
* Sets up base entity data providers with entity config.
* This function should to be used at the root level.
*
* @usageNotes
*
* ### Providing entity data with effects
*
* When used with `withEffects` feature, the `provideEntityData` function is
* an alternative to `EntityDataModule.forRoot`
*
* ```ts
* import { provideStore } from '@ngrx/store';
* import { provideEffects } from '@ngrx/effects';
* import {
* EntityMetadataMap,
* provideEntityData,
* withEffects,
* } from '@ngrx/data';
*
* const entityMetadata: EntityMetadataMap = {
* Hero: {},
* Villain: {},
* };
* const pluralNames = { Hero: 'Heroes' };
*
* bootstrapApplication(AppComponent, {
* providers: [
* provideStore(),
* provideEffects(),
* provideEntityData({ entityMetadata, pluralNames }, withEffects()),
* ],
* });
* ```
*
* ### Providing entity data without effects
*
* When used without `withEffects` feature, the `provideEntityData` function is
* an alternative to `EntityDataModuleWithoutEffects.forRoot`.
*
* ```ts
* import { provideStore } from '@ngrx/store';
* import { EntityMetadataMap, provideEntityData } from '@ngrx/data';
*
* const entityMetadata: EntityMetadataMap = {
* Musician: {},
* Song: {},
* };
*
* bootstrapApplication(AppComponent, {
* providers: [
* provideStore(),
* provideEntityData({ entityMetadata }),
* ],
* });
* ```
*
*/
export function provideEntityData(
config: EntityDataModuleConfig
config: EntityDataModuleConfig,
...features: EntityDataFeature[]
): EnvironmentProviders {
return makeEnvironmentProviders([
// add EntityDataWithoutEffects providers
ENTITY_DATA_WITHOUT_EFFECTS_PROVIDERS,
provideRootEntityDataWithoutEffects(config),
ENTITY_DATA_WITHOUT_EFFECTS_ENV_PROVIDER,
// add EntityData providers
ENTITY_DATA_PROVIDERS,
provideRootEntityData(config),
ENTITY_DATA_ENV_PROVIDER,
BASE_ENTITY_DATA_PROVIDERS,
provideEntityDataConfig(config),
...features.map((feature) => feature.ɵproviders),
]);
}

enum EntityDataFeatureKind {
WithEffects,
}

interface EntityDataFeature {
ɵkind: EntityDataFeatureKind;
ɵproviders: Provider[];
}

/**
* Registers entity data effects and provides HTTP data services.
*
* @see `provideEntityData`
*/
export function withEffects(): EntityDataFeature {
return {
ɵkind: EntityDataFeatureKind.WithEffects,
ɵproviders: [ENTITY_DATA_EFFECTS_PROVIDERS],
};
}

0 comments on commit a6959e8

Please sign in to comment.