UNPKG

nope-js-node

Version:

NoPE Runtime for Nodejs. For Browser-Support please use nope-browser

299 lines (298 loc) 9.46 kB
/** * @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; }