my-http-resource
Version:
Reactive Angular HttpClient wrapper with RxJS & Signals. Angular 17+.
134 lines (127 loc) • 4.52 kB
JavaScript
import { signal, inject, DestroyRef } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
class ReactiveHttpModel {
value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
constructor(initialValue) {
if (initialValue) {
this.value.set(initialValue);
}
}
}
class ParamsModel {
body = {};
params = {};
headers = {};
constructor(data) {
if ('queryParams' in data)
this.params = data.queryParams;
if ('body' in data)
this.body = data.body;
if ('headers' in data)
this.headers = new HttpHeaders(data.headers);
}
}
class MyHttpService {
http = inject(HttpClient);
destroyRef = inject(DestroyRef);
getAfterActions(reactiveData, data) {
return {
next: (result) => {
const value = data.mergeValues
? this.mergeResult(result, reactiveData.value())
: result;
reactiveData.value.set(value);
if (data.afterSuccess)
data.afterSuccess(value);
reactiveData.loading.set(false);
},
error: (error) => {
reactiveData.error.set(error);
if (data.afterError)
data.afterError(error);
reactiveData.loading.set(false);
},
};
}
mergeResult(result, currentValue) {
if (!result || !currentValue || typeof result !== typeof currentValue) {
return result;
}
if (Array.isArray(result) && Array.isArray(currentValue)) {
return [...currentValue, ...result];
}
if (typeof result === 'object' &&
typeof currentValue === 'object' &&
!Array.isArray(result) &&
!Array.isArray(currentValue)) {
return { ...currentValue, ...result };
}
return result;
}
createUrl(url, urlParams) {
return url.replace(/{{(.*?)}}/g, (_, key) => {
const trimmedKey = key.trim();
const value = urlParams?.[trimmedKey];
if (value === undefined) {
throw new Error(`Missing value for URL parameter: ${trimmedKey}`);
}
return encodeURIComponent(value);
});
}
getHttp(method, data) {
const url = this.createUrl(data.url, data.urlParams);
const { body, params, headers } = new ParamsModel(data);
const isGetOrDelete = ['get', 'delete'].includes(method);
const request$ = isGetOrDelete
? this.http[method](url, { params, headers })
: this.http[method](url, body, { headers });
const baseRequest = request$.pipe(takeUntilDestroyed(this.destroyRef));
return data.pipe ? baseRequest.pipe(data.pipe) : baseRequest;
}
get(data) {
return this.createResource(data, 'get');
}
post(data) {
return this.createResource(data, 'post');
}
patch(data) {
return this.createResource(data, 'patch');
}
put(data) {
return this.createResource(data, 'put');
}
delete(data) {
return this.createResource(data, 'delete');
}
createResource(data, method) {
const reactiveData = new ReactiveHttpModel(data.initialValue);
if (!data.manual) {
reactiveData.loading.set(true);
this.getHttp(method, data).subscribe(this.getAfterActions(reactiveData, data));
}
return {
value: reactiveData.value,
error: reactiveData.error,
loading: reactiveData.loading.asReadonly(),
request$: (fetchData) => {
return this.getHttp(method, { ...data, ...fetchData });
},
fetch: (fetchData) => {
reactiveData.loading.set(true);
const mergedData = { ...data, ...fetchData };
this.getHttp(method, mergedData).subscribe(this.getAfterActions(reactiveData, mergedData));
}
};
}
}
function myHttpResource() {
return new MyHttpService();
}
/**
* Generated bundle index. Do not edit.
*/
export { myHttpResource };
//# sourceMappingURL=my-http-resource.mjs.map