S
Services & DI
Type-safe Angular services with dependency injection, HTTP client, and RxJS observables.
Code
typescript
1// user.service.ts2import { Injectable, inject } from "@angular/core";3import { HttpClient, HttpParams, HttpErrorResponse } from "@angular/common/http";4import { Observable, throwError, BehaviorSubject } from "rxjs";5import { map, catchError, tap, retry } from "rxjs/operators";6 7// Types8interface User {9 id: number;10 name: string;11 email: string;12 role: "admin" | "user";13}14 15interface CreateUserDto {16 name: string;17 email: string;18 role?: "admin" | "user";19}20 21interface UpdateUserDto extends Partial<CreateUserDto> {}22 23interface PaginatedResponse<T> {24 data: T[];25 total: number;26 page: number;27 limit: number;28}29 30interface QueryParams {31 page?: number;32 limit?: number;33 search?: string;34 role?: "admin" | "user";35}36 37@Injectable({38 providedIn: "root",39})40export class UserService {41 private readonly http = inject(HttpClient);42 private readonly apiUrl = "/api/users";43 44 // State management with BehaviorSubject45 private usersSubject = new BehaviorSubject<User[]>([]);46 public users$ = this.usersSubject.asObservable();47 48 private loadingSubject = new BehaviorSubject<boolean>(false);49 public loading$ = this.loadingSubject.asObservable();50 51 // GET all users with pagination52 getUsers(params: QueryParams = {}): Observable<PaginatedResponse<User>> {53 let httpParams = new HttpParams();54 55 if (params.page) httpParams = httpParams.set("page", params.page.toString());56 if (params.limit) httpParams = httpParams.set("limit", params.limit.toString());57 if (params.search) httpParams = httpParams.set("search", params.search);58 if (params.role) httpParams = httpParams.set("role", params.role);59 60 this.loadingSubject.next(true);61 62 return this.http63 .get<PaginatedResponse<User>>(this.apiUrl, { params: httpParams })64 .pipe(65 tap((response) => {66 this.usersSubject.next(response.data);67 this.loadingSubject.next(false);68 }),69 catchError(this.handleError.bind(this))70 );71 }72 73 // GET single user74 getUser(id: number): Observable<User> {75 return this.http.get<User>(`${this.apiUrl}/${id}`).pipe(76 retry(2),77 catchError(this.handleError.bind(this))78 );79 }80 81 // POST create user82 createUser(data: CreateUserDto): Observable<User> {83 return this.http.post<User>(this.apiUrl, data).pipe(84 tap((newUser) => {85 const currentUsers = this.usersSubject.value;86 this.usersSubject.next([...currentUsers, newUser]);87 }),88 catchError(this.handleError.bind(this))89 );90 }91 92 // PATCH update user93 updateUser(id: number, data: UpdateUserDto): Observable<User> {94 return this.http.patch<User>(`${this.apiUrl}/${id}`, data).pipe(95 tap((updatedUser) => {96 const currentUsers = this.usersSubject.value;97 const index = currentUsers.findIndex((u) => u.id === id);98 if (index !== -1) {99 currentUsers[index] = updatedUser;100 this.usersSubject.next([...currentUsers]);101 }102 }),103 catchError(this.handleError.bind(this))104 );105 }106 107 // DELETE user108 deleteUser(id: number): Observable<void> {109 return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(110 tap(() => {111 const currentUsers = this.usersSubject.value;112 this.usersSubject.next(currentUsers.filter((u) => u.id !== id));113 }),114 catchError(this.handleError.bind(this))115 );116 }117 118 // Error handler119 private handleError(error: HttpErrorResponse): Observable<never> {120 this.loadingSubject.next(false);121 122 let errorMessage = "An error occurred";123 124 if (error.error instanceof ErrorEvent) {125 // Client-side error126 errorMessage = error.error.message;127 } else {128 // Server-side error129 errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;130 }131 132 console.error(errorMessage);133 return throwError(() => new Error(errorMessage));134 }135}136 137// auth.service.ts - Authentication service138@Injectable({139 providedIn: "root",140})141export class AuthService {142 private readonly http = inject(HttpClient);143 private currentUserSubject = new BehaviorSubject<User | null>(null);144 public currentUser$ = this.currentUserSubject.asObservable();145 146 public get currentUserValue(): User | null {147 return this.currentUserSubject.value;148 }149 150 public get isAuthenticated(): boolean {151 return this.currentUserValue !== null;152 }153 154 login(email: string, password: string): Observable<{ user: User; token: string }> {155 return this.http156 .post<{ user: User; token: string }>("/api/auth/login", { email, password })157 .pipe(158 tap((response) => {159 localStorage.setItem("token", response.token);160 this.currentUserSubject.next(response.user);161 })162 );163 }164 165 logout(): void {166 localStorage.removeItem("token");167 this.currentUserSubject.next(null);168 }169}Run this example locally
$ ng generate service user