Activity 32: Angular Library Grid
Table of contents
- Create a List of Object Data Structures:
- OUTPUT:
- Angular Library Grid
- FIREBASE
Create a List of Object Data Structures:
book.service.ts
book.model.ts
book.component.ts
book.component.html
Step 1: Create the Book Model
Create a Book
model that includes the necessary fields for each book, including the rating
.
export interface Book {
id: number;
title: string;
author: string;
year: number;
imageUrl: string;
description: string;
rating: number; // Added rating
}
id: Unique identifier for each book.
title: Title of the book.
author: Author of the book.
year: Year the book was published.
imageUrl: URL for the book's cover image.
description: Brief description of the book.
rating: Rating for the book, represented as a number between 1 and 5.
Step 2: Update the Book Component
Create or update your BookComponent
to display the list of books, handle adding new books, and manage the removal of books. This component also contains logic for displaying the rating in star format.
import { Component } from '@angular/core';
import { Book } from '../../models/book.model';
import { BookService } from '../../services/book.service';
@Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.css']
})
export class BookComponent {
books: Book[] = [];
newBook: Book = { id: 0, title: '', author: '', year: 0, imageUrl: '', description: '', rating: 0 }; // Add rating to the new book
constructor(private bookService: BookService) {}
ngOnInit(): void {
// Load the initial list of books from the service
this.books = this.bookService.getBooks();
}
// Add a new book to the list
addBook(): void {
if (this.newBook.title && this.newBook.author && this.newBook.year && this.newBook.imageUrl && this.newBook.description && this.newBook.rating >= 1 && this.newBook.rating <= 5) {
this.bookService.addBook(this.newBook);
this.books = this.bookService.getBooks(); // Update the book list
this.newBook = { id: 0, title: '', author: '', year: 0, imageUrl: '', description: '', rating: 0 }; // Reset the form
}
}
// Remove a book from the list
removeBook(id: number): void {
this.bookService.removeBook(id);
this.books = this.bookService.getBooks(); // Update the book list
}
// Convert rating to an array of stars for display
getStars(rating: number): string[] {
return new Array(5).fill('').map((_, index) => index < rating ? 'filled' : 'empty');
}
}
Key Methods:
ngOnInit()
: Loads the books from theBookService
.addBook()
: Adds a new book to the list, ensuring the form is correctly filled and the rating is between 1 and 5.removeBook()
: Removes a book from the list.getStars(rating: number)
: Converts the rating number (1 to 5) into an array of star indicators (filled
orempty
).
Step 3: Create the Book Service
Create a service to handle adding and removing books. This service can maintain a simple array of books.
import { Injectable } from '@angular/core';
import { Book } from '../models/book.model';
@Injectable({
providedIn: 'root'
})
export class BookService {
// Array of books with image URL, description, and rating included
private books: Book[] = [
{
id: 1,
title: '1984',
author: 'George Orwell',
year: 1949,
imageUrl: 'https://i.pinimg.com/474x/17/fb/e1/17fbe1b3096863d5af032ce42c80c79c.jpg',
description: 'A dystopian novel set in a totalitarian society under constant surveillance.',
rating: 4.7 // Added rating
},
{
id: 2,
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
year: 1960,
imageUrl: 'https://i.pinimg.com/474x/de/3a/33/de3a3352cfa36ee2dcec88ba19ac8cb6.jpg',
description: 'A novel about racial injustice in the Deep South during the 1930s.',
rating: 4.8 // Added rating
},
{
id: 3,
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
year: 1925,
imageUrl: 'https://i.pinimg.com/474x/e1/cf/5e/e1cf5e169b77685a85a6e99b949681b2.jpg',
description: 'A story of wealth, love, and the American Dream set in the 1920s.',
rating: 4.5 // Added rating
},
{
id: 4,
title: 'Moby-Dick',
author: 'Herman Melville',
year: 1851,
imageUrl: 'https://i.pinimg.com/474x/2a/fb/78/2afb780cfed71aefa9f0a28b64201ddb.jpg',
description: 'A sea captain’s obsession with hunting the white whale that once wounded him.',
rating: 4.0 // Added rating
},
{
id: 5,
title: 'Pride and Prejudice',
author: 'Jane Austen',
year: 1813,
imageUrl: 'https://i.pinimg.com/474x/a1/97/0c/a1970cc8c9073c181b57cf15bd7837ed.jpg',
description: 'A classic romance novel about Elizabeth Bennet and her journey to understanding love.',
rating: 4.9 // Added rating
},
{
id: 6,
title: 'The Catcher in the Rye',
author: 'J.D. Salinger',
year: 1951,
imageUrl: 'https://i.pinimg.com/474x/53/33/4f/53334fc80a12be02195f18d23f7ad46c.jpg',
description: 'A coming-of-age story about Holden Caulfield, a teenager facing the difficulties of life.',
rating: 4.3 // Added rating
},
{
id: 7,
title: 'War and Peace',
author: 'Leo Tolstoy',
year: 1869,
imageUrl: 'https://i.pinimg.com/474x/59/1a/ff/591aff2e466579e0b57afebf11a61580.jpg',
description: 'A historical novel set during the Napoleonic wars, focusing on Russian aristocracy.',
rating: 4.2 // Added rating
},
{
id: 8,
title: 'The Odyssey',
author: 'Homer',
year: 800,
imageUrl: 'https://i.pinimg.com/474x/39/18/2b/39182b5a70622766a73f6340865a8ade.jpg',
description: 'An epic Greek poem that tells the story of Odysseus’ long journey home after the Trojan War.',
rating: 4.6 // Added rating
},
{
id: 9,
title: 'Frankenstein',
author: 'Mary Shelley',
year: 1818,
imageUrl: 'https://i.pinimg.com/474x/14/f7/7b/14f77bba717e72fcbdadf38b8979510c.jpg',
description: 'A novel about a scientist who creates a living being that soon turns against him.',
rating: 4.4 // Added rating
},
{
id: 10,
title: 'The Hobbit',
author: 'J.R.R. Tolkien',
year: 1937,
imageUrl: 'https://i.pinimg.com/474x/72/10/ad/7210ad89e6f21b3d32fad4cc3c20a89b.jpg',
description: 'The first book in the epic tale of Bilbo Baggins, his adventure to find treasure, and his encounter with dragons.',
rating: 4.8 // Added rating
},
];
constructor() {}
// Get the list of books
getBooks(): Book[] {
return this.books;
}
// Add a new book to the list
addBook(book: Book): void {
this.books.push(book);
}
// Remove a book from the list by ID
removeBook(id: number): void {
this.books = this.books.filter(book => book.id !== id);
}
}
Imports:
Injectable
: The@Injectable
decorator makes this service available for dependency injection in Angular components.Book
: TheBook
model (defined elsewhere in the app) represents the structure of a book object.
@Injectable: The decorator marks the class as an injectable service, meaning Angular can inject it into components or other services. The
providedIn: 'root'
metadata ensures that the service is available application-wide and only one instance is created (singleton pattern).books
array: This private array holds the list of books. Initially, it is empty ([]
). It simulates a basic data storage mechanism for books in the app.getBooks()
method: This method returns the current list of books from thebooks
array. It provides access to the book data for use in components. It’s a getter for the book data.addBook()
method: This method allows adding a new book to thebooks
array.It automatically assigns an
id
to the book by setting it to the current length of the array plus 1 (so the IDs will start from 1).The
book
object is added to thebooks
array usingpush()
.
removeBook()
method: This method removes a book from thebooks
array by itsid
.- It uses the
filter()
method to create a new array with all books except the one that matches the givenid
. The originalbooks
array is then replaced with this filtered array.
- It uses the
Step 4: Update the HTML to Display the Books and Rating
Update the HTML to display the list of books along with their ratings, and also provide a form to add new books.
<div class="book-list">
<h2 class="book-list__title">Book List</h2>
<!-- Display the list of books -->
<div class="book-list__grid">
<div class="book-list__item" *ngFor="let book of books">
<div class="book-item">
<!-- Display book image -->
<img [src]="book.imageUrl" alt="{{ book.title }} cover" class="book-item__image" />
<!-- Display book details -->
<div class="book-item__details">
<span class="book-item__title"><strong>{{ book.title }}</strong> by {{ book.author }} ({{ book.year }})</span>
<p class="book-item__description">{{ book.description }}</p>
<!-- Display rating stars -->
<div class="book-item__rating">
<span *ngFor="let star of getStars(book.rating)">
<i class="star" [ngClass]="{'filled': star === 'filled', 'empty': star === 'empty'}">★</i>
</span>
</div>
</div>
<button class="book-item__remove-btn" (click)="removeBook(book.id)">Remove</button>
</div>
</div>
</div>
<!-- Form to add a new book -->
<div class="book-list__add-form">
<h3 class="book-list__form-title">Add a New Book</h3>
<input [(ngModel)]="newBook.title" placeholder="Title" class="book-list__input" />
<input [(ngModel)]="newBook.author" placeholder="Author" class="book-list__input" />
<input [(ngModel)]="newBook.year" type="number" placeholder="Year" class="book-list__input" />
<input [(ngModel)]="newBook.imageUrl" placeholder="Image URL" class="book-list__input" />
<textarea [(ngModel)]="newBook.description" placeholder="Description" class="book-list__textarea"></textarea>
<!-- Rating input -->
<label for="rating">Rating (1-5):</label>
<input [(ngModel)]="newBook.rating" type="number" min="1" max="5" id="rating" class="book-list__rating-input" />
<button class="book-list__add-btn" (click)="addBook()">Add Book</button>
</div>
</div>
Step 5: Style the Components
Add the necessary styles for the book list, including the star ratings, form inputs, and buttons.
/* Book List container */
.book-list {
padding: 20px;
font-family: Arial, sans-serif;
}
/* Title of the book list */
.book-list__title {
text-align: center;
font-size: 2rem;
margin-bottom: 20px;
}
/* Grid for displaying books */
.book-list__grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
/* 2 columns for mobile */
gap: 20px;
}
@media (min-width: 600px) {
.book-list__grid {
grid-template-columns: repeat(3, 1fr);
/* 3 columns for tablets */
}
}
@media (min-width: 1024px) {
.book-list__grid {
grid-template-columns: repeat(5, 1fr);
/* 5 columns for desktops */
}
}
/* Each book item */
.book-list__item {
display: flex;
flex-direction: column;
justify-content: space-between;
border: 1px solid #ccc;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
}
.book-item {
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Book image styling */
.book-item__image {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 10px;
}
/* Book details */
.book-item__details {
margin-bottom: 10px;
}
.book-item__title {
font-size: 1.2rem;
margin-bottom: 5px;
}
.book-item__description {
font-size: 1rem;
color: #555;
}
/* Rating styling (stars) */
.book-item__rating {
margin-top: 10px;
}
.star {
font-size: 1.5rem;
color: #ccc;
margin-right: 3px;
}
.star.filled {
color: #ff6347;
/* Filled star color */
}
.star.empty {
color: #ccc;
/* Empty star color */
}
/* Remove button */
.book-item__remove-btn {
padding: 5px 10px;
border: none;
background-color: #ff6347;
color: white;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
.book-item__remove-btn:hover {
background-color: #ff4500;
}
/* Form to add a new book */
.book-list__add-form {
margin-top: 30px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background-color: #f9f9f9;
}
.book-list__form-title {
text-align: center;
font-size: 1.5rem;
margin-bottom: 20px;
}
/* Form inputs and buttons */
.book-list__input,
.book-list__textarea {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
}
.book-list__textarea {
height: 100px;
}
/* Add rating input styling */
.book-list__rating-input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
}
/* Add book button */
.book-list__add-btn {
padding: 10px 20px;
border: none;
background-color: #4caf50;
color: white;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
.book-list__add-btn:hover {
background-color: #45a049;
}
Key Styles:
.star
: Styles for the stars, with filled and empty versions..filled
: Applies to filled stars (highlighted in red)..empty
: Applies to empty stars (default gray).