@alicanyucelankara06/generic-service 1.0.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.
@@ -0,0 +1,336 @@
1
+ import { HttpClient, HttpParams } from '@angular/common/http';
2
+ import { InjectionToken, inject, signal, computed } from '@angular/core';
3
+ import { tap, catchError, finalize, throwError } from 'rxjs';
4
+
5
+ /**
6
+ * Konfigürasyon için InjectionToken
7
+ */
8
+ const GENERIC_SERVICE_CONFIG = new InjectionToken('GENERIC_SERVICE_CONFIG');
9
+ /**
10
+ * Konfigürasyonu provide etmek için yardımcı fonksiyon
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // app.config.ts
15
+ * export const appConfig: ApplicationConfig = {
16
+ * providers: [
17
+ * provideGenericServiceConfig({
18
+ * baseUrl: 'https://api.example.com',
19
+ * defaultPageSize: 20,
20
+ * enableLogging: true,
21
+ * }),
22
+ * ],
23
+ * };
24
+ * ```
25
+ */
26
+ function provideGenericServiceConfig(config) {
27
+ return {
28
+ provide: GENERIC_SERVICE_CONFIG,
29
+ useValue: {
30
+ defaultPageSize: 10,
31
+ enableLogging: false,
32
+ ...config,
33
+ },
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Angular 21 Generic CRUD Service
39
+ *
40
+ * Tüm entity servisleri için temel sınıf.
41
+ * HTTP CRUD operasyonları, pagination, filtreleme, hata yönetimi ve
42
+ * reactive state (signals) desteği sağlar.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // user.service.ts
47
+ * @Injectable({ providedIn: 'root' })
48
+ * export class UserService extends GenericService<User> {
49
+ * constructor() {
50
+ * super('users'); // endpoint: /api/users
51
+ * }
52
+ * }
53
+ *
54
+ * // component içinde kullanım
55
+ * export class UserListComponent {
56
+ * private userService = inject(UserService);
57
+ *
58
+ * users = this.userService.items;
59
+ * loading = this.userService.loading;
60
+ * error = this.userService.error;
61
+ * totalCount = this.userService.totalCount;
62
+ * }
63
+ * ```
64
+ */
65
+ class GenericService {
66
+ endpoint;
67
+ // Angular 21 inject() ile DI
68
+ http = inject(HttpClient);
69
+ config = inject(GENERIC_SERVICE_CONFIG);
70
+ // Reactive state — Angular Signals
71
+ _items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
72
+ _selectedItem = signal(null, ...(ngDevMode ? [{ debugName: "_selectedItem" }] : /* istanbul ignore next */ []));
73
+ _loading = signal(false, ...(ngDevMode ? [{ debugName: "_loading" }] : /* istanbul ignore next */ []));
74
+ _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
75
+ _totalCount = signal(0, ...(ngDevMode ? [{ debugName: "_totalCount" }] : /* istanbul ignore next */ []));
76
+ _currentPage = signal(1, ...(ngDevMode ? [{ debugName: "_currentPage" }] : /* istanbul ignore next */ []));
77
+ _pageSize;
78
+ /** Mevcut entity listesi (readonly signal) */
79
+ items = this._items.asReadonly();
80
+ /** Seçili entity (readonly signal) */
81
+ selectedItem = this._selectedItem.asReadonly();
82
+ /** Yüklenme durumu */
83
+ loading = this._loading.asReadonly();
84
+ /** Son hata */
85
+ error = this._error.asReadonly();
86
+ /** Toplam kayıt sayısı */
87
+ totalCount = this._totalCount.asReadonly();
88
+ /** Mevcut sayfa */
89
+ currentPage = this._currentPage.asReadonly();
90
+ /** Sayfa boyutu */
91
+ pageSize;
92
+ /** Toplam sayfa sayısı (computed) */
93
+ totalPages = computed(() => Math.ceil(this._totalCount() / this._pageSize()), ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
94
+ /** Veri var mı? (computed) */
95
+ hasData = computed(() => this._items().length > 0, ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
96
+ /** Tam API URL */
97
+ apiUrl;
98
+ /**
99
+ * @param endpoint - API endpoint yolu (örn: 'users', 'products')
100
+ */
101
+ constructor(endpoint) {
102
+ this.endpoint = endpoint;
103
+ this._pageSize = signal(this.config.defaultPageSize ?? 10, ...(ngDevMode ? [{ debugName: "_pageSize" }] : /* istanbul ignore next */ []));
104
+ this.pageSize = computed(() => this._pageSize(), ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
105
+ this.apiUrl = `${this.config.baseUrl}/${endpoint}`;
106
+ }
107
+ // ─── CRUD Operations ──────────────────────────────────────
108
+ /**
109
+ * Tüm kayıtları getirir
110
+ */
111
+ getAll(params) {
112
+ this._loading.set(true);
113
+ this._error.set(null);
114
+ const httpParams = this.buildHttpParams(params);
115
+ return this.http.get(this.apiUrl, { params: httpParams }).pipe(tap((data) => {
116
+ this._items.set(data);
117
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
118
+ }
119
+ /**
120
+ * Sayfalı kayıtları getirir
121
+ */
122
+ getPaged(params) {
123
+ this._loading.set(true);
124
+ this._error.set(null);
125
+ const queryParams = {
126
+ page: this._currentPage(),
127
+ pageSize: this._pageSize(),
128
+ ...params,
129
+ };
130
+ const httpParams = this.buildHttpParams(queryParams);
131
+ return this.http
132
+ .get(this.apiUrl, { params: httpParams })
133
+ .pipe(tap((response) => {
134
+ this._items.set(response.data);
135
+ this._totalCount.set(response.totalCount);
136
+ this._currentPage.set(response.page);
137
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
138
+ }
139
+ /**
140
+ * ID ile tek kayıt getirir
141
+ */
142
+ getById(id) {
143
+ this._loading.set(true);
144
+ this._error.set(null);
145
+ return this.http.get(`${this.apiUrl}/${id}`).pipe(tap((item) => {
146
+ this._selectedItem.set(item);
147
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
148
+ }
149
+ /**
150
+ * Yeni kayıt oluşturur
151
+ */
152
+ create(item) {
153
+ this._loading.set(true);
154
+ this._error.set(null);
155
+ return this.http.post(this.apiUrl, item).pipe(tap((created) => {
156
+ this._items.update((items) => [...items, created]);
157
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
158
+ }
159
+ /**
160
+ * Mevcut kaydı günceller
161
+ */
162
+ update(id, item) {
163
+ this._loading.set(true);
164
+ this._error.set(null);
165
+ return this.http.put(`${this.apiUrl}/${id}`, item).pipe(tap((updated) => {
166
+ this._items.update((items) => items.map((i) => (i.id === id ? updated : i)));
167
+ if (this._selectedItem()?.id === id) {
168
+ this._selectedItem.set(updated);
169
+ }
170
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
171
+ }
172
+ /**
173
+ * Kaydı kısmen günceller (PATCH)
174
+ */
175
+ patch(id, item) {
176
+ this._loading.set(true);
177
+ this._error.set(null);
178
+ return this.http.patch(`${this.apiUrl}/${id}`, item).pipe(tap((updated) => {
179
+ this._items.update((items) => items.map((i) => (i.id === id ? updated : i)));
180
+ if (this._selectedItem()?.id === id) {
181
+ this._selectedItem.set(updated);
182
+ }
183
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
184
+ }
185
+ /**
186
+ * Kaydı siler
187
+ */
188
+ delete(id) {
189
+ this._loading.set(true);
190
+ this._error.set(null);
191
+ return this.http.delete(`${this.apiUrl}/${id}`).pipe(tap(() => {
192
+ this._items.update((items) => items.filter((i) => i.id !== id));
193
+ if (this._selectedItem()?.id === id) {
194
+ this._selectedItem.set(null);
195
+ }
196
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
197
+ }
198
+ // ─── Bulk Operations ──────────────────────────────────────
199
+ /**
200
+ * Toplu silme
201
+ */
202
+ deleteMany(ids) {
203
+ this._loading.set(true);
204
+ this._error.set(null);
205
+ return this.http
206
+ .request('DELETE', this.apiUrl, { body: { ids } })
207
+ .pipe(tap(() => {
208
+ this._items.update((items) => items.filter((i) => !ids.includes(i.id)));
209
+ if (this._selectedItem() && ids.includes(this._selectedItem().id)) {
210
+ this._selectedItem.set(null);
211
+ }
212
+ }), catchError((err) => this.handleError(err)), finalize(() => this._loading.set(false)));
213
+ }
214
+ // ─── Search & Filter ──────────────────────────────────────
215
+ /**
216
+ * Arama yapar
217
+ */
218
+ search(term, params) {
219
+ return this.getAll({ ...params, search: term });
220
+ }
221
+ // ─── Pagination Controls ──────────────────────────────────
222
+ /**
223
+ * Belirtilen sayfaya gider
224
+ */
225
+ goToPage(page) {
226
+ this._currentPage.set(page);
227
+ return this.getPaged();
228
+ }
229
+ /**
230
+ * Sonraki sayfa
231
+ */
232
+ nextPage() {
233
+ if (this._currentPage() < this.totalPages()) {
234
+ this._currentPage.update((p) => p + 1);
235
+ }
236
+ return this.getPaged();
237
+ }
238
+ /**
239
+ * Önceki sayfa
240
+ */
241
+ previousPage() {
242
+ if (this._currentPage() > 1) {
243
+ this._currentPage.update((p) => p - 1);
244
+ }
245
+ return this.getPaged();
246
+ }
247
+ /**
248
+ * Sayfa boyutunu değiştirir
249
+ */
250
+ setPageSize(size) {
251
+ this._pageSize.set(size);
252
+ this._currentPage.set(1);
253
+ }
254
+ // ─── State Management ─────────────────────────────────────
255
+ /**
256
+ * Seçili entity'yi ayarlar
257
+ */
258
+ select(item) {
259
+ this._selectedItem.set(item);
260
+ }
261
+ /**
262
+ * State'i sıfırlar
263
+ */
264
+ clearState() {
265
+ this._items.set([]);
266
+ this._selectedItem.set(null);
267
+ this._error.set(null);
268
+ this._totalCount.set(0);
269
+ this._currentPage.set(1);
270
+ }
271
+ /**
272
+ * Hatayı temizler
273
+ */
274
+ clearError() {
275
+ this._error.set(null);
276
+ }
277
+ // ─── Protected Helpers ────────────────────────────────────
278
+ /**
279
+ * QueryParams'ı HttpParams'a dönüştürür.
280
+ * Alt sınıflarda override edilebilir.
281
+ */
282
+ buildHttpParams(params) {
283
+ let httpParams = new HttpParams();
284
+ if (!params)
285
+ return httpParams;
286
+ if (params.page != null) {
287
+ httpParams = httpParams.set('page', params.page.toString());
288
+ }
289
+ if (params.pageSize != null) {
290
+ httpParams = httpParams.set('pageSize', params.pageSize.toString());
291
+ }
292
+ if (params.sort) {
293
+ httpParams = httpParams.set('sort', params.sort);
294
+ }
295
+ if (params.sortDirection) {
296
+ httpParams = httpParams.set('sortDirection', params.sortDirection);
297
+ }
298
+ if (params.search) {
299
+ httpParams = httpParams.set('search', params.search);
300
+ }
301
+ if (params.filters) {
302
+ for (const [key, value] of Object.entries(params.filters)) {
303
+ httpParams = httpParams.set(key, String(value));
304
+ }
305
+ }
306
+ return httpParams;
307
+ }
308
+ /**
309
+ * HTTP hatalarını yönetir.
310
+ * Alt sınıflarda override edilebilir.
311
+ */
312
+ handleError(error) {
313
+ const apiError = {
314
+ status: error.status,
315
+ message: error.error?.message ?? error.message ?? 'Bilinmeyen hata',
316
+ errors: error.error?.errors,
317
+ timestamp: new Date().toISOString(),
318
+ };
319
+ this._error.set(apiError);
320
+ if (this.config.enableLogging) {
321
+ console.error(`[GenericService] ${this.endpoint}:`, apiError);
322
+ }
323
+ return throwError(() => apiError);
324
+ }
325
+ }
326
+
327
+ /*
328
+ * Public API Surface
329
+ */
330
+
331
+ /**
332
+ * Generated bundle index. Do not edit.
333
+ */
334
+
335
+ export { GENERIC_SERVICE_CONFIG, GenericService, provideGenericServiceConfig };
336
+ //# sourceMappingURL=acy-generic-service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acy-generic-service.mjs","sources":["../../src/lib/generic-service.config.ts","../../src/lib/generic.service.ts","../../src/public-api.ts","../../src/acy-generic-service.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\n\r\n/**\r\n * Generic service konfigürasyonu\r\n */\r\nexport interface GenericServiceConfig {\r\n /** API base URL (örn: 'https://api.example.com') */\r\n baseUrl: string;\r\n /** Varsayılan sayfa boyutu */\r\n defaultPageSize?: number;\r\n /** İsteklere eklenecek varsayılan header'lar */\r\n defaultHeaders?: Record<string, string>;\r\n /** Hata mesajlarını loglama */\r\n enableLogging?: boolean;\r\n}\r\n\r\n/**\r\n * Konfigürasyon için InjectionToken\r\n */\r\nexport const GENERIC_SERVICE_CONFIG = new InjectionToken<GenericServiceConfig>(\r\n 'GENERIC_SERVICE_CONFIG'\r\n);\r\n\r\n/**\r\n * Konfigürasyonu provide etmek için yardımcı fonksiyon\r\n *\r\n * @example\r\n * ```ts\r\n * // app.config.ts\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [\r\n * provideGenericServiceConfig({\r\n * baseUrl: 'https://api.example.com',\r\n * defaultPageSize: 20,\r\n * enableLogging: true,\r\n * }),\r\n * ],\r\n * };\r\n * ```\r\n */\r\nexport function provideGenericServiceConfig(config: GenericServiceConfig) {\r\n return {\r\n provide: GENERIC_SERVICE_CONFIG,\r\n useValue: {\r\n defaultPageSize: 10,\r\n enableLogging: false,\r\n ...config,\r\n } satisfies GenericServiceConfig,\r\n };\r\n}\r\n","import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\r\nimport { inject, signal, computed } from '@angular/core';\r\nimport { Observable, throwError, catchError, tap, map, finalize } from 'rxjs';\r\n\r\nimport { GENERIC_SERVICE_CONFIG, GenericServiceConfig } from './generic-service.config';\r\nimport {\r\n BaseEntity,\r\n PagedResponse,\r\n QueryParams,\r\n ApiResponse,\r\n ApiError,\r\n} from './models';\r\n\r\n/**\r\n * Angular 21 Generic CRUD Service\r\n *\r\n * Tüm entity servisleri için temel sınıf.\r\n * HTTP CRUD operasyonları, pagination, filtreleme, hata yönetimi ve\r\n * reactive state (signals) desteği sağlar.\r\n *\r\n * @example\r\n * ```ts\r\n * // user.service.ts\r\n * @Injectable({ providedIn: 'root' })\r\n * export class UserService extends GenericService<User> {\r\n * constructor() {\r\n * super('users'); // endpoint: /api/users\r\n * }\r\n * }\r\n *\r\n * // component içinde kullanım\r\n * export class UserListComponent {\r\n * private userService = inject(UserService);\r\n *\r\n * users = this.userService.items;\r\n * loading = this.userService.loading;\r\n * error = this.userService.error;\r\n * totalCount = this.userService.totalCount;\r\n * }\r\n * ```\r\n */\r\nexport abstract class GenericService<T extends BaseEntity> {\r\n // Angular 21 inject() ile DI\r\n protected readonly http = inject(HttpClient);\r\n protected readonly config = inject<GenericServiceConfig>(GENERIC_SERVICE_CONFIG);\r\n\r\n // Reactive state — Angular Signals\r\n private readonly _items = signal<T[]>([]);\r\n private readonly _selectedItem = signal<T | null>(null);\r\n private readonly _loading = signal(false);\r\n private readonly _error = signal<ApiError | null>(null);\r\n private readonly _totalCount = signal(0);\r\n private readonly _currentPage = signal(1);\r\n private readonly _pageSize: ReturnType<typeof signal<number>>;\r\n\r\n /** Mevcut entity listesi (readonly signal) */\r\n readonly items = this._items.asReadonly();\r\n\r\n /** Seçili entity (readonly signal) */\r\n readonly selectedItem = this._selectedItem.asReadonly();\r\n\r\n /** Yüklenme durumu */\r\n readonly loading = this._loading.asReadonly();\r\n\r\n /** Son hata */\r\n readonly error = this._error.asReadonly();\r\n\r\n /** Toplam kayıt sayısı */\r\n readonly totalCount = this._totalCount.asReadonly();\r\n\r\n /** Mevcut sayfa */\r\n readonly currentPage = this._currentPage.asReadonly();\r\n\r\n /** Sayfa boyutu */\r\n readonly pageSize: ReturnType<typeof computed<number>>;\r\n\r\n /** Toplam sayfa sayısı (computed) */\r\n readonly totalPages = computed(() =>\r\n Math.ceil(this._totalCount() / this._pageSize())\r\n );\r\n\r\n /** Veri var mı? (computed) */\r\n readonly hasData = computed(() => this._items().length > 0);\r\n\r\n /** Tam API URL */\r\n protected readonly apiUrl: string;\r\n\r\n /**\r\n * @param endpoint - API endpoint yolu (örn: 'users', 'products')\r\n */\r\n constructor(protected readonly endpoint: string) {\r\n this._pageSize = signal(this.config.defaultPageSize ?? 10);\r\n this.pageSize = computed(() => this._pageSize());\r\n this.apiUrl = `${this.config.baseUrl}/${endpoint}`;\r\n }\r\n\r\n // ─── CRUD Operations ──────────────────────────────────────\r\n\r\n /**\r\n * Tüm kayıtları getirir\r\n */\r\n getAll(params?: QueryParams): Observable<T[]> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n const httpParams = this.buildHttpParams(params);\r\n\r\n return this.http.get<T[]>(this.apiUrl, { params: httpParams }).pipe(\r\n tap((data) => {\r\n this._items.set(data);\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * Sayfalı kayıtları getirir\r\n */\r\n getPaged(params?: QueryParams): Observable<PagedResponse<T>> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n const queryParams: QueryParams = {\r\n page: this._currentPage(),\r\n pageSize: this._pageSize(),\r\n ...params,\r\n };\r\n\r\n const httpParams = this.buildHttpParams(queryParams);\r\n\r\n return this.http\r\n .get<PagedResponse<T>>(this.apiUrl, { params: httpParams })\r\n .pipe(\r\n tap((response) => {\r\n this._items.set(response.data);\r\n this._totalCount.set(response.totalCount);\r\n this._currentPage.set(response.page);\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * ID ile tek kayıt getirir\r\n */\r\n getById(id: string | number): Observable<T> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http.get<T>(`${this.apiUrl}/${id}`).pipe(\r\n tap((item) => {\r\n this._selectedItem.set(item);\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * Yeni kayıt oluşturur\r\n */\r\n create(item: Omit<T, 'id'>): Observable<T> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http.post<T>(this.apiUrl, item).pipe(\r\n tap((created) => {\r\n this._items.update((items) => [...items, created]);\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * Mevcut kaydı günceller\r\n */\r\n update(id: string | number, item: Partial<T>): Observable<T> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http.put<T>(`${this.apiUrl}/${id}`, item).pipe(\r\n tap((updated) => {\r\n this._items.update((items) =>\r\n items.map((i) => (i.id === id ? updated : i))\r\n );\r\n if (this._selectedItem()?.id === id) {\r\n this._selectedItem.set(updated);\r\n }\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * Kaydı kısmen günceller (PATCH)\r\n */\r\n patch(id: string | number, item: Partial<T>): Observable<T> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http.patch<T>(`${this.apiUrl}/${id}`, item).pipe(\r\n tap((updated) => {\r\n this._items.update((items) =>\r\n items.map((i) => (i.id === id ? updated : i))\r\n );\r\n if (this._selectedItem()?.id === id) {\r\n this._selectedItem.set(updated);\r\n }\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n /**\r\n * Kaydı siler\r\n */\r\n delete(id: string | number): Observable<void> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(\r\n tap(() => {\r\n this._items.update((items) => items.filter((i) => i.id !== id));\r\n if (this._selectedItem()?.id === id) {\r\n this._selectedItem.set(null);\r\n }\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n // ─── Bulk Operations ──────────────────────────────────────\r\n\r\n /**\r\n * Toplu silme\r\n */\r\n deleteMany(ids: (string | number)[]): Observable<void> {\r\n this._loading.set(true);\r\n this._error.set(null);\r\n\r\n return this.http\r\n .request<void>('DELETE', this.apiUrl, { body: { ids } })\r\n .pipe(\r\n tap(() => {\r\n this._items.update((items) =>\r\n items.filter((i) => !ids.includes(i.id))\r\n );\r\n if (this._selectedItem() && ids.includes(this._selectedItem()!.id)) {\r\n this._selectedItem.set(null);\r\n }\r\n }),\r\n catchError((err) => this.handleError(err)),\r\n finalize(() => this._loading.set(false))\r\n );\r\n }\r\n\r\n // ─── Search & Filter ──────────────────────────────────────\r\n\r\n /**\r\n * Arama yapar\r\n */\r\n search(term: string, params?: QueryParams): Observable<T[]> {\r\n return this.getAll({ ...params, search: term });\r\n }\r\n\r\n // ─── Pagination Controls ──────────────────────────────────\r\n\r\n /**\r\n * Belirtilen sayfaya gider\r\n */\r\n goToPage(page: number): Observable<PagedResponse<T>> {\r\n this._currentPage.set(page);\r\n return this.getPaged();\r\n }\r\n\r\n /**\r\n * Sonraki sayfa\r\n */\r\n nextPage(): Observable<PagedResponse<T>> {\r\n if (this._currentPage() < this.totalPages()) {\r\n this._currentPage.update((p) => p + 1);\r\n }\r\n return this.getPaged();\r\n }\r\n\r\n /**\r\n * Önceki sayfa\r\n */\r\n previousPage(): Observable<PagedResponse<T>> {\r\n if (this._currentPage() > 1) {\r\n this._currentPage.update((p) => p - 1);\r\n }\r\n return this.getPaged();\r\n }\r\n\r\n /**\r\n * Sayfa boyutunu değiştirir\r\n */\r\n setPageSize(size: number): void {\r\n this._pageSize.set(size);\r\n this._currentPage.set(1);\r\n }\r\n\r\n // ─── State Management ─────────────────────────────────────\r\n\r\n /**\r\n * Seçili entity'yi ayarlar\r\n */\r\n select(item: T | null): void {\r\n this._selectedItem.set(item);\r\n }\r\n\r\n /**\r\n * State'i sıfırlar\r\n */\r\n clearState(): void {\r\n this._items.set([]);\r\n this._selectedItem.set(null);\r\n this._error.set(null);\r\n this._totalCount.set(0);\r\n this._currentPage.set(1);\r\n }\r\n\r\n /**\r\n * Hatayı temizler\r\n */\r\n clearError(): void {\r\n this._error.set(null);\r\n }\r\n\r\n // ─── Protected Helpers ────────────────────────────────────\r\n\r\n /**\r\n * QueryParams'ı HttpParams'a dönüştürür.\r\n * Alt sınıflarda override edilebilir.\r\n */\r\n protected buildHttpParams(params?: QueryParams): HttpParams {\r\n let httpParams = new HttpParams();\r\n\r\n if (!params) return httpParams;\r\n\r\n if (params.page != null) {\r\n httpParams = httpParams.set('page', params.page.toString());\r\n }\r\n if (params.pageSize != null) {\r\n httpParams = httpParams.set('pageSize', params.pageSize.toString());\r\n }\r\n if (params.sort) {\r\n httpParams = httpParams.set('sort', params.sort);\r\n }\r\n if (params.sortDirection) {\r\n httpParams = httpParams.set('sortDirection', params.sortDirection);\r\n }\r\n if (params.search) {\r\n httpParams = httpParams.set('search', params.search);\r\n }\r\n if (params.filters) {\r\n for (const [key, value] of Object.entries(params.filters)) {\r\n httpParams = httpParams.set(key, String(value));\r\n }\r\n }\r\n\r\n return httpParams;\r\n }\r\n\r\n /**\r\n * HTTP hatalarını yönetir.\r\n * Alt sınıflarda override edilebilir.\r\n */\r\n protected handleError(error: HttpErrorResponse): Observable<never> {\r\n const apiError: ApiError = {\r\n status: error.status,\r\n message: error.error?.message ?? error.message ?? 'Bilinmeyen hata',\r\n errors: error.error?.errors,\r\n timestamp: new Date().toISOString(),\r\n };\r\n\r\n this._error.set(apiError);\r\n\r\n if (this.config.enableLogging) {\r\n console.error(`[GenericService] ${this.endpoint}:`, apiError);\r\n }\r\n\r\n return throwError(() => apiError);\r\n }\r\n}\r\n","/*\r\n * Public API Surface\r\n */\r\nexport { GenericService } from './lib/generic.service';\r\nexport {\r\n BaseEntity,\r\n PagedResponse,\r\n QueryParams,\r\n ApiResponse,\r\n ApiError,\r\n} from './lib/models';\r\nexport {\r\n GenericServiceConfig,\r\n GENERIC_SERVICE_CONFIG,\r\n provideGenericServiceConfig,\r\n} from './lib/generic-service.config';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,238 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { InjectionToken, computed } from '@angular/core';
3
+ import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
4
+ import { Observable } from 'rxjs';
5
+
6
+ /**
7
+ * Generic service konfigürasyonu
8
+ */
9
+ interface GenericServiceConfig {
10
+ /** API base URL (örn: 'https://api.example.com') */
11
+ baseUrl: string;
12
+ /** Varsayılan sayfa boyutu */
13
+ defaultPageSize?: number;
14
+ /** İsteklere eklenecek varsayılan header'lar */
15
+ defaultHeaders?: Record<string, string>;
16
+ /** Hata mesajlarını loglama */
17
+ enableLogging?: boolean;
18
+ }
19
+ /**
20
+ * Konfigürasyon için InjectionToken
21
+ */
22
+ declare const GENERIC_SERVICE_CONFIG: InjectionToken<GenericServiceConfig>;
23
+ /**
24
+ * Konfigürasyonu provide etmek için yardımcı fonksiyon
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * // app.config.ts
29
+ * export const appConfig: ApplicationConfig = {
30
+ * providers: [
31
+ * provideGenericServiceConfig({
32
+ * baseUrl: 'https://api.example.com',
33
+ * defaultPageSize: 20,
34
+ * enableLogging: true,
35
+ * }),
36
+ * ],
37
+ * };
38
+ * ```
39
+ */
40
+ declare function provideGenericServiceConfig(config: GenericServiceConfig): {
41
+ provide: InjectionToken<GenericServiceConfig>;
42
+ useValue: {
43
+ /** API base URL (örn: 'https://api.example.com') */
44
+ baseUrl: string;
45
+ defaultPageSize: number;
46
+ /** İsteklere eklenecek varsayılan header'lar */
47
+ defaultHeaders?: Record<string, string>;
48
+ enableLogging: boolean;
49
+ };
50
+ };
51
+
52
+ /**
53
+ * Base entity interface - tüm entity'lerin bir ID'si olmalı
54
+ */
55
+ interface BaseEntity {
56
+ id: string | number;
57
+ }
58
+ /**
59
+ * Sayfalı yanıt modeli
60
+ */
61
+ interface PagedResponse<T> {
62
+ data: T[];
63
+ totalCount: number;
64
+ page: number;
65
+ pageSize: number;
66
+ totalPages: number;
67
+ }
68
+ /**
69
+ * Sorgu parametreleri
70
+ */
71
+ interface QueryParams {
72
+ page?: number;
73
+ pageSize?: number;
74
+ sort?: string;
75
+ sortDirection?: 'asc' | 'desc';
76
+ search?: string;
77
+ filters?: Record<string, string | number | boolean>;
78
+ }
79
+ /**
80
+ * API yanıt sarmalayıcı
81
+ */
82
+ interface ApiResponse<T> {
83
+ data: T;
84
+ success: boolean;
85
+ message?: string;
86
+ errors?: string[];
87
+ }
88
+ /**
89
+ * Hata detay modeli
90
+ */
91
+ interface ApiError {
92
+ status: number;
93
+ message: string;
94
+ errors?: string[];
95
+ timestamp: string;
96
+ }
97
+
98
+ /**
99
+ * Angular 21 Generic CRUD Service
100
+ *
101
+ * Tüm entity servisleri için temel sınıf.
102
+ * HTTP CRUD operasyonları, pagination, filtreleme, hata yönetimi ve
103
+ * reactive state (signals) desteği sağlar.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * // user.service.ts
108
+ * @Injectable({ providedIn: 'root' })
109
+ * export class UserService extends GenericService<User> {
110
+ * constructor() {
111
+ * super('users'); // endpoint: /api/users
112
+ * }
113
+ * }
114
+ *
115
+ * // component içinde kullanım
116
+ * export class UserListComponent {
117
+ * private userService = inject(UserService);
118
+ *
119
+ * users = this.userService.items;
120
+ * loading = this.userService.loading;
121
+ * error = this.userService.error;
122
+ * totalCount = this.userService.totalCount;
123
+ * }
124
+ * ```
125
+ */
126
+ declare abstract class GenericService<T extends BaseEntity> {
127
+ protected readonly endpoint: string;
128
+ protected readonly http: HttpClient;
129
+ protected readonly config: GenericServiceConfig;
130
+ private readonly _items;
131
+ private readonly _selectedItem;
132
+ private readonly _loading;
133
+ private readonly _error;
134
+ private readonly _totalCount;
135
+ private readonly _currentPage;
136
+ private readonly _pageSize;
137
+ /** Mevcut entity listesi (readonly signal) */
138
+ readonly items: _angular_core.Signal<T[]>;
139
+ /** Seçili entity (readonly signal) */
140
+ readonly selectedItem: _angular_core.Signal<T>;
141
+ /** Yüklenme durumu */
142
+ readonly loading: _angular_core.Signal<boolean>;
143
+ /** Son hata */
144
+ readonly error: _angular_core.Signal<ApiError>;
145
+ /** Toplam kayıt sayısı */
146
+ readonly totalCount: _angular_core.Signal<number>;
147
+ /** Mevcut sayfa */
148
+ readonly currentPage: _angular_core.Signal<number>;
149
+ /** Sayfa boyutu */
150
+ readonly pageSize: ReturnType<typeof computed<number>>;
151
+ /** Toplam sayfa sayısı (computed) */
152
+ readonly totalPages: _angular_core.Signal<number>;
153
+ /** Veri var mı? (computed) */
154
+ readonly hasData: _angular_core.Signal<boolean>;
155
+ /** Tam API URL */
156
+ protected readonly apiUrl: string;
157
+ /**
158
+ * @param endpoint - API endpoint yolu (örn: 'users', 'products')
159
+ */
160
+ constructor(endpoint: string);
161
+ /**
162
+ * Tüm kayıtları getirir
163
+ */
164
+ getAll(params?: QueryParams): Observable<T[]>;
165
+ /**
166
+ * Sayfalı kayıtları getirir
167
+ */
168
+ getPaged(params?: QueryParams): Observable<PagedResponse<T>>;
169
+ /**
170
+ * ID ile tek kayıt getirir
171
+ */
172
+ getById(id: string | number): Observable<T>;
173
+ /**
174
+ * Yeni kayıt oluşturur
175
+ */
176
+ create(item: Omit<T, 'id'>): Observable<T>;
177
+ /**
178
+ * Mevcut kaydı günceller
179
+ */
180
+ update(id: string | number, item: Partial<T>): Observable<T>;
181
+ /**
182
+ * Kaydı kısmen günceller (PATCH)
183
+ */
184
+ patch(id: string | number, item: Partial<T>): Observable<T>;
185
+ /**
186
+ * Kaydı siler
187
+ */
188
+ delete(id: string | number): Observable<void>;
189
+ /**
190
+ * Toplu silme
191
+ */
192
+ deleteMany(ids: (string | number)[]): Observable<void>;
193
+ /**
194
+ * Arama yapar
195
+ */
196
+ search(term: string, params?: QueryParams): Observable<T[]>;
197
+ /**
198
+ * Belirtilen sayfaya gider
199
+ */
200
+ goToPage(page: number): Observable<PagedResponse<T>>;
201
+ /**
202
+ * Sonraki sayfa
203
+ */
204
+ nextPage(): Observable<PagedResponse<T>>;
205
+ /**
206
+ * Önceki sayfa
207
+ */
208
+ previousPage(): Observable<PagedResponse<T>>;
209
+ /**
210
+ * Sayfa boyutunu değiştirir
211
+ */
212
+ setPageSize(size: number): void;
213
+ /**
214
+ * Seçili entity'yi ayarlar
215
+ */
216
+ select(item: T | null): void;
217
+ /**
218
+ * State'i sıfırlar
219
+ */
220
+ clearState(): void;
221
+ /**
222
+ * Hatayı temizler
223
+ */
224
+ clearError(): void;
225
+ /**
226
+ * QueryParams'ı HttpParams'a dönüştürür.
227
+ * Alt sınıflarda override edilebilir.
228
+ */
229
+ protected buildHttpParams(params?: QueryParams): HttpParams;
230
+ /**
231
+ * HTTP hatalarını yönetir.
232
+ * Alt sınıflarda override edilebilir.
233
+ */
234
+ protected handleError(error: HttpErrorResponse): Observable<never>;
235
+ }
236
+
237
+ export { GENERIC_SERVICE_CONFIG, GenericService, provideGenericServiceConfig };
238
+ export type { ApiError, ApiResponse, BaseEntity, GenericServiceConfig, PagedResponse, QueryParams };
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "./dist",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ }
7
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@alicanyucelankara06/generic-service",
3
+ "version": "1.0.0",
4
+ "description": "Angular 21 Generic CRUD Service Package",
5
+ "license": "MIT",
6
+ "peerDependencies": {
7
+ "@angular/common": "^21.0.0",
8
+ "@angular/core": "^21.0.0",
9
+ "rxjs": "^7.8.0"
10
+ },
11
+ "devDependencies": {
12
+ "@angular/common": "^21.0.0",
13
+ "@angular/compiler": "^21.0.0",
14
+ "@angular/compiler-cli": "^21.0.0",
15
+ "@angular/core": "^21.0.0",
16
+ "@angular/platform-browser": "^21.0.0",
17
+ "ng-packagr": "^21.0.0",
18
+ "rxjs": "^7.8.0",
19
+ "typescript": "~5.9.0",
20
+ "zone.js": "~0.15.0"
21
+ },
22
+ "scripts": {
23
+ "build": "ng-packagr -p ng-package.json",
24
+ "build:watch": "ng-packagr -p ng-package.json --watch"
25
+ },
26
+ "sideEffects": false
27
+ }