projects/storefrontlib/cms-structure/services/cms-features.service.ts
Service responsible for resolving cms config based feature modules.
Properties |
|
Methods |
|
constructor(configInitializer: ConfigInitializerService, featureModules: FeatureModulesService)
|
|||||||||
|
Parameters :
|
| Private createFeatureInstance | |||||||||
createFeatureInstance(moduleRef: NgModuleRef<any>, feature: string)
|
|||||||||
|
Create feature instance from feature's moduleRef
Parameters :
Returns :
FeatureInstance
|
| getCmsMapping | ||||||
getCmsMapping(componentType: string)
|
||||||
|
Return full CmsComponent mapping defined in feature module
Parameters :
Returns :
Observable<CmsComponentMapping | undefined>
|
| getModule | ||||||
getModule(componentType: string)
|
||||||
|
Resolves feature module for provided component type
Parameters :
Returns :
NgModuleRef | undefined
|
| hasFeatureFor | ||||||
hasFeatureFor(componentType: string)
|
||||||
|
Check if there is feature module configuration that covers specified component type
Parameters :
Returns :
boolean
|
| Private initFeatureMap |
initFeatureMap()
|
|
Returns :
void
|
| Private resolveFeatureConfiguration | ||||||
resolveFeatureConfiguration(featureInjector: Injector)
|
||||||
|
Returns configuration provided in feature module
Parameters :
Returns :
CmsConfig
|
| Private resolveFeatureInstance | ||||||
resolveFeatureInstance(featureName: string)
|
||||||
|
Resolve feature based on feature name, if feature was not yet resolved It will first resolve all module dependencies if defined
Parameters :
Returns :
Observable<FeatureInstance>
|
| Private componentFeatureMap |
Type : Map<string | string>
|
Default value : new Map()
|
| Private featureInstances |
Type : Map<string | Observable<FeatureInstance>>
|
Default value : new Map()
|
| Private Optional featureModulesConfig |
Type : literal type
|
import { Injectable, InjectFlags, Injector, NgModuleRef } from '@angular/core';
import {
CMSComponentConfig,
CmsComponentMapping,
CmsConfig,
ConfigChunk,
ConfigInitializerService,
deepMerge,
DefaultConfigChunk,
FeatureModuleConfig,
FeatureModulesService,
} from '@spartacus/core';
import { defer, Observable, of } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
interface FeatureInstance extends FeatureModuleConfig {
moduleRef?: NgModuleRef<any>;
componentsMappings?: CMSComponentConfig;
}
/**
* Service responsible for resolving cms config based feature modules.
*/
@Injectable({
providedIn: 'root',
})
export class CmsFeaturesService {
// feature modules configuration
private featureModulesConfig?: {
[featureName: string]: FeatureModuleConfig | string;
};
// maps componentType to feature
private componentFeatureMap: Map<string, string> = new Map();
/*
* Contains either FeatureInstance or FeatureInstance resolver for not yet
* resolved feature modules
*/
private featureInstances: Map<string, Observable<FeatureInstance>> =
new Map();
constructor(
protected configInitializer: ConfigInitializerService,
protected featureModules: FeatureModulesService
) {
this.initFeatureMap();
}
private initFeatureMap(): void {
this.configInitializer
.getStable('featureModules')
.subscribe((config: CmsConfig) => {
this.featureModulesConfig = config.featureModules ?? {};
for (const [featureName, featureConfig] of Object.entries(
this.featureModulesConfig
)) {
if (
typeof featureConfig !== 'string' &&
featureConfig?.module &&
featureConfig?.cmsComponents?.length
) {
for (const component of featureConfig.cmsComponents) {
this.componentFeatureMap.set(component, featureName);
}
}
}
});
}
/**
* Check if there is feature module configuration that covers specified
* component type
*/
hasFeatureFor(componentType: string): boolean {
return this.componentFeatureMap.has(componentType);
}
/**
* Return full CmsComponent mapping defined in feature module
*/
getCmsMapping(
componentType: string
): Observable<CmsComponentMapping | undefined> {
const feature = this.componentFeatureMap.get(componentType);
if (!feature) {
return of(undefined);
}
return this.resolveFeatureInstance(feature).pipe(
map(
(featureInstance) => featureInstance.componentsMappings?.[componentType]
)
);
}
/**
* Resolves feature module for provided component type
*
* @param componentType
*/
getModule(componentType: string): NgModuleRef<any> | undefined {
const feature = this.componentFeatureMap.get(componentType);
if (!feature) {
return undefined;
}
let module;
// we are returning injectors only for already resolved features
this.featureInstances
.get(feature)
?.subscribe((featureInstance) => {
module = featureInstance.moduleRef;
})
.unsubscribe();
return module;
}
/**
* Resolve feature based on feature name, if feature was not yet resolved
*
* It will first resolve all module dependencies if defined
*/
private resolveFeatureInstance(
featureName: string
): Observable<FeatureInstance> {
return defer(() => {
if (!this.featureInstances.has(featureName)) {
this.featureInstances.set(
featureName,
this.featureModules.resolveFeature(featureName).pipe(
map((moduleRef) =>
this.createFeatureInstance(moduleRef, featureName)
),
shareReplay()
)
);
}
return this.featureInstances.get(featureName);
});
}
/**
* Create feature instance from feature's moduleRef
*/
private createFeatureInstance(
moduleRef: NgModuleRef<any>,
feature: string
): FeatureInstance {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const featureConfig = this.featureModulesConfig![
feature
] as FeatureModuleConfig;
const featureInstance: FeatureInstance = {
moduleRef,
componentsMappings: {},
};
// resolve configuration for feature module
const resolvedConfiguration = this.resolveFeatureConfiguration(
moduleRef.injector
);
// extract cms components configuration from feature config
for (const componentType of featureConfig.cmsComponents ?? []) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
featureInstance.componentsMappings![componentType] =
resolvedConfiguration.cmsComponents?.[componentType] ?? {};
}
return featureInstance;
}
/**
* Returns configuration provided in feature module
*/
private resolveFeatureConfiguration(featureInjector: Injector): CmsConfig {
// get config chunks from feature lib
const featureConfigChunks = featureInjector.get<any[]>(
ConfigChunk,
[],
InjectFlags.Self
);
// get default config chunks from feature lib
const featureDefaultConfigChunks = featureInjector.get<any[]>(
DefaultConfigChunk,
[],
InjectFlags.Self
);
return deepMerge(
{},
...(featureDefaultConfigChunks ?? []),
...(featureConfigChunks ?? [])
) as CmsConfig;
}
}