|
import { NgRedux } from '@angular-redux/store'; |
|
import { Injectable } from '@angular/core'; |
|
import { Subscription } from 'rxjs/Subscription'; |
|
import { IAppState } from '../store/app.models'; |
|
|
|
/** |
|
* The selector callback with the redux store that must return an boolean value. |
|
* |
|
* If the value is true, then it trigger the action |
|
*/ |
|
export type ConditionTriggerFunc<T> = (state: T) => boolean; |
|
|
|
/** |
|
* The selector callback with the redux store that returns the value from type `R`. |
|
* |
|
* If the selector observable is trigger, the action for the redux store is trigger. |
|
*/ |
|
export type SelectorTriggerFunc<T, R> = (state: T) => R; |
|
|
|
/** |
|
* Interface for the service call `#triggerIf(...)`. |
|
*/ |
|
export interface IReduxTriggerCondition { |
|
|
|
/** |
|
* Add the action with the optional payload being triggered if the condition is return true. |
|
* |
|
* @param {string} action the redux action |
|
* @param {*} [payload] the payload for the redux action |
|
* @return {Subscription | null} the subscription fur unsubscribe or the null value. |
|
*/ |
|
withAction(action: string, payload?: any): Subscription | null; |
|
} |
|
|
|
/** |
|
* Interface for the service call `#trigger<R>(...)` |
|
*/ |
|
export interface IReduxTriggerNullAllow { |
|
|
|
/** |
|
* Add the action that is trigger when the observable is execute. |
|
* |
|
* If the parameter `withNullAllow` is true (the default), then it trigger as well, otherwise it trigger only |
|
* if a value is not null is returns |
|
* |
|
* @param {string} action the redux action |
|
* @param {boolean} [withNullAllow] the flag for trigger the action (default is true). |
|
* @return {Subscription | null} the subscription fur unsubscribe or the null value. |
|
*/ |
|
withAction(action: string, withNullAllow?: boolean): Subscription | null; |
|
} |
|
|
|
export interface IReduxTriggerBulk { |
|
|
|
/** |
|
* Add the list of actions if the condition selector is returns true. |
|
* |
|
* The redux actions can not have a payload value. |
|
* |
|
* @param {string} actions the list of action. |
|
* @return {Subscription | null} the subscription fur unsubscribe or the null value. |
|
*/ |
|
withActions(...actions: string[]): Subscription | null; |
|
} |
|
|
|
|
|
@Injectable() |
|
export class ReduxTriggerService { |
|
|
|
private timeout: number = 100; |
|
|
|
constructor(private store: NgRedux<IAppState>) { |
|
} |
|
|
|
/** |
|
* Trigger a redux action if the condition selector is returns a true value. |
|
* |
|
* @param {ConditionTriggerFunc<IAppState>} condition |
|
* @return {IReduxTriggerCondition} |
|
*/ |
|
triggerIf(condition: ConditionTriggerFunc<IAppState>): IReduxTriggerCondition { |
|
return { |
|
withAction: (action: string, payload: any = null) => { |
|
if (!action || action === '') { |
|
throw new Error('triggerIf requires an action'); |
|
} |
|
return this.store.select(condition) |
|
.subscribe((value: boolean) => { |
|
if (value === true) { |
|
setTimeout(() => { |
|
this.store.dispatch({ |
|
type: action, |
|
payload |
|
},); |
|
}, this.timeout); |
|
} |
|
}); |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* Trigger the given action with the result of the selection with the redux store. |
|
* |
|
* @param {SelectorTriggerFunc<IAppState, R>} selector |
|
* @return {IReduxTriggerNullAllow} |
|
*/ |
|
trigger<R>(selector: SelectorTriggerFunc<IAppState, R>): IReduxTriggerNullAllow { |
|
return { |
|
withAction: (action: string, withNullAllow?: boolean): Subscription => { |
|
if (!action || action === '') { |
|
throw new Error('trigger requires an action'); |
|
} |
|
// adjust the parameter "withNullAllow |
|
withNullAllow = typeof withNullAllow === 'boolean' ? withNullAllow : false; |
|
|
|
return this.store.select(selector) |
|
.subscribe((value: R) => { |
|
const running = withNullAllow === true || !!value; |
|
if (running) { |
|
setTimeout(() => { |
|
this.store.dispatch({ |
|
type: action, |
|
payload: value |
|
}); |
|
}, this.timeout); |
|
} |
|
}); |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* Trigger the condition selector callback if there returns true. |
|
* |
|
* **Note**: The return value of the method `withAction` is always `null`. |
|
* |
|
* @param {ConditionTriggerFunc<IAppState>} condition |
|
* @return {IReduxTriggerCondition} |
|
*/ |
|
triggerOnce(condition: ConditionTriggerFunc<IAppState>): IReduxTriggerCondition { |
|
return { |
|
withAction: (action: string, payload: any = null) => { |
|
if (!action || action === '') { |
|
throw new Error('triggerOny requires an action'); |
|
} |
|
let isRunning: boolean = false; |
|
const subscribe = this.store.select(condition) |
|
.subscribe((value: boolean) => { |
|
if (isRunning) { |
|
return; |
|
} |
|
if (value === true) { |
|
isRunning = true; |
|
// trigger is running |
|
setTimeout(() => { |
|
this.store.dispatch({ |
|
type: action, |
|
payload |
|
}); |
|
}, this.timeout); |
|
// unsubscribe |
|
setTimeout(() => { |
|
if (subscribe && !subscribe.closed) { |
|
subscribe.unsubscribe(); |
|
} |
|
}, this.timeout); |
|
} |
|
}); |
|
return null; |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* Trigger an bulk of redux actions once if the condition selector subscribes the value is true. |
|
* |
|
* **Note**: The return value of the method `withAction` is always `null`. |
|
* |
|
* @param {ConditionTriggerFunc<IAppState>} condition |
|
* @param {boolean} [asyncCalling] the flag manages calling of the redux store dispatch (Default is `true`) |
|
* @return {IReduxTriggerBulk} |
|
*/ |
|
triggerBulk(condition: ConditionTriggerFunc<IAppState>, asyncCalling: boolean = true): IReduxTriggerBulk { |
|
return { |
|
withActions: (...actions: string[]) => { |
|
if (!actions || actions.length === 0) { |
|
throw new Error('triggerBulk requires a list of action.'); |
|
} |
|
let isRunning: boolean = false; |
|
const subscribe = this.store.select(condition) |
|
.subscribe((value: boolean) => { |
|
if (isRunning) { |
|
return; |
|
} |
|
|
|
if (value === true) { |
|
// trigger running |
|
isRunning = true; |
|
|
|
if (asyncCalling) { |
|
actions.forEach((action: string) => { |
|
setTimeout(() => { |
|
this.store.dispatch({type: action}); |
|
}, this.timeout); |
|
}); |
|
} else { |
|
// sync calling |
|
actions.forEach((action: string) => { |
|
this.store.dispatch({type: action}); |
|
}); |
|
} |
|
// unsubscribe |
|
setTimeout(() => { |
|
if (subscribe && !subscribe.closed) { |
|
subscribe.unsubscribe(); |
|
} |
|
}, this.timeout); |
|
} |
|
}); |
|
return null; |
|
} |
|
}; |
|
} |
|
} |