Overview
The FavoritesService manages user favorites with full CRUD (Create, Read, Update, Delete) operations. It automatically handles authentication tokens and provides EventEmitters for real-time updates across components.
Import
import { FavoritesService } from 'src/app/shared/services/favorites/favorites.service';
import { MediaItem } from 'src/app/shared/models/movie.model';
import { FavoritesResponse } from 'src/app/shared/models/favoritesResponse.model';
import { DeleteResponse } from 'src/app/shared/models/deleteResponse.model';
Properties
EventEmitters
favoriteDeleted
EventEmitter<string>
required
Emits the ID of a deleted favorite media item. Components can subscribe to this to react to deletions.
favoriteUpdated
EventEmitter<MediaItem>
required
Emits the updated MediaItem when a favorite is modified. Components can subscribe to this to react to updates.
Methods
addToFavorites
Adds a media item to the user’s favorites list.
addToFavorites(movie: MediaItem): Observable<MediaItem>
The media item to add to favorites
Observable that emits the created favorite item with server-generated fields (_id, createdAt, updatedAt)
HTTP Details:
- Method:
POST
- Endpoint:
${environment.serverFavoritesURL}
- Headers:
Content-Type: application/json, Authorization: Bearer {token}
Example:
const mediaItem: MediaItem = {
title: 'Inception',
year: '2010',
imdbID: 'tt1375666',
type: 'movie',
poster: 'https://example.com/poster.jpg',
description: 'A mind-bending thriller'
};
favoritesService.addToFavorites(mediaItem).subscribe({
next: (createdItem) => {
console.log('Added to favorites:', createdItem);
console.log('Favorite ID:', createdItem._id);
},
error: (error) => {
if (error.message === 'Media item already in favorites') {
console.log('Already in favorites');
}
}
});
getFavorites
Retrieves the user’s favorite media items with pagination, sorting, and filtering.
getFavorites(
currentPage: number,
pageSize: number,
sortField?: string,
sortOrder?: number,
searchTerm?: string,
mediaType?: string
): Observable<FavoritesResponse>
The page number to retrieve (starts at 1)
Field name to sort by (e.g., ‘title’, ‘year’, ‘createdAt’)
Sort direction: 1 for ascending, -1 for descending
Search query to filter favorites by title
Filter by media type: 'movie', 'series', 'episode', or 'all' for no filter
return
Observable<FavoritesResponse>
Observable that emits a FavoritesResponse containing favorites array and pagination metadata
HTTP Details:
- Method:
GET
- Endpoint:
${environment.serverFavoritesURL}
- Headers:
Authorization: Bearer {token}
- Query Parameters:
page, pageSize, sortField, sortOrder, type, searchTerm
Example:
// Basic pagination
favoritesService.getFavorites(1, 10).subscribe({
next: (response) => {
console.log('Favorites:', response.favorites);
console.log('Total:', response.totalFavorites);
console.log('Current page:', response.currentPage);
}
});
// With sorting and filtering
favoritesService.getFavorites(
1, // page 1
20, // 20 items per page
'title', // sort by title
1, // ascending order
'Batman', // search for "Batman"
'movie' // only movies
).subscribe({
next: (response) => {
console.log('Filtered favorites:', response.favorites);
}
});
// Get all types (no filter)
favoritesService.getFavorites(1, 10, undefined, undefined, undefined, 'all')
.subscribe(response => {
// All media types included
});
Deletes a favorite media item from the user’s list.
deleteMediaItem(mediaId: string): Observable<DeleteResponse>
The unique ID (_id) of the favorite to delete
return
Observable<DeleteResponse>
Observable that emits a DeleteResponse with success message
HTTP Details:
- Method:
DELETE
- Endpoint:
${environment.serverFavoritesURL}/{mediaId}
- Headers:
Authorization: Bearer {token}
Example:
const favoriteId = '507f1f77bcf86cd799439011';
favoritesService.deleteMediaItem(favoriteId).subscribe({
next: (response) => {
console.log('Delete response:', response.message);
// Response: { message: 'Success', data: {} }
},
error: (error) => {
if (error.status === 404) {
console.log('Favorite not found');
}
}
});
updateFavorite
Updates the description of a favorite media item.
updateFavorite(mediaItem: MediaItem): Observable<MediaItem>
The media item with updated description (must include _id field)
Observable that emits the updated MediaItem with all fields
HTTP Details:
- Method:
PATCH
- Endpoint:
${environment.serverFavoritesURL}/{mediaItem._id}
- Headers:
Content-Type: application/json, Authorization: Bearer {token}
- Body:
{ description: string }
Example:
const mediaItem: MediaItem = {
_id: '507f1f77bcf86cd799439011',
title: 'Inception',
year: '2010',
imdbID: 'tt1375666',
type: 'movie',
poster: 'https://example.com/poster.jpg',
description: 'Updated description: A masterpiece of cinema'
};
favoritesService.updateFavorite(mediaItem).subscribe({
next: (updatedItem) => {
console.log('Updated favorite:', updatedItem);
console.log('New description:', updatedItem.description);
},
error: (error) => {
console.error('Update failed:', error);
}
});
Authentication Token Handling
All methods automatically retrieve the authentication token from AuthService and include it in the Authorization header:
const token = this.authService.getAuthToken();
const options = {
headers: new HttpHeaders({
'Authorization': `Bearer ${token}`
})
};
If the token is invalid or expired, the AuthInterceptor will handle the 401 error and redirect to login.
Environment Configuration
The service uses environment.serverFavoritesURL from the environment configuration:
// environment.development.ts
export const environment = {
serverFavoritesURL: 'http://localhost:3000/api/favorites'
};
EventEmitter Usage
The service provides EventEmitters for real-time updates across components:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FavoritesService } from 'src/app/shared/services/favorites/favorites.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-favorites-list',
templateUrl: './favorites-list.component.html'
})
export class FavoritesListComponent implements OnInit, OnDestroy {
private deleteSubscription: Subscription;
private updateSubscription: Subscription;
constructor(private favoritesService: FavoritesService) {}
ngOnInit() {
// Listen for deletions
this.deleteSubscription = this.favoritesService.favoriteDeleted.subscribe(
(deletedId) => {
console.log('Favorite deleted:', deletedId);
// Refresh the list or remove item from UI
}
);
// Listen for updates
this.updateSubscription = this.favoritesService.favoriteUpdated.subscribe(
(updatedItem) => {
console.log('Favorite updated:', updatedItem);
// Update the item in the UI
}
);
}
ngOnDestroy() {
this.deleteSubscription?.unsubscribe();
this.updateSubscription?.unsubscribe();
}
}
Complete Usage Example
import { Component, OnInit } from '@angular/core';
import { FavoritesService } from 'src/app/shared/services/favorites/favorites.service';
import { MediaItem } from 'src/app/shared/models/movie.model';
@Component({
selector: 'app-favorites',
templateUrl: './favorites.component.html'
})
export class FavoritesComponent implements OnInit {
favorites: MediaItem[] = [];
totalFavorites: number = 0;
currentPage: number = 1;
pageSize: number = 10;
constructor(private favoritesService: FavoritesService) {}
ngOnInit() {
this.loadFavorites();
}
loadFavorites() {
this.favoritesService.getFavorites(this.currentPage, this.pageSize)
.subscribe({
next: (response) => {
this.favorites = response.favorites;
this.totalFavorites = response.totalFavorites;
}
});
}
addFavorite(mediaItem: MediaItem) {
this.favoritesService.addToFavorites(mediaItem).subscribe({
next: (created) => {
this.favorites.push(created);
this.totalFavorites++;
},
error: (error) => {
console.error('Failed to add favorite:', error);
}
});
}
deleteFavorite(favoriteId: string) {
this.favoritesService.deleteMediaItem(favoriteId).subscribe({
next: () => {
this.favorites = this.favorites.filter(f => f._id !== favoriteId);
this.totalFavorites--;
this.favoritesService.favoriteDeleted.emit(favoriteId);
}
});
}
updateDescription(mediaItem: MediaItem, newDescription: string) {
mediaItem.description = newDescription;
this.favoritesService.updateFavorite(mediaItem).subscribe({
next: (updated) => {
const index = this.favorites.findIndex(f => f._id === updated._id);
if (index !== -1) {
this.favorites[index] = updated;
}
this.favoritesService.favoriteUpdated.emit(updated);
}
});
}
searchFavorites(searchTerm: string) {
this.favoritesService.getFavorites(
1,
this.pageSize,
'title',
1,
searchTerm
).subscribe({
next: (response) => {
this.favorites = response.favorites;
this.totalFavorites = response.totalFavorites;
}
});
}
}