@datorama/akita-ng-effects
Version:
A Reactive State Management extension dealing with side effects.
245 lines (233 loc) • 8.36 kB
JavaScript
import { __rest } from 'tslib';
import { ɵɵdefineInjectable, Injectable, InjectionToken, ɵɵinject, Inject, Optional, SkipSelf, NgModule, Injector } from '@angular/core';
import { Subject } from 'rxjs';
import { logAction } from '@datorama/akita';
import { takeUntil } from 'rxjs/operators';
export { action as createAction, payload, props } from 'ts-action';
export { ofType } from 'ts-action-operators';
class Actions extends Subject {
dispatch(value) {
this.logAction(value);
this.next(value);
}
logAction(value) {
const { type } = value, props = __rest(value, ["type"]);
const hasPayload = Object.getOwnPropertyNames(props).length > 0;
logAction(type, null, hasPayload ? props : null);
}
}
Actions.ɵprov = ɵɵdefineInjectable({ factory: function Actions_Factory() { return new Actions(); }, token: Actions, providedIn: "root" });
Actions.decorators = [
{ type: Injectable, args: [{
providedIn: 'root',
},] }
];
const _ROOT_EFFECTS = new InjectionToken('@datorama/akita Internal Root Effects');
const ROOT_EFFECT_INSTANCES = new InjectionToken('@datorama/akita Root Effects');
const _FEATURE_EFFECTS = new InjectionToken('@datorama/akita Internal Feature Effects');
const FEATURE_EFFECT_INSTANCES = new InjectionToken('@datorama/akita Feature Effects');
class ModuleManager {
constructor(actions$) {
this.actions$ = actions$;
this.effectInstanceSources = new WeakSet();
this.destroyEffects$ = new Subject();
}
subscribeToEffects(effectInstance) {
for (let key in effectInstance) {
const property = effectInstance[key];
if (property.isEffect === true) {
property.pipe(takeUntil(this.destroyEffects$)).subscribe((actionOrSkip) => {
this.dispatchAction(property, actionOrSkip);
});
}
}
}
has(effect) {
return this.effectInstanceSources.has(effect);
}
add(effect) {
this.effectInstanceSources.add(effect);
}
dispatchAction(property, actionOrSkip) {
if (property.dispatchAction && this.checkAction(actionOrSkip)) {
this.actions$.dispatch(actionOrSkip);
}
}
checkAction(action) {
if (action.type) {
return true;
}
throw new TypeError('Make sure to provide a valid action type or set the option {dispatch: false}');
}
ngOnDestroy() {
// modules aren't supposed to be destroyed; might not be needed
this.destroyEffects$.next();
this.effectInstanceSources = new WeakSet();
}
}
ModuleManager.ctorParameters = () => [
{ type: Actions }
];
ModuleManager.ɵprov = ɵɵdefineInjectable({ factory: function ModuleManager_Factory() { return new ModuleManager(ɵɵinject(Actions)); }, token: ModuleManager, providedIn: "root" });
ModuleManager.decorators = [
{ type: Injectable, args: [{
providedIn: 'root',
},] }
];
ModuleManager.ctorParameters = () => [
{ type: Actions }
];
class EffectsRootModule {
constructor(moduleManager, actions, rootEffects, parentModule) {
this.moduleManager = moduleManager;
this.actions = actions;
this.parentModule = parentModule;
this.rootGuard();
rootEffects.forEach((effect) => this.moduleManager.subscribeToEffects(effect));
}
rootGuard() {
if (this.parentModule) {
throw new Error('EffectsRootModule is already loaded. Import it in the AppModule only');
}
}
}
EffectsRootModule.ctorParameters = () => [
{ type: ModuleManager },
{ type: Actions },
{ type: Array, decorators: [{ type: Inject, args: [ROOT_EFFECT_INSTANCES,] }] },
{ type: EffectsRootModule, decorators: [{ type: Optional }, { type: SkipSelf }] }
];
EffectsRootModule.decorators = [
{ type: NgModule }
];
EffectsRootModule.ctorParameters = () => [
{ type: ModuleManager },
{ type: Actions },
{ type: Array, decorators: [{ type: Inject, args: [ROOT_EFFECT_INSTANCES,] }] },
{ type: EffectsRootModule, decorators: [{ type: Optional }, { type: SkipSelf }] }
];
class EffectsFeatureModule {
constructor(moduleManager, featureEffects) {
this.moduleManager = moduleManager;
featureEffects.forEach((group) => group.forEach((effect) => {
this.moduleManager.subscribeToEffects(effect);
}));
}
}
EffectsFeatureModule.ctorParameters = () => [
{ type: ModuleManager },
{ type: Array, decorators: [{ type: Inject, args: [FEATURE_EFFECT_INSTANCES,] }] }
];
EffectsFeatureModule.decorators = [
{ type: NgModule }
];
EffectsFeatureModule.ctorParameters = () => [
{ type: ModuleManager },
{ type: Array, decorators: [{ type: Inject, args: [FEATURE_EFFECT_INSTANCES,] }] }
];
class AkitaNgEffectsModule {
static forRoot(rootEffects = []) {
return {
ngModule: EffectsRootModule,
providers: [
ModuleManager,
Actions,
rootEffects,
{
provide: _ROOT_EFFECTS,
useValue: [rootEffects],
},
{
provide: ROOT_EFFECT_INSTANCES,
useFactory: createEffectInstances,
deps: [Injector, _ROOT_EFFECTS, ModuleManager],
},
],
};
}
static forFeature(featureEffects = []) {
return {
ngModule: EffectsFeatureModule,
providers: [
featureEffects,
{
provide: _FEATURE_EFFECTS,
useValue: featureEffects,
multi: true,
},
{
provide: FEATURE_EFFECT_INSTANCES,
multi: true,
useFactory: createEffectInstances,
deps: [Injector, _FEATURE_EFFECTS, ModuleManager],
},
],
};
}
}
AkitaNgEffectsModule.decorators = [
{ type: NgModule, args: [{},] }
];
function createEffectInstances(injector, effectGroups, moduleManager) {
const mergedEffects = [];
for (const effectGroup of effectGroups) {
mergedEffects.push(...effectGroup);
}
// todo we shouldn't use a map to avoid registering the effects twice;
// fix the underlying issue for feature is called twice
const effectInstances = mergedEffects.reduce((acc, effect) => {
if (!moduleManager.has(effect)) {
moduleManager.add(effect);
acc.push(injector.get(effect));
}
return acc;
}, []);
return effectInstances;
}
function setMetadata(effect, propertyName, effectOptions) {
Object.defineProperty(effect, 'isEffect', {
enumerable: true,
configurable: false,
writable: false,
value: true,
});
Object.defineProperty(effect, 'name', {
enumerable: true,
configurable: false,
writable: false,
value: propertyName,
});
Object.defineProperty(effect, 'dispatchAction', {
enumerable: true,
configurable: false,
writable: false,
value: effectOptions.dispatch,
});
}
function Effect(options) {
options = Object.assign({ dispatch: false }, options);
return function (classProto, propKey) {
let returnValue;
Object.defineProperty(classProto, propKey, {
get: function () {
return returnValue;
},
set: function (value) {
setMetadata(value, propKey, options);
returnValue = value;
},
enumerable: true,
});
};
}
function createEffect(actions$, options) {
const effect = actions$();
options = Object.assign({ dispatch: false }, options);
setMetadata(effect, null, options);
return effect;
}
/**
* Generated bundle index. Do not edit.
*/
export { Actions, AkitaNgEffectsModule, Effect, EffectsRootModule, createEffect, createEffectInstances as ɵa, _ROOT_EFFECTS as ɵb, ROOT_EFFECT_INSTANCES as ɵc, _FEATURE_EFFECTS as ɵd, FEATURE_EFFECT_INSTANCES as ɵe, ModuleManager as ɵf, EffectsFeatureModule as ɵg };
//# sourceMappingURL=datorama-akita-ng-effects.js.map