@telperion/ng-pack 1.1.1 → 1.2.2

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.
package/README.md CHANGED
@@ -80,6 +80,7 @@ Angular utility functions and plugins for enhanced development experience.
80
80
 
81
81
  - 🎯 Event modifier syntax for templates (`.pd`, `.sp`)
82
82
  - 🔗 Directive-as-service provider utility
83
+ - 🌐 Promise-based HTTP client with tuple error handling
83
84
  - 📦 Tree-shakeable and type-safe
84
85
  - ⚡ Zero dependencies
85
86
 
@@ -102,6 +103,32 @@ bootstrapApplication(AppComponent, {
102
103
  <button (click.pd.sp)="handleButtonClick()">Click me</button>
103
104
  ```
104
105
 
106
+ **Construct Fetcher**
107
+
108
+ ```typescript
109
+ import { Component } from '@angular/core';
110
+ import { constructFetcher } from '@telperion/ng-pack/utils';
111
+
112
+ @Component({
113
+ selector: 'app-example',
114
+ template: `<button (click)="fetchData()">Fetch Data</button>`
115
+ })
116
+ export class ExampleComponent {
117
+ private fetcher = constructFetcher();
118
+
119
+ async fetchData() {
120
+ const [error, response] = await this.fetcher.get('/api/data');
121
+
122
+ if (error) {
123
+ console.error('Error:', error);
124
+ return;
125
+ }
126
+
127
+ console.log('Data:', response);
128
+ }
129
+ }
130
+ ```
131
+
105
132
  **Provide Service Directive**
106
133
 
107
134
  ```typescript
@@ -1,7 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { forwardRef, Inject, Injectable } from '@angular/core';
2
+ import { forwardRef, Inject, Injectable, inject } from '@angular/core';
3
3
  import { DOCUMENT } from '@angular/common';
4
4
  import { EventManagerPlugin, EVENT_MANAGER_PLUGINS } from '@angular/platform-browser';
5
+ import { HttpClient } from '@angular/common/http';
6
+ import { tryCatch } from '@thalesrc/js-utils/promise/try-catch';
7
+ import { firstValueFrom } from 'rxjs';
5
8
 
6
9
  /**
7
10
  * Utility function to create a provider for a directive/component that can be injected as a service.
@@ -118,9 +121,49 @@ function provideEventModifiersPlugin() {
118
121
  };
119
122
  }
120
123
 
124
+ /**
125
+ * Constructs a Fetcher that proxies HttpClient methods and returns a tuple of [error, response].
126
+ * If the HttpClient method throws an error, it will be caught and returned as the first element of the tuple.
127
+ * If the HttpClient method succeeds, the response will be returned as the second element of the tuple.
128
+ * Example usage:
129
+ * ```
130
+ * @Component({
131
+ * selector: 'app-example',
132
+ * template: `<button (click)="fetchData()">Fetch Data</button>`,
133
+ * })
134
+ * export class ExampleComponent {
135
+ * private fetcher = constructFetcher();
136
+ *
137
+ * async fetchData() {
138
+ * const [error, response] = await this.fetcher.get('/api/data');
139
+ *
140
+ * if (error) {
141
+ * console.error('Error fetching data:', error);
142
+ * } else {
143
+ * console.log('Fetched data:', response);
144
+ * }
145
+ * }
146
+ * }
147
+ * ```
148
+ */
149
+ function constructFetcher() {
150
+ const client = inject(HttpClient);
151
+ return new Proxy({}, {
152
+ get(target, prop) {
153
+ if (prop in client) {
154
+ return (...args) => {
155
+ const method = client[prop];
156
+ return tryCatch(firstValueFrom(method.apply(client, args)));
157
+ };
158
+ }
159
+ throw new Error(`HttpClient does not have method ${String(prop)}`);
160
+ }
161
+ });
162
+ }
163
+
121
164
  /**
122
165
  * Generated bundle index. Do not edit.
123
166
  */
124
167
 
125
- export { provideEventModifiersPlugin, provideServiceDirective };
168
+ export { constructFetcher, provideEventModifiersPlugin, provideServiceDirective };
126
169
  //# sourceMappingURL=telperion-ng-pack-utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"telperion-ng-pack-utils.mjs","sources":["../../utils/src/provide-service-directive.ts","../../utils/src/event-modifiers.plugin.ts","../../utils/src/telperion-ng-pack-utils.ts"],"sourcesContent":["import { forwardRef, InjectionToken, Provider, Type } from \"@angular/core\";\r\n\r\n/**\r\n * Utility function to create a provider for a directive/component that can be injected as a service.\r\n * This is useful for cases where you want to inject a directive/component instance into another directive/component.\r\n *\r\n * Example usage:\r\n *\r\n * files:\r\n * - parent.directive.ts\r\n * - child.directive.ts\r\n * - parent.service.ts\r\n *\r\n * parent.service.ts:\r\n * ```\r\n * import { InjectionToken } from \"@angular/core\";\r\n * import type { ParentDirective } from \"./parent.directive\";\r\n *\r\n * export const ParentService = new InjectionToken<ParentDirective>(\"ParentService\");\r\n * ```\r\n *\r\n * parent.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'parent',\r\n * providers: [provideServiceDirective(ParentService, ParentDirective)],\r\n * })\r\n * export class ParentDirective {...}\r\n * ```\r\n * child.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'child',\r\n * })\r\n * export class ChildDirective {\r\n * #parent = inject(ParentService);\r\n * }\r\n * ```\r\n *\r\n * @param token - The injection token to provide.\r\n * @param directive - The directive/component class that will be provided.\r\n * @returns A provider object that can be used in the providers array of an Angular module or component.\r\n */\r\nexport function provideServiceDirective<T extends Type<unknown>>(\r\n token: InjectionToken<T>,\r\n directive: T\r\n): Provider {\r\n return {\r\n provide: token,\r\n useExisting: forwardRef(() => directive)\r\n }\r\n}\r\n","/**\r\n * Angular Event Manager Plugin that enables event modifier syntax in templates.\r\n *\r\n * @example\r\n * ```html\r\n * <form (submit.pd)=\"onSubmit()\">...</form>\r\n * <div (click.sp)=\"handleClick()\">...</div>\r\n * <button (click.pd.sp)=\"handleButtonClick()\">Click me</button>\r\n * ```\r\n */\r\n\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { Inject, Injectable, Provider } from '@angular/core';\r\nimport { EVENT_MANAGER_PLUGINS, EventManagerPlugin } from '@angular/platform-browser';\r\n\r\n/**\r\n * Event Manager Plugin that adds support for event modifiers.\r\n * @internal\r\n */\r\n@Injectable()\r\nexport class EventModifiersPlugin extends EventManagerPlugin {\r\n /**\r\n * Supported modifiers: `pd` (preventDefault), `sp` (stopPropagation)\r\n */\r\n private static modifierMap: { [key: string]: (e: Event) => void } = {\r\n 'pd': (e: Event) => e.preventDefault(),\r\n 'sp': (e: Event) => e.stopPropagation(),\r\n };\r\n\r\n constructor(@Inject(DOCUMENT) doc: Document) {\r\n super(doc);\r\n }\r\n\r\n supports(eventName: string): boolean {\r\n return eventName.includes('.') &&\r\n eventName.split('.').some(part => part in EventModifiersPlugin.modifierMap);\r\n }\r\n\r\n addEventListener(element: HTMLElement, eventName: string, handler: (event: Event) => void): () => void {\r\n const parts = eventName.split('.');\r\n const domEventName = parts[0];\r\n const modifiers = parts.slice(1).filter(m => m in EventModifiersPlugin.modifierMap);\r\n\r\n const wrappedHandler = (event: Event) => {\r\n modifiers.forEach(mod => EventModifiersPlugin.modifierMap[mod](event));\r\n\r\n return handler(event);\r\n };\r\n\r\n element.addEventListener(domEventName, wrappedHandler);\r\n\r\n return () => element.removeEventListener(domEventName, wrappedHandler);\r\n }\r\n}\r\n\r\n/**\r\n * Provides the Event Modifiers Plugin.\r\n *\r\n * @example\r\n * ```typescript\r\n * bootstrapApplication(AppComponent, {\r\n * providers: [provideEventModifiersPlugin()]\r\n * });\r\n * ```\r\n */\r\nexport function provideEventModifiersPlugin(): Provider {\r\n return {\r\n provide: EVENT_MANAGER_PLUGINS,\r\n useClass: EventModifiersPlugin,\r\n multi: true\r\n };\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCG;AACG,SAAU,uBAAuB,CACrC,KAAwB,EACxB,SAAY,EAAA;IAEZ,OAAO;AACL,QAAA,OAAO,EAAE,KAAK;AACd,QAAA,WAAW,EAAE,UAAU,CAAC,MAAM,SAAS;KACxC;AACH;;ACnDA;;;;;;;;;AASG;AAMH;;;AAGG;AAEG,MAAO,oBAAqB,SAAQ,kBAAkB,CAAA;AAC1D;;AAEG;IACK,OAAO,WAAW,GAA0C;QAClE,IAAI,EAAE,CAAC,CAAQ,KAAK,CAAC,CAAC,cAAc,EAAE;QACtC,IAAI,EAAE,CAAC,CAAQ,KAAK,CAAC,CAAC,eAAe,EAAE;KACxC;AAED,IAAA,WAAA,CAA8B,GAAa,EAAA;QACzC,KAAK,CAAC,GAAG,CAAC;IACZ;AAEA,IAAA,QAAQ,CAAC,SAAiB,EAAA;AACxB,QAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,oBAAoB,CAAC,WAAW,CAAC;IAC/E;AAEA,IAAA,gBAAgB,CAAC,OAAoB,EAAE,SAAiB,EAAE,OAA+B,EAAA;QACvF,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;AAClC,QAAA,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC;QAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,WAAW,CAAC;AAEnF,QAAA,MAAM,cAAc,GAAG,CAAC,KAAY,KAAI;AACtC,YAAA,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,oBAAoB,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAEtE,YAAA,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,QAAA,CAAC;AAED,QAAA,OAAO,CAAC,gBAAgB,CAAC,YAAY,EAAE,cAAc,CAAC;QAEtD,OAAO,MAAM,OAAO,CAAC,mBAAmB,CAAC,YAAY,EAAE,cAAc,CAAC;IACxE;AAhCW,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,oBAAoB,kBASX,QAAQ,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GATjB,oBAAoB,EAAA,CAAA;;2FAApB,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBADhC;;0BAUc,MAAM;2BAAC,QAAQ;;AA0B9B;;;;;;;;;AASG;SACa,2BAA2B,GAAA;IACzC,OAAO;AACL,QAAA,OAAO,EAAE,qBAAqB;AAC9B,QAAA,QAAQ,EAAE,oBAAoB;AAC9B,QAAA,KAAK,EAAE;KACR;AACH;;ACvEA;;AAEG;;;;"}
1
+ {"version":3,"file":"telperion-ng-pack-utils.mjs","sources":["../../utils/src/provide-service-directive.ts","../../utils/src/event-modifiers.plugin.ts","../../utils/src/construct-fetcher.ts","../../utils/src/telperion-ng-pack-utils.ts"],"sourcesContent":["import { forwardRef, InjectionToken, Provider, Type } from \"@angular/core\";\r\n\r\n/**\r\n * Utility function to create a provider for a directive/component that can be injected as a service.\r\n * This is useful for cases where you want to inject a directive/component instance into another directive/component.\r\n *\r\n * Example usage:\r\n *\r\n * files:\r\n * - parent.directive.ts\r\n * - child.directive.ts\r\n * - parent.service.ts\r\n *\r\n * parent.service.ts:\r\n * ```\r\n * import { InjectionToken } from \"@angular/core\";\r\n * import type { ParentDirective } from \"./parent.directive\";\r\n *\r\n * export const ParentService = new InjectionToken<ParentDirective>(\"ParentService\");\r\n * ```\r\n *\r\n * parent.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'parent',\r\n * providers: [provideServiceDirective(ParentService, ParentDirective)],\r\n * })\r\n * export class ParentDirective {...}\r\n * ```\r\n * child.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'child',\r\n * })\r\n * export class ChildDirective {\r\n * #parent = inject(ParentService);\r\n * }\r\n * ```\r\n *\r\n * @param token - The injection token to provide.\r\n * @param directive - The directive/component class that will be provided.\r\n * @returns A provider object that can be used in the providers array of an Angular module or component.\r\n */\r\nexport function provideServiceDirective<T>(\r\n token: InjectionToken<T>,\r\n directive: Type<NoInfer<T>>\r\n): Provider {\r\n return {\r\n provide: token,\r\n useExisting: forwardRef(() => directive)\r\n }\r\n}\r\n","/**\r\n * Angular Event Manager Plugin that enables event modifier syntax in templates.\r\n *\r\n * @example\r\n * ```html\r\n * <form (submit.pd)=\"onSubmit()\">...</form>\r\n * <div (click.sp)=\"handleClick()\">...</div>\r\n * <button (click.pd.sp)=\"handleButtonClick()\">Click me</button>\r\n * ```\r\n */\r\n\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { Inject, Injectable, Provider } from '@angular/core';\r\nimport { EVENT_MANAGER_PLUGINS, EventManagerPlugin } from '@angular/platform-browser';\r\n\r\n/**\r\n * Event Manager Plugin that adds support for event modifiers.\r\n * @internal\r\n */\r\n@Injectable()\r\nexport class EventModifiersPlugin extends EventManagerPlugin {\r\n /**\r\n * Supported modifiers: `pd` (preventDefault), `sp` (stopPropagation)\r\n */\r\n private static modifierMap: { [key: string]: (e: Event) => void } = {\r\n 'pd': (e: Event) => e.preventDefault(),\r\n 'sp': (e: Event) => e.stopPropagation(),\r\n };\r\n\r\n constructor(@Inject(DOCUMENT) doc: Document) {\r\n super(doc);\r\n }\r\n\r\n supports(eventName: string): boolean {\r\n return eventName.includes('.') &&\r\n eventName.split('.').some(part => part in EventModifiersPlugin.modifierMap);\r\n }\r\n\r\n addEventListener(element: HTMLElement, eventName: string, handler: (event: Event) => void): () => void {\r\n const parts = eventName.split('.');\r\n const domEventName = parts[0];\r\n const modifiers = parts.slice(1).filter(m => m in EventModifiersPlugin.modifierMap);\r\n\r\n const wrappedHandler = (event: Event) => {\r\n modifiers.forEach(mod => EventModifiersPlugin.modifierMap[mod](event));\r\n\r\n return handler(event);\r\n };\r\n\r\n element.addEventListener(domEventName, wrappedHandler);\r\n\r\n return () => element.removeEventListener(domEventName, wrappedHandler);\r\n }\r\n}\r\n\r\n/**\r\n * Provides the Event Modifiers Plugin.\r\n *\r\n * @example\r\n * ```typescript\r\n * bootstrapApplication(AppComponent, {\r\n * providers: [provideEventModifiersPlugin()]\r\n * });\r\n * ```\r\n */\r\nexport function provideEventModifiersPlugin(): Provider {\r\n return {\r\n provide: EVENT_MANAGER_PLUGINS,\r\n useClass: EventModifiersPlugin,\r\n multi: true\r\n };\r\n}\r\n","import { HttpClient } from \"@angular/common/http\";\nimport { inject } from \"@angular/core\";\nimport { AnyFunction } from '@thalesrc/extra-ts-types';\nimport { tryCatch } from \"@thalesrc/js-utils/promise/try-catch\";\nimport { firstValueFrom, Observable } from 'rxjs';\n\nexport type Fetcher = {\n [P in keyof HttpClient]: HttpClient[P] extends (...args: infer A) => Observable<unknown> ? <T>(...args: A) => Promise<[unknown, T]> : never;\n}\n\n/**\n * Constructs a Fetcher that proxies HttpClient methods and returns a tuple of [error, response].\n * If the HttpClient method throws an error, it will be caught and returned as the first element of the tuple.\n * If the HttpClient method succeeds, the response will be returned as the second element of the tuple.\n * Example usage:\n * ```\n * @Component({\n * selector: 'app-example',\n * template: `<button (click)=\"fetchData()\">Fetch Data</button>`,\n * })\n * export class ExampleComponent {\n * private fetcher = constructFetcher();\n *\n * async fetchData() {\n * const [error, response] = await this.fetcher.get('/api/data');\n *\n* if (error) {\n* console.error('Error fetching data:', error);\n* } else {\n* console.log('Fetched data:', response);\n* }\n * }\n * }\n * ```\n */\nexport function constructFetcher() {\n const client = inject(HttpClient);\n\n return new Proxy({} as Fetcher, {\n get(target, prop) {\n if (prop in client) {\n return (...args: any[]) => {\n const method = (client as any)[prop] as AnyFunction;\n\n return tryCatch(firstValueFrom(method.apply(client, args)));\n }\n }\n\n throw new Error(`HttpClient does not have method ${String(prop)}`);\n }\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telperion/ng-pack",
3
- "version": "1.1.1",
3
+ "version": "1.2.2",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0",
@@ -8,6 +8,8 @@
8
8
  "rxjs": "^7.8.1"
9
9
  },
10
10
  "dependencies": {
11
+ "@thalesrc/extra-ts-types": "^1.4.0",
12
+ "@thalesrc/js-utils": "^3.4.0",
11
13
  "@thalesrc/reactive-storage": "^1.0.5",
12
14
  "tslib": "^2.3.0"
13
15
  },
@@ -1,4 +1,6 @@
1
- import { Type, InjectionToken, Provider } from '@angular/core';
1
+ import { InjectionToken, Type, Provider } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { Observable } from 'rxjs';
2
4
 
3
5
  /**
4
6
  * Utility function to create a provider for a directive/component that can be injected as a service.
@@ -41,7 +43,7 @@ import { Type, InjectionToken, Provider } from '@angular/core';
41
43
  * @param directive - The directive/component class that will be provided.
42
44
  * @returns A provider object that can be used in the providers array of an Angular module or component.
43
45
  */
44
- declare function provideServiceDirective<T extends Type<unknown>>(token: InjectionToken<T>, directive: T): Provider;
46
+ declare function provideServiceDirective<T>(token: InjectionToken<T>, directive: Type<NoInfer<T>>): Provider;
45
47
 
46
48
  /**
47
49
  * Provides the Event Modifiers Plugin.
@@ -55,4 +57,34 @@ declare function provideServiceDirective<T extends Type<unknown>>(token: Injecti
55
57
  */
56
58
  declare function provideEventModifiersPlugin(): Provider;
57
59
 
58
- export { provideEventModifiersPlugin, provideServiceDirective };
60
+ type Fetcher = {
61
+ [P in keyof HttpClient]: HttpClient[P] extends (...args: infer A) => Observable<unknown> ? <T>(...args: A) => Promise<[unknown, T]> : never;
62
+ };
63
+ /**
64
+ * Constructs a Fetcher that proxies HttpClient methods and returns a tuple of [error, response].
65
+ * If the HttpClient method throws an error, it will be caught and returned as the first element of the tuple.
66
+ * If the HttpClient method succeeds, the response will be returned as the second element of the tuple.
67
+ * Example usage:
68
+ * ```
69
+ * @Component({
70
+ * selector: 'app-example',
71
+ * template: `<button (click)="fetchData()">Fetch Data</button>`,
72
+ * })
73
+ * export class ExampleComponent {
74
+ * private fetcher = constructFetcher();
75
+ *
76
+ * async fetchData() {
77
+ * const [error, response] = await this.fetcher.get('/api/data');
78
+ *
79
+ * if (error) {
80
+ * console.error('Error fetching data:', error);
81
+ * } else {
82
+ * console.log('Fetched data:', response);
83
+ * }
84
+ * }
85
+ * }
86
+ * ```
87
+ */
88
+ declare function constructFetcher(): Fetcher;
89
+
90
+ export { constructFetcher, provideEventModifiersPlugin, provideServiceDirective };
package/utils/README.md CHANGED
@@ -123,6 +123,125 @@ export class ChildDirective {
123
123
  </div>
124
124
  ```
125
125
 
126
+ ### Construct Fetcher
127
+
128
+ A utility function that creates a proxy wrapper around Angular's `HttpClient` to convert Observable-based HTTP methods into Promise-based methods with automatic error handling. Returns a tuple of `[error, response]` instead of throwing, following the Go-style error handling pattern.
129
+
130
+ #### Features
131
+
132
+ - 🎯 Converts all HttpClient methods to Promise-based API
133
+ - ✅ Returns `[error, response]` tuple for predictable error handling
134
+ - 🔄 Automatically uses `firstValueFrom` to convert Observables
135
+ - 🛡️ No try-catch blocks needed in your code
136
+ - 💉 Uses Angular's dependency injection
137
+
138
+ #### Usage
139
+
140
+ **Basic example:**
141
+
142
+ ```typescript
143
+ import { Component } from '@angular/core';
144
+ import { constructFetcher } from '@telperion/ng-pack/utils';
145
+
146
+ @Component({
147
+ selector: 'app-example',
148
+ template: `
149
+ <button (click)="fetchData()">Fetch Data</button>
150
+ <div *ngIf="loading">Loading...</div>
151
+ <div *ngIf="error">Error: {{ error }}</div>
152
+ <div *ngIf="data">{{ data | json }}</div>
153
+ `
154
+ })
155
+ export class ExampleComponent {
156
+ private fetcher = constructFetcher();
157
+
158
+ loading = false;
159
+ error: any = null;
160
+ data: any = null;
161
+
162
+ async fetchData() {
163
+ this.loading = true;
164
+ const [error, response] = await this.fetcher.get<{ items: string[] }>('/api/data');
165
+ this.loading = false;
166
+
167
+ if (error) {
168
+ this.error = error;
169
+ console.error('Error fetching data:', error);
170
+ return;
171
+ }
172
+
173
+ this.data = response;
174
+ console.log('Fetched data:', response);
175
+ }
176
+ }
177
+ ```
178
+
179
+ **POST request with body:**
180
+
181
+ ```typescript
182
+ async createUser() {
183
+ const [error, response] = await this.fetcher.post<User>(
184
+ '/api/users',
185
+ { name: 'John', email: 'john@example.com' }
186
+ );
187
+
188
+ if (error) {
189
+ console.error('Failed to create user:', error);
190
+ return;
191
+ }
192
+
193
+ console.log('User created:', response);
194
+ }
195
+ ```
196
+
197
+ **With request options:**
198
+
199
+ ```typescript
200
+ async fetchWithHeaders() {
201
+ const [error, response] = await this.fetcher.get<Data>(
202
+ '/api/data',
203
+ {
204
+ headers: { 'Authorization': 'Bearer token' },
205
+ params: { page: '1', limit: '10' }
206
+ }
207
+ );
208
+
209
+ if (error) {
210
+ // Handle error
211
+ return;
212
+ }
213
+
214
+ // Use response
215
+ }
216
+ ```
217
+
218
+ #### API
219
+
220
+ The fetcher provides the same methods as `HttpClient` but with Promise-based signatures:
221
+
222
+ - `get<T>(url, options?)` → `Promise<[error, T]>`
223
+ - `post<T>(url, body, options?)` → `Promise<[error, T]>`
224
+ - `put<T>(url, body, options?)` → `Promise<[error, T]>`
225
+ - `patch<T>(url, body, options?)` → `Promise<[error, T]>`
226
+ - `delete<T>(url, options?)` → `Promise<[error, T]>`
227
+ - `head<T>(url, options?)` → `Promise<[error, T]>`
228
+ - `options<T>(url, options?)` → `Promise<[error, T]>`
229
+ - `request<T>(method, url, options?)` → `Promise<[error, T]>`
230
+
231
+ #### Return Value
232
+
233
+ All methods return a Promise that resolves to a tuple:
234
+ - **Success**: `[null, response]` - Error is null, response contains the data
235
+ - **Failure**: `[error, null]` - Error contains the error object, response is null
236
+
237
+ #### Benefits
238
+
239
+ 1. **No try-catch needed**: Error handling is explicit and predictable
240
+ 2. **Type-safe**: Full TypeScript support with generic types
241
+ 3. **Consistent pattern**: Same error handling pattern across all HTTP methods
242
+ 4. **DI integration**: Works seamlessly with Angular's dependency injection
243
+ 5. **Clean code**: Reduces boilerplate and improves readability
244
+
126
245
  ## License
127
246
 
128
247
  MIT