An ObservableStore maintains data of generic type StoreDataT, providing methods to set the fields, in turn emitting values on requested Observables. The values are emitted as read-only objects to prevent manipulation of the internal data structure, though it is also possible to request a deep clone of the objects.

The ObservableStore can write its state to LocalStorage, making it possible to easily recreate the application state when reloading the app.

(The ObservableStore is itself an Observable of type StoreDataT, acting like a BehaviorSubject, maintaining a single value and replaying it to new subscribers).

Example

interface ApplicationData {
name: string;
count: number;
}
const applicationState = new ObservableStore<ApplicationData>({
name: 'Initial value',
count: 0
});
applicationState.observe('name').subscribe(value => console.log(value));
applicationState.set('name', 'New name');

Type Parameters

  • StoreDataT

Hierarchy

  • Observable<StoreDataT>

    Hierarchy

    • ObservableStore

Constructors

  • Example

    new ObservableStore<TestObject>(
    {a: 'A', b: 1},
    {
    a: {serializer: v => JSON.stringify(v), deserializer: json => JSON.parse(json)},
    b: true
    }
    );

    Type Parameters

    Type Parameters

    • StoreDataT

    Parameters

    • initialValue: StoreDataT

      A valid instance of type StoreDataT, serving as the initial value of the data.

    • Optional fieldsToPersist: { [ K in string | number | symbol]?: boolean | { deserializer?: ((json: string) => StoreDataT[K]); serializer?: ((value: StoreDataT[K]) => string) } }

      An object describing which fields in the store data to persist in localStorage. The object consists of keys matching the keys in StoreDataT´, with either a boolean value (true if the value should be persisted as standard JSON), or an object with custom serializer/deserializers for the field. @param storeNamespace If given, will be used when storing state in LocalStorage. This allows an app to use more than one ObservableStore` without their values colliding.

    • Optional storeNamespace: string

    Returns ObservableStore<StoreDataT>

Methods

  • Concats the given element to the list in field key, updating the value as per set

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT
    • element: ArrayElement<StoreDataT[KeyInStoreT]>

    Returns void

  • Updates the ObservableStore based on the current values in the store. This is useful if you need functionality such as "add an item to an existing array in the store". (Remember that the store values should be treated as immutable, so in this example, you should .concat rather than .push).

    Returns

    A DeepReadonly version of the result of the update function; i.e. the same object which has been set in the store.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • updateFunction: ((storeData: DeepReadonly<StoreDataT>) => DeepReadonly<Pick<StoreDataT, KeyInStoreT>>)

      A function which takes the current data of the store and returns an object with a subset (Pick) of the fields. The calculated fields will be updated in the store, and any Observables will be triggered as if the values had been set with set.

        • (storeData: DeepReadonly<StoreDataT>): DeepReadonly<Pick<StoreDataT, KeyInStoreT>>
        • Parameters

          • storeData: DeepReadonly<StoreDataT>

          Returns DeepReadonly<Pick<StoreDataT, KeyInStoreT>>

    Returns DeepReadonly<Pick<StoreDataT, KeyInStoreT>>

  • Updates the ObservableStore based on the current values in the store.

    Returns

    A DeepReadonly version of the result of the update function; i.e. the same object which has been set in the store.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • updateFunction: ((storeData: DeepReadonly<StoreDataT>) => DeepReadonly<Partial<StoreDataT>>)

      A function which takes the current data of the store and returns an object with a subset (Partial) of the fields. This is a looser typing than a Pick, in turn giving less information about the return type. Also, if you do not use exactOptionalPropertyTypes: true, be careful not to break the types by explicitly setting an undefined value in an optional field. The calculated fields will be updated in the store, and any Observables will be triggered as if the values had been set with set.

        • (storeData: DeepReadonly<StoreDataT>): DeepReadonly<Partial<StoreDataT>>
        • Parameters

          • storeData: DeepReadonly<StoreDataT>

          Returns DeepReadonly<Partial<StoreDataT>>

    Returns DeepReadonly<Partial<StoreDataT>>

  • Updates the ObservableStore asynchronously based on the current values in the store. This is useful if you need functionality such as "add an item to an existing array in the store". (Remember that the store values should be treated as immutable, so in this example, you should .concat rather than .push).

    Returns

    A DeepReadonly version of the result of the update function; i.e. the same Promised object which will be set in the store.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • updateFunction: ((storeData: DeepReadonly<StoreDataT>) => Promise<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>)

      A function which takes the current data of the store and returns a Promise of a subset (Pick) of the fields. Once the Promise fulfills, the calculated fields will be updated in the store, and any Observables will be triggered as if the values had been set with set.

        • (storeData: DeepReadonly<StoreDataT>): Promise<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>
        • Parameters

          • storeData: DeepReadonly<StoreDataT>

          Returns Promise<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>

    Returns Promise<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>

  • Updates the ObservableStore asynchronously based on the current values in the store. This is useful if you need functionality

    Returns

    A DeepReadonly version of the result of the update function; i.e. the same Promised object which will be set in the store.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • updateFunction: ((storeData: DeepReadonly<StoreDataT>) => Promise<DeepReadonly<Partial<StoreDataT>>>)

      A function which takes the current data of the store and returns a Promise of a subset (Partial) of the fields. This is a looser typing than a Pick, in turn giving less information about the return type. Also, if you do not use exactOptionalPropertyTypes: true, be careful not to break the types by explicitly setting an undefined value in an optional field. Once the Promise fulfills, the calculated fields will be updated in the store, and any Observables will be triggered as if the values had been set with set.

        • (storeData: DeepReadonly<StoreDataT>): Promise<DeepReadonly<Partial<StoreDataT>>>
        • Parameters

          • storeData: DeepReadonly<StoreDataT>

          Returns Promise<DeepReadonly<Partial<StoreDataT>>>

    Returns Promise<DeepReadonly<Partial<StoreDataT>>>

  • Angular-specific utility method binding a given FormControl(Like) to a key in the store. The FormControl will be initialized with the current value in the store, and will then be kept in sync with the store, updating each other as changes happen in one or the other.

    Returns

    A SubscriptionLike which must be used to unsubscribe the binding when the FormControl is disposed of.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

      The store key to bind to.

    • formControl: FormControlLike

      An Angular FormControl. It is assumed that the formControl values will be of the correct type for the store.

    Returns SubscriptionLike

  • Angular-specific utility method binding a given FormControl(Like) to a key in the store. The FormControl will be initialized with the current value in the store, and will then be kept in sync with the store, updating each other as changes happen in one or the other.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

      The store key to bind to.

    • formControl: FormControlLike

      An Angular FormControl. It is assumed that the formControl values will be of the correct type for the store.

    • until: Observable<unknown>

      An Observable which signals when this binding should be disconnected. Typically, this will be an Observable signaling when the Angular component is being destroyed.

    Returns void

  • Returns the current value of the internal data.

    Returns DeepReadonly<StoreDataT>

  • Returns an Observable which emits the values for the given key as they are updated.

    If an observed field is of an object type, e.g. MyInterface, the returned value will be of utility type DeepReadonly<MyInterface>. (If the returned object was not immutable, it would be possible to manipulate the internal state of the ObservableStore by reference without triggering change notifications).

    This is essentially a shorthand for

    Example

    observableStore.pipe(
    map(data => {
    const o = data.key;
    return o as DeepReadonly<typeof o>;
    }),
    distinctUntilChanged((a, b) => deepEquals(a, b))
    );

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

    Returns Observable<DeepReadonly<StoreDataT[KeyInStoreT]>>

  • Returns an Observable of an object consisting of the specified fields.

    If an observed field is of an object type, e.g. MyInterface, the returned value will be of utility type DeepReadonly<MyInterface>. (If the returned object was not immutable, it would be possible to manipulate the internal state of the ObservableStore by reference without triggering change notifications).

    This is essentially a shorthand for

    Example

    observableStore.pipe(
    map(data => {
    const o = {
    k1: data.k1,
    k2: data.k2
    };
    return o as DeepReadonly<typeof o>;
    }),
    distinctUntilChanged((a, b) => deepEquals(a, b))
    );

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT
    • Rest ...additionalKeys: KeyInStoreT[]

    Returns Observable<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>

  • Returns an Observable of an object consisting of the specified fields, given as an array.

    If an observed field is of an object type, e.g. MyInterface, the returned value will be of utility type DeepReadonly<MyInterface>. (If the returned object was not immutable, it would be possible to manipulate the internal state of the ObservableStore by reference without triggering change notifications).

    This is equivalent to

    Example

    observableStore.pipe(
    map(data => {
    const o = {
    k1: data.k1,
    k2: data.k2
    };
    return o as DeepReadonly<typeof o>;
    }),
    distinctUntilChanged((a, b) => deepEquals(a, b))
    );

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • keys: KeyInStoreT[]

    Returns Observable<DeepReadonly<Pick<StoreDataT, KeyInStoreT>>>

  • As observe, but instead of returning DeepReadonly objects, it returns a deep clone (using structuredClone). This means you're free to manipulate the returned object without violating the immutability of the ObservableStore. Be aware that this can only clone data. Object prototypes and methods will not be cloned.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

    Returns Observable<StoreDataT[KeyInStoreT]>

  • As observe, but instead of returning DeepReadonly objects, it returns a deep clone (using structuredClone). This means you're free to manipulate the returned object without violating the immutability of the ObservableStore. Be aware that this can only clone data. Object prototypes and methods will not be cloned.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT
    • Rest ...additionalKeys: KeyInStoreT[]

    Returns Observable<Pick<StoreDataT, KeyInStoreT>>

  • As observe, but instead of returning DeepReadonly objects, it returns a deep clone (using structuredClone). This means you're free to manipulate the returned object without violating the immutability of the ObservableStore. Be aware that this can only clone data. Object prototypes and methods will not be cloned.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • keys: KeyInStoreT[]

    Returns Observable<Pick<StoreDataT, KeyInStoreT>>

  • As observe, but emits a tuple of [previous, current] values. previous will be undefined at the first emit.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

    Returns Observable<[previous: DeepReadonly<StoreDataT[KeyInStoreT]>, current: DeepReadonly<StoreDataT[KeyInStoreT]>]>

  • As observe, but emits a tuple of [previous, current] values. previous will be undefined at the first emit.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT
    • Rest ...additionalKeys: KeyInStoreT[]

    Returns Observable<[previous: DeepReadonly<Pick<StoreDataT, KeyInStoreT>>, current: DeepReadonly<Pick<StoreDataT, KeyInStoreT>>]>

  • As observe, but emits a tuple of [previous, current] values. previous will be undefined at the first emit.

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • keys: KeyInStoreT[]

    Returns Observable<[previous: DeepReadonly<Pick<StoreDataT, KeyInStoreT>>, current: DeepReadonly<Pick<StoreDataT, KeyInStoreT>>]>

  • Sets a given field to a given value. If the new value is not equal to the current value, it will trigger a notification in the relevant Observables. (See observe)

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • key: KeyInStoreT

      The name of the field. Must be a field in type StoreDataT

    • value: DeepReadonly<StoreDataT[KeyInStoreT]>

      The new value. Must be of the appropriate type.

    Returns void

  • Sets several fields at once. If any of the new values are not equal to the current value, it will trigger a notification in the relevant Observables. (See observe)

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • keyValueObject: DeepReadonly<Pick<StoreDataT, KeyInStoreT>>

      An object containing key/value pairs to set. The object must be a subset (Pick) of type StoreDataT

    Returns void

  • Angular-specific utility method which sets the given keys from a ParamMap-like object in the store, restricted to only the keys which are of type number, string or undefined in the ObservableStore (since they are the types which can reliably be expressed in the route without needing custom deserialization).

    Example

    ngOnInit(): void {
    this.activatedRoute.paramMap
    .subscribe(paramMap => this.applicationState.setParamMap(paramMap, 'wantedUserId'));
    ...

    Type Parameters

    Type Parameters

    • KeyInStoreT extends string | number | symbol

    Parameters

    • params: ParamMapLike

      A ParamMaplike object, as obtained from Angular's ActivatedRoute.paramMap Observable.

    • Rest ...keys: KeyInStoreT[]

      The name of the keys to pick from the ParamMap and set in the ObservableStore.

    Returns Pick<StoreDataT, KeyInStoreT>

  • Transfers the given fields from the other store to this, and keeps the two stores in sync after that. Setting a value in one will cause it to also be set in the other, and vice versa. This is particularly useful for sharing data from a minimal, application-wide state to lazy-loaded daughter modules.

    Type Parameters

    Type Parameters

    • OtherStoreDataT

    • KeyT extends string | number | symbol

    Parameters

    • otherStore: ObservableStore<OtherStoreDataT>

      The store to synchronize data with. The given store will function as master for the initial synchronization, overriding values in this store.

    • Rest ...keys: KeyT[]

      The fields to synchronize. They must exist in both stores, and have compatible data types.

    Returns void