@angular-architects/ngrx-toolkit 20.3.0 → 20.4.0

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, withLinkedState } 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) {
@@ -1811,47 +1812,73 @@ const exhaustOp = {
1811
1812
  *
1812
1813
  * The `operation` is the only mandatory option.
1813
1814
  *
1815
+ * The returned mutation can be called as an async function and returns a Promise.
1816
+ * This promise informs about whether the mutation was successful, failed, or aborted
1817
+ * (due to switchMap or exhaustMap semantics).
1818
+ *
1819
+ * The mutation also provides several Signals such as error, status or isPending (see below).
1820
+ *
1821
+ * Example usage without Store:
1822
+ *
1814
1823
  * ```typescript
1815
- * export type Params = {
1816
- * value: number;
1817
- * };
1824
+ * const counterSignal = signal(0);
1818
1825
  *
1819
- * export const CounterStore = signalStore(
1820
- * { providedIn: 'root' },
1821
- * withState({ counter: 0 }),
1822
- * withMutations((store) => ({
1823
- * increment: rxMutation({
1824
- * operation: (params: Params) => {
1825
- * return calcSum(store.counter(), params.value);
1826
- * },
1827
- * operator: concatOp,
1828
- * onSuccess: (result) => {
1829
- * console.log('result', result);
1830
- * patchState(store, { counter: result });
1831
- * },
1832
- * onError: (error) => {
1833
- * console.error('Error occurred:', error);
1834
- * },
1835
- * }),
1836
- * })),
1837
- * );
1826
+ * const increment = rxMutation({
1827
+ * operation: (param: Param) => {
1828
+ * return calcSum(this.counterSignal(), param.value);
1829
+ * },
1830
+ * operator: concatOp,
1831
+ * onSuccess: (result) => {
1832
+ * this.counterSignal.set(result);
1833
+ * },
1834
+ * onError: (error) => {
1835
+ * console.error('Error occurred:', error);
1836
+ * },
1837
+ * });
1838
+ *
1839
+ * const error = increment.error;
1840
+ * const isPending = increment.isPending;
1841
+ * const status = increment.status;
1842
+ * const value = increment.value;
1843
+ * const hasValue = increment.hasValue;
1844
+ *
1845
+ * async function incrementCounter() {
1846
+ * const result = await increment({ value: 1 });
1847
+ * if (result.status === 'success') {
1848
+ * console.log('Success:', result.value);
1849
+ * }
1850
+ * if (result.status === 'error') {
1851
+ * console.log('Error:', result.error);
1852
+ * }
1853
+ * if (result.status === 'aborted') {
1854
+ * console.log('Operation aborted');
1855
+ * }
1856
+ * }
1838
1857
  *
1839
1858
  * function calcSum(a: number, b: number): Observable<number> {
1840
- * return of(a + b);
1859
+ * return of(result).pipe(delay(500));
1841
1860
  * }
1842
1861
  * ```
1843
1862
  *
1844
1863
  * @param options
1845
- * @returns
1864
+ * @returns the actual mutation function along tracking data as properties/methods
1846
1865
  */
1847
- function rxMutation(options) {
1866
+ function rxMutation(optionsOrOperation) {
1848
1867
  const inputSubject = new Subject();
1868
+ const options = typeof optionsOrOperation === 'function'
1869
+ ? { operation: optionsOrOperation }
1870
+ : optionsOrOperation;
1849
1871
  const flatteningOp = options.operator ?? concatOp;
1850
1872
  const destroyRef = options.injector?.get(DestroyRef) ?? inject(DestroyRef);
1851
1873
  const callCount = signal(0, ...(ngDevMode ? [{ debugName: "callCount" }] : []));
1852
1874
  const errorSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "errorSignal" }] : []));
1853
1875
  const idle = signal(true, ...(ngDevMode ? [{ debugName: "idle" }] : []));
1854
1876
  const isPending = computed(() => callCount() > 0, ...(ngDevMode ? [{ debugName: "isPending" }] : []));
1877
+ const value = signal(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
1878
+ const isSuccess = computed(() => !idle() && !isPending() && !errorSignal(), ...(ngDevMode ? [{ debugName: "isSuccess" }] : []));
1879
+ const hasValue = function () {
1880
+ return typeof value() !== 'undefined';
1881
+ };
1855
1882
  const status = computed(() => {
1856
1883
  if (idle()) {
1857
1884
  return 'idle';
@@ -1866,7 +1893,6 @@ function rxMutation(options) {
1866
1893
  }, ...(ngDevMode ? [{ debugName: "status" }] : []));
1867
1894
  const initialInnerStatus = 'idle';
1868
1895
  let innerStatus = initialInnerStatus;
1869
- let lastResult;
1870
1896
  inputSubject
1871
1897
  .pipe(flatteningOp.rxJsOperator((input) => defer(() => {
1872
1898
  callCount.update((c) => c + 1);
@@ -1875,10 +1901,11 @@ function rxMutation(options) {
1875
1901
  options.onSuccess?.(result, input.param);
1876
1902
  innerStatus = 'success';
1877
1903
  errorSignal.set(undefined);
1878
- lastResult = result;
1904
+ value.set(result);
1879
1905
  }), catchError((error) => {
1880
1906
  options.onError?.(error, input.param);
1881
1907
  errorSignal.set(error);
1908
+ value.set(undefined);
1882
1909
  innerStatus = 'error';
1883
1910
  return EMPTY;
1884
1911
  }), finalize(() => {
@@ -1886,7 +1913,7 @@ function rxMutation(options) {
1886
1913
  if (innerStatus === 'success') {
1887
1914
  input.resolve({
1888
1915
  status: 'success',
1889
- value: lastResult,
1916
+ value: value(),
1890
1917
  });
1891
1918
  }
1892
1919
  else if (innerStatus === 'error') {
@@ -1923,6 +1950,9 @@ function rxMutation(options) {
1923
1950
  mutation.status = status;
1924
1951
  mutation.isPending = isPending;
1925
1952
  mutation.error = errorSignal;
1953
+ mutation.value = value;
1954
+ mutation.hasValue = hasValue;
1955
+ mutation.isSuccess = isSuccess;
1926
1956
  return mutation;
1927
1957
  }
1928
1958
 
@@ -2060,9 +2090,107 @@ function mapToResource(store, name) {
2060
2090
  };
2061
2091
  }
2062
2092
 
2093
+ /**
2094
+ * Creates an HTTP mutation.
2095
+ *
2096
+ * ```typescript
2097
+ * export type Params = {
2098
+ * value: number;
2099
+ * };
2100
+ *
2101
+ * export type CounterResponse = {
2102
+ * // httpbin.org echos the request using the
2103
+ * // json property
2104
+ * json: { counter: number };
2105
+ * };
2106
+ *
2107
+ * const simpleSaveUser = httpMutation({
2108
+ * request: (userData: AddUserEntry) => ({
2109
+ * url: 'api/users',
2110
+ * body: userData,
2111
+ * }),
2112
+ * parse: Boolean,
2113
+ * })
2114
+ *
2115
+ * const saveUser = httpMutation({
2116
+ * request: (p: Params) => ({
2117
+ * url: `https://httpbin.org/post`,
2118
+ * method: 'POST',
2119
+ * body: { counter: p.value },
2120
+ * headers: { 'Content-Type': 'application/json' },
2121
+ * }),
2122
+ * onSuccess: (response: CounterResponse) => {
2123
+ * console.log('Counter sent to server:', response);
2124
+ * },
2125
+ * onError: (error) => {
2126
+ * console.error('Failed to send counter:', error);
2127
+ * },
2128
+ * });
2129
+ *
2130
+ * const result = await this.saveUser({ value: 17 });
2131
+ * if (result.status === 'success') {
2132
+ * console.log('Successfully saved to server:', result.value);
2133
+ * }
2134
+ * else if (result.status === 'error') {
2135
+ * console.log('Failed to save:', result.error);
2136
+ * }
2137
+ * else {
2138
+ * console.log('Operation aborted');
2139
+ * }
2140
+ * ```
2141
+ *
2142
+ * @param options The options for the HTTP mutation.
2143
+ * @returns The HTTP mutation.
2144
+ */
2145
+ function httpMutation(optionsOrRequest) {
2146
+ const httpClient = inject(HttpClient);
2147
+ const options = typeof optionsOrRequest === 'function'
2148
+ ? { request: optionsOrRequest }
2149
+ : optionsOrRequest;
2150
+ const parse = options.parse ?? ((raw) => raw);
2151
+ const uploadProgress = signal(undefined, ...(ngDevMode ? [{ debugName: "uploadProgress" }] : []));
2152
+ const downloadProgress = signal(undefined, ...(ngDevMode ? [{ debugName: "downloadProgress" }] : []));
2153
+ const headers = signal(undefined, ...(ngDevMode ? [{ debugName: "headers" }] : []));
2154
+ const statusCode = signal(undefined, ...(ngDevMode ? [{ debugName: "statusCode" }] : []));
2155
+ const mutation = rxMutation({
2156
+ ...options,
2157
+ operation: (param) => {
2158
+ const httpRequest = options.request(param);
2159
+ return defer(() => {
2160
+ uploadProgress.set(undefined);
2161
+ downloadProgress.set(undefined);
2162
+ headers.set(undefined);
2163
+ statusCode.set(undefined);
2164
+ return httpClient
2165
+ .request(httpRequest.method, httpRequest.url, {
2166
+ ...httpRequest,
2167
+ observe: 'events',
2168
+ responseType: 'json',
2169
+ })
2170
+ .pipe(tap((response) => {
2171
+ if (response.type === HttpEventType.UploadProgress) {
2172
+ uploadProgress.set(response);
2173
+ }
2174
+ else if (response.type === HttpEventType.DownloadProgress) {
2175
+ downloadProgress.set(response);
2176
+ }
2177
+ }), filter((event) => event instanceof HttpResponse), tap((response) => {
2178
+ headers.set(response.headers);
2179
+ statusCode.set(response.status.toString());
2180
+ }), map((event) => parse(event.body)));
2181
+ });
2182
+ },
2183
+ });
2184
+ mutation.uploadProgress = uploadProgress;
2185
+ mutation.downloadProgress = downloadProgress;
2186
+ mutation.statusCode = statusCode;
2187
+ mutation.headers = headers;
2188
+ return mutation;
2189
+ }
2190
+
2063
2191
  /**
2064
2192
  * Generated bundle index. Do not edit.
2065
2193
  */
2066
2194
 
2067
- export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, mapToResource, 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, withResource, withSessionStorage, withStorageSync, withUndoRedo };
2195
+ export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mapToResource, 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, withResource, withSessionStorage, withStorageSync, withUndoRedo };
2068
2196
  //# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map