@angular-architects/ngrx-toolkit 19.3.0 → 19.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,10 @@ import * as i0 from '@angular/core';
2
2
  import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, computed, isSignal, untracked, isDevMode as isDevMode$1, DestroyRef } from '@angular/core';
3
3
  import { watchState, getState, signalStoreFeature, withMethods, withHooks, patchState as patchState$1, withState, withComputed, withProps } from '@ngrx/signals';
4
4
  import { isPlatformBrowser, isPlatformServer } from '@angular/common';
5
- import { Subject, switchMap, mergeMap, concatMap, exhaustMap, defer, tap, catchError, EMPTY, finalize } from 'rxjs';
5
+ import { Subject, switchMap, mergeMap, concatMap, exhaustMap, defer, tap, catchError, EMPTY, finalize, filter, map } from 'rxjs';
6
6
  import { removeEntity, setAllEntities, updateEntity, addEntity } from '@ngrx/signals/entities';
7
7
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
+ import { HttpClient, HttpEventType, HttpResponse } from '@angular/common/http';
8
9
 
9
10
  const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');
10
11
  function createDevtoolsFeature(options) {
@@ -1809,47 +1810,73 @@ const exhaustOp = {
1809
1810
  *
1810
1811
  * The `operation` is the only mandatory option.
1811
1812
  *
1813
+ * The returned mutation can be called as an async function and returns a Promise.
1814
+ * This promise informs about whether the mutation was successful, failed, or aborted
1815
+ * (due to switchMap or exhaustMap semantics).
1816
+ *
1817
+ * The mutation also provides several Signals such as error, status or isPending (see below).
1818
+ *
1819
+ * Example usage without Store:
1820
+ *
1812
1821
  * ```typescript
1813
- * export type Params = {
1814
- * value: number;
1815
- * };
1822
+ * const counterSignal = signal(0);
1816
1823
  *
1817
- * export const CounterStore = signalStore(
1818
- * { providedIn: 'root' },
1819
- * withState({ counter: 0 }),
1820
- * withMutations((store) => ({
1821
- * increment: rxMutation({
1822
- * operation: (params: Params) => {
1823
- * return calcSum(store.counter(), params.value);
1824
- * },
1825
- * operator: concatOp,
1826
- * onSuccess: (result) => {
1827
- * console.log('result', result);
1828
- * patchState(store, { counter: result });
1829
- * },
1830
- * onError: (error) => {
1831
- * console.error('Error occurred:', error);
1832
- * },
1833
- * }),
1834
- * })),
1835
- * );
1824
+ * const increment = rxMutation({
1825
+ * operation: (param: Param) => {
1826
+ * return calcSum(this.counterSignal(), param.value);
1827
+ * },
1828
+ * operator: concatOp,
1829
+ * onSuccess: (result) => {
1830
+ * this.counterSignal.set(result);
1831
+ * },
1832
+ * onError: (error) => {
1833
+ * console.error('Error occurred:', error);
1834
+ * },
1835
+ * });
1836
+ *
1837
+ * const error = increment.error;
1838
+ * const isPending = increment.isPending;
1839
+ * const status = increment.status;
1840
+ * const value = increment.value;
1841
+ * const hasValue = increment.hasValue;
1842
+ *
1843
+ * async function incrementCounter() {
1844
+ * const result = await increment({ value: 1 });
1845
+ * if (result.status === 'success') {
1846
+ * console.log('Success:', result.value);
1847
+ * }
1848
+ * if (result.status === 'error') {
1849
+ * console.log('Error:', result.error);
1850
+ * }
1851
+ * if (result.status === 'aborted') {
1852
+ * console.log('Operation aborted');
1853
+ * }
1854
+ * }
1836
1855
  *
1837
1856
  * function calcSum(a: number, b: number): Observable<number> {
1838
- * return of(a + b);
1857
+ * return of(result).pipe(delay(500));
1839
1858
  * }
1840
1859
  * ```
1841
1860
  *
1842
1861
  * @param options
1843
- * @returns
1862
+ * @returns the actual mutation function along tracking data as properties/methods
1844
1863
  */
1845
- function rxMutation(options) {
1864
+ function rxMutation(optionsOrOperation) {
1846
1865
  const inputSubject = new Subject();
1866
+ const options = typeof optionsOrOperation === 'function'
1867
+ ? { operation: optionsOrOperation }
1868
+ : optionsOrOperation;
1847
1869
  const flatteningOp = options.operator ?? concatOp;
1848
1870
  const destroyRef = options.injector?.get(DestroyRef) ?? inject(DestroyRef);
1849
1871
  const callCount = signal(0);
1850
1872
  const errorSignal = signal(undefined);
1851
1873
  const idle = signal(true);
1852
1874
  const isPending = computed(() => callCount() > 0);
1875
+ const value = signal(undefined);
1876
+ const isSuccess = computed(() => !idle() && !isPending() && !errorSignal());
1877
+ const hasValue = function () {
1878
+ return typeof value() !== 'undefined';
1879
+ };
1853
1880
  const status = computed(() => {
1854
1881
  if (idle()) {
1855
1882
  return 'idle';
@@ -1864,7 +1891,6 @@ function rxMutation(options) {
1864
1891
  });
1865
1892
  const initialInnerStatus = 'idle';
1866
1893
  let innerStatus = initialInnerStatus;
1867
- let lastResult;
1868
1894
  inputSubject
1869
1895
  .pipe(flatteningOp.rxJsOperator((input) => defer(() => {
1870
1896
  callCount.update((c) => c + 1);
@@ -1873,10 +1899,11 @@ function rxMutation(options) {
1873
1899
  options.onSuccess?.(result, input.param);
1874
1900
  innerStatus = 'success';
1875
1901
  errorSignal.set(undefined);
1876
- lastResult = result;
1902
+ value.set(result);
1877
1903
  }), catchError((error) => {
1878
1904
  options.onError?.(error, input.param);
1879
1905
  errorSignal.set(error);
1906
+ value.set(undefined);
1880
1907
  innerStatus = 'error';
1881
1908
  return EMPTY;
1882
1909
  }), finalize(() => {
@@ -1884,7 +1911,7 @@ function rxMutation(options) {
1884
1911
  if (innerStatus === 'success') {
1885
1912
  input.resolve({
1886
1913
  status: 'success',
1887
- value: lastResult,
1914
+ value: value(),
1888
1915
  });
1889
1916
  }
1890
1917
  else if (innerStatus === 'error') {
@@ -1921,6 +1948,9 @@ function rxMutation(options) {
1921
1948
  mutation.status = status;
1922
1949
  mutation.isPending = isPending;
1923
1950
  mutation.error = errorSignal;
1951
+ mutation.value = value;
1952
+ mutation.hasValue = hasValue;
1953
+ mutation.isSuccess = isSuccess;
1924
1954
  return mutation;
1925
1955
  }
1926
1956
 
@@ -1959,9 +1989,107 @@ function createMutationsFeature(mutations) {
1959
1989
  return feature;
1960
1990
  }
1961
1991
 
1992
+ /**
1993
+ * Creates an HTTP mutation.
1994
+ *
1995
+ * export type Params = {
1996
+ * value: number;
1997
+ * };
1998
+ *
1999
+ * export type CounterResponse = {
2000
+ * // httpbin.org echos the request using the
2001
+ * // json property
2002
+ * json: { counter: number };
2003
+ * };
2004
+ *
2005
+ * const simpleSaveUser = httpMutation({
2006
+ * request: (userData: AddUserEntry) => ({
2007
+ * url: 'api/users',
2008
+ * body: userData,
2009
+ * }),
2010
+ * parse: Boolean,
2011
+ * })
2012
+ *
2013
+ * const saveUser = httpMutation({
2014
+ * request: (p: Params) => ({
2015
+ * url: `https://httpbin.org/post`,
2016
+ * method: 'POST',
2017
+ * body: { counter: p.value },
2018
+ * headers: { 'Content-Type': 'application/json' },
2019
+ * }),
2020
+ * onSuccess: (response: CounterResponse) => {
2021
+ * console.log('Counter sent to server:', response);
2022
+ * },
2023
+ * onError: (error) => {
2024
+ * console.error('Failed to send counter:', error);
2025
+ * },
2026
+ * });
2027
+ *
2028
+ * ...
2029
+ *
2030
+ * const result = await this.saveUser({ value: 17 });
2031
+ * if (result.status === 'success') {
2032
+ * console.log('Successfully saved to server:', result.value);
2033
+ * }
2034
+ * else if (result.status === 'error') {
2035
+ * console.log('Failed to save:', result.error);
2036
+ * }
2037
+ * else {
2038
+ * console.log('Operation aborted');
2039
+ * }
2040
+ *
2041
+ * @param options The options for the HTTP mutation.
2042
+ * @returns The HTTP mutation.
2043
+ */
2044
+ function httpMutation(optionsOrRequest) {
2045
+ const httpClient = inject(HttpClient);
2046
+ const options = typeof optionsOrRequest === 'function'
2047
+ ? { request: optionsOrRequest }
2048
+ : optionsOrRequest;
2049
+ const parse = options.parse ?? ((raw) => raw);
2050
+ const uploadProgress = signal(undefined);
2051
+ const downloadProgress = signal(undefined);
2052
+ const headers = signal(undefined);
2053
+ const statusCode = signal(undefined);
2054
+ const mutation = rxMutation({
2055
+ ...options,
2056
+ operation: (param) => {
2057
+ const httpRequest = options.request(param);
2058
+ return defer(() => {
2059
+ uploadProgress.set(undefined);
2060
+ downloadProgress.set(undefined);
2061
+ headers.set(undefined);
2062
+ statusCode.set(undefined);
2063
+ return httpClient
2064
+ .request(httpRequest.method, httpRequest.url, {
2065
+ ...httpRequest,
2066
+ observe: 'events',
2067
+ responseType: 'json',
2068
+ })
2069
+ .pipe(tap((response) => {
2070
+ if (response.type === HttpEventType.UploadProgress) {
2071
+ uploadProgress.set(response);
2072
+ }
2073
+ else if (response.type === HttpEventType.DownloadProgress) {
2074
+ downloadProgress.set(response);
2075
+ }
2076
+ }), filter((event) => event instanceof HttpResponse), tap((response) => {
2077
+ headers.set(response.headers);
2078
+ statusCode.set(response.status.toString());
2079
+ }), map((event) => parse(event.body)));
2080
+ });
2081
+ },
2082
+ });
2083
+ mutation.uploadProgress = uploadProgress;
2084
+ mutation.downloadProgress = downloadProgress;
2085
+ mutation.statusCode = statusCode;
2086
+ mutation.headers = headers;
2087
+ return mutation;
2088
+ }
2089
+
1962
2090
  /**
1963
2091
  * Generated bundle index. Do not edit.
1964
2092
  */
1965
2093
 
1966
- export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withSessionStorage, withStorageSync, withUndoRedo };
2094
+ export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withSessionStorage, withStorageSync, withUndoRedo };
1967
2095
  //# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map