nope-js-node
Version:
NoPE Runtime for Nodejs. For Browser-Support please use nope-browser
336 lines (324 loc) • 9.14 kB
text/typescript
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:31:24
* @modify date 2021-11-13 14:07:55
* @desc [description]
*/
import {
BehaviorSubject,
CompletionObserver,
ErrorObserver,
NextObserver,
Observable,
} from "rxjs";
import { INopeEventEmitter } from ".";
import {
IEventAdditionalData,
IEventCallback,
INopeWaitForEventOptions,
} from "./nopeEventEmitter.interface";
export interface INopeWaitForObservableChangeOptions
extends INopeWaitForEventOptions {
/**
* Directly test the current value.
* Otherwise waits for an updated
* value.
*/
testCurrent?: boolean;
}
export interface INopePartialObserver<
T,
AD extends IEventAdditionalData = IEventAdditionalData
> {
next?: IEventCallback<T, AD>;
error?: (error: any) => void;
complete?: () => void;
}
export declare type IPartialObserver<T> =
| NextObserver<T>
| ErrorObserver<T>
| CompletionObserver<T>;
/**
* Helper to interact with the underlying RXJS implementation.
*/
export type IPipe<T, K> = (
scope: { [index: string]: any },
observable: Observable<T>
) => Observable<K>;
/**
* The IObservable data contains additional value.
* This is the data, which will be utilized by the
* underlying RXJS implementation.
*
* @export
* @typedef {IObservableType}
* @template T The datatype of the {@link INopeObservable}
* @template AD The additional data of which is utilized by the RXJS Implementation.
*/
export type IObservableType<
T,
AD extends IEventAdditionalData = IEventAdditionalData
> = Partial<AD> & {
value: T;
};
/**
* The Observable extends the {@link INopeEventEmitter}.
*
* Instead of the {@link INopeEventEmitter} the `Observable` contains persistend data. This results in
* the following behavior:
*
* > Subscribing an `Observable` immediatly will call the subscriber, be cause `data` is present.
*
* > `data` can be accessed with `getContent`
*
* Contains additional Functionalities like:
* - property with the current value
* - function to publish values. (wrapper for next)
* - enables performing a subscription with synced call or a immediate call.
*
* # Example:
*
*
*
*
* ## Usage of `nope.observables`
*
* To work with `observables` we have to import these:
*
* ```typescript
* import * as nope from "nope-js-node";
* ```
*
* Afterward we are able to create our first Observable.
*
* ```typescript
* // Create our Observable:
* const obs = new nope.observables.NopeObservable<number>()
* ```
*
* ### `setContent`: Change the content of the Observable.
*
* To change the content of an observable use the method `setContent`.
*
*
* ```typescript
* // Set the content to "1337"
* obs.setContent(5);
*
* // Print the content (see getContent)
* console.log("current value =",obs.getContent());
* ```
* This will print:
*
* current value = 5
*
*
* #### `setter`: Define a specific setter for the observable.
*
* You can specify a specifc getter for the observable for instance, to limit the number to the following constrains `> 0` and `< 10`.
*
* ---
*
* The setter function will receive multiple parameters, as listed below:
*
* 1. `value`,
* 2. ``options`` containing:
* * `sender`: The Element, which changed the data
* * `timestamp`: The timestamp of the change
* * `args`: additional args.
*
* ---
*
* The setter function have to return a `dict` with the following keys:
* | key | type | content |
* | - | - | - |
* | `valid` | `bool` | A Flag, to show whether the data are valid or not. If the data is invalid, the observable wont store them |
* | `value` | `any` | The Data that has been adapted |
*
* ---
*
* Below, we will implement an example to show the setter above.
*
*
* ```typescript
* obs.setter = (value, options) => {
* // Print, which data we received
* console.log("setter received", value, options);
* // Show the result of our comparison
* console.log("data is valid:", (value > 0) && (value < 10));
* return {
* // The Value
* value: value,
* // Valid
* valid: (value > 0) && (value < 10)
* }
* }
*
* // Set the content to "1337" ==> But our setter will prevent using this value because it isnt valid.
* obs.setContent(1337);
*
* // Print the content (see getContent) ==> we expect to get "5"
* console.log("current value =", obs.getContent());
* ```
*
* This will print:
*
* setter received 1337 {}
* data is valid: false
* current value = 5
*
*
* Now we can reset the setter by assigning `null`. This disables the setter again:
*
* ```typescript
* obs.setter = null
*
* // Set the content to "1337" we do not have any setter ==> we will use this parameter
* obs.setContent(1337);
*
* // Print the content (see getContent) ==> we expect to get "1337"
* console.log("current value =", obs.getContent());
* ```
*
* This will print:
*
* current value = 1337
*
*
* To remove such a getter just set the getter property to `null`.
*
* ### `getContent`: Get the current content of the Observable.
* To extract the content of our observable, we are able to use the function `getContent`
*
*
* ```typescript
* let content = obs.getContent();
* console.log("current value =", content)
* ```
*
* This will print:
*
* current value = 1337
*
*
* If no data is assigned, this will result in `undefined`. Otherwise the current data is returned.
*
* #### `getter`: Define a specific getter for the observable.
*
* You can specify a specifc getter for the observable for instance, to allways return a `string`
*
*
* ```typescript
* // Define a getter
* obs.getter = (value) => "Allways this result";
* console.log("current value (with getter) =", obs.getContent());
* ```
*
* This will print:
*
* current value (with getter) = Allways this result
*
*
* To remove such a getter just set the getter property to `null`.
*
* The Original value is not changed ==> we expect to get "1337"
*
*
* ```typescript
* // Reset the getter.
* obs.getter = null;
* console.log("current value (after removing the getter) =", obs.getContent());
* ```
*
* This will print:
*
* current value (after removing the getter) = 1337
*
*
* # Subscriptions
*
* You can use an observable to get informed about changes:
*
* 1. define a `callback`, which receives both, the value and the options.
* 2. `subscribe` the observable.
*
* During subscribing you can use the following options for subscribing:
*
* | key | type | content |
* | - | - | - |
* | `skipCurrent` | `bool` | A Flag, which will call the callback if set to true the first time after a change. Defaults to False |
*
* Lets determine the behavior using an example code:
*
* ```typescript
* function onChange(value, options){
* console.log("options.skipCurrent = False. Value is now:", value)
* }
*
* function onChangeSkip(value, options){
* console.log("options.skipCurrent = True. Value is now:", value);
* }
*
* const observer_01 = obs.subscribe(onChange)
* const observer_02 = obs.subscribe(onChangeSkip, {"skipCurrent": true})
*
* obs.setContent("new-value")
* obs.setContent("new-value-2")
* ```
*
* This code results in:
*
* options.skipCurrent = False. Value is now: 1337
* options.skipCurrent = False. Value is now: new-value
* options.skipCurrent = True. Value is now: new-value
* options.skipCurrent = False. Value is now: new-value-2
* options.skipCurrent = True. Value is now: new-value-2
*
*
* @interface INopeObservable
* @typedef {INopeObservable}
* @template T The internal Datatype of the Observable. This is the datatype of the `_value` property
* @template S Datatype, the `setContent` method must receive
* @template G Datatype, that will be during forwarding the event data.
* @template AD The Additional Event-Data, that observers will receive.
* @extends {INopeEventEmitter<T, S, G, AD>}
*/
export interface INopeObservable<
T = any,
S = T,
G = T,
AD extends IEventAdditionalData = IEventAdditionalData
> extends INopeEventEmitter<T, S, G, AD> {
/**
* The original Observable. Implemented by an Behaviour Subject.
* See here: https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject
* for more details.
*/
observable: BehaviorSubject<IObservableType<G, AD>>;
/**
* Accessor to the currently stored value.
*/
_value: T;
/**
* Function to update the Content
* @param value The content
* @param sender A sender, how has updated the Content (Optional)
* @param timeStamp Timestampt of the Update (optional)
* @param data
*/
setContent(value: S | null, options?: Partial<AD>): boolean;
/**
* Function to Force an Update.
* @param sender Sender, which initiated the Update
* @param timestamp The Timestamp of the Updaet
* @param data Additional Args.
*/
forcePublish(options?: Partial<AD>): boolean;
/**
* Function to extract the Content.
* If a Getter is provided, the {@link INopeEventEmitter.getter} will be used
* to Transform the data.
*/
getContent(): G | null;
}