Activity 32: Angular Library Grid


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 the BookService.

  • 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 or empty).

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: The Book 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 the books 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 the books 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 the books array using push().

  • removeBook() method: This method removes a book from the books array by its id.

    • It uses the filter() method to create a new array with all books except the one that matches the given id. The original books array is then replaced with this filtered array.

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).


OUTPUT:


Angular Library Grid

FIREBASE