Activity 32: Angular Library Grid

Photo by Jess Bailey on Unsplash

Activity 32: Angular Library Grid

In this activity, I will create an Angular application to display a list of books in a grid layout. The app will fetch the data from a service, display it using a component, and utilize CSS Grid and Flexbox for layout. We will also apply the BEM CSS architecture for clean and maintainable styles.

Book Model

Create a model for the books. This will structure the book data and ensure we have a consistent format for each book.

Create the file book.model.ts:


export interface Book {
  id: number;
  title: string;
  author: string;
  genre: string;
  rating: number; 
  coverImage: string; 
  isAvailable: boolean;
}

This model will define the structure for each book object.

Book Service

Next, we create a service to manage the list of books. We will add a few books to this service with placeholder data, including book images and price.

Create the service book.service.ts:

import { Injectable } from '@angular/core';
import { Book } from './book.model';

@Injectable({
  providedIn: 'root',
})
export class BookService {
  getBooks(): Book[] {
    return [
      {
        id: 1,
        title: 'Romeo and Juliet',
        author: 'William Shakespeare',
        genre: 'Fiction',
        rating: 4,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
      {
        id: 2,
        title: 'To Kill a Mockingbird',
        author: 'Harper Lee',
        genre: 'Classic',
        rating: 5,
        coverImage: 'image/Book.jpg',
        isAvailable: false,
      },
      {
        id: 3,
        title: '1984',
        author: 'George Orwell',
        genre: 'Dystopian',
        rating: 5,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
      {
        id: 4,
        title: 'Pride and Prejudice',
        author: 'Jane Austen',
        genre: 'Romance',
        rating: 4,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
      {
        id: 5,
        title: 'The Great Gatsby',
        author: 'F. Scott Fitzgerald',
        genre: 'Classic',
        rating: 4,
        coverImage: 'image/Book.jpg',
        isAvailable: false,
      },
      {
        id: 6,
        title: 'Moby Dick',
        author: 'Herman Melville',
        genre: 'Adventure',
        rating: 3,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
      {
        id: 7,
        title: 'The Catcher in the Rye',
        author: 'J.D. Salinger',
        genre: 'Classic',
        rating: 4,
        coverImage: 'image/Book.jpg',
        isAvailable: false,
      },
      {
        id: 8,
        title: 'The Hobbit',
        author: 'J.R.R. Tolkien',
        genre: 'Fantasy',
        rating: 5,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
      {
        id: 9,
        title: 'War and Peace',
        author: 'Leo Tolstoy',
        genre: 'Historical Fiction',
        rating: 5,
        coverImage: 'image/Book.jpg',
        isAvailable: false,
      },
      {
        id: 10,
        title: 'Brave New World',
        author: 'Aldous Huxley',
        genre: 'Science Fiction',
        rating: 4,
        coverImage: 'image/Book.jpg',
        isAvailable: true,
      },
    ];
  }
}

This service provides a list of books with relevant details, including a placeholder image and price.

Displaying the Books in the Component

Now, let’s fetch and display the book data in the book.component.ts file. We'll also use CSS Grid and Flexbox for layout.

Create the component book.component.ts:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BookService } from '../book.service';
import { Book } from '../book.model';

@Component({
  selector: 'app-book',
  standalone: true,
  templateUrl: './book.component.html',
  styleUrls: ['./book.component.css'],
  imports: [CommonModule],
})
export class BookComponent implements OnInit {
  books: Book[] = [];

  constructor(private bookService: BookService) {}

  ngOnInit(): void {
    this.books = this.bookService.getBooks();
  }
}

This component fetches the list of books from the BookService and stores them in the books array.

Creating the Book Layout Using Grid and Flex

In the book.component.html, we will display the books in a grid layout with individual book cards. We’ll use Flexbox for aligning the content inside the book card, and CSS Grid for the overall layout.

Create the HTML file book.component.html:

<div class="book-grid">
  <div class="book-card" *ngFor="let book of books">
    <img class="book-card__image" [src]="book.coverImage" [alt]="book.title" />

    <h3 class="book-card__title">{{ book.title }}</h3>

    <p class="book-card__author"><strong>Author:</strong> {{ book.author }}</p>
    <p class="book-card__genre"><strong>Genre:</strong> {{ book.genre }}</p>
    <p class="book-card__rating">
      <strong>Rating:</strong>
      <span class="stars">
        <span *ngFor="let star of [].constructor(book.rating)"></span>
        <span *ngFor="let emptyStar of [].constructor(5 - book.rating)"></span>
      </span>
    </p>

    <span
      class="badge badge--available"
      *ngIf="book.isAvailable"
      >Available</span
    >
    <span
      class="badge badge--unavailable"
      *ngIf="!book.isAvailable"
      >Unavailable</span
    >

    <button class="button button--details">View Details</button>
  </div>
</div>

Styling the Book Cards Using BEM CSS Architecture

Create the CSS for the grid layout and styling the book cards. We’ll apply BEM CSS architecture for maintainability and clarity.

Create the CSS file book.component.css:

.book-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(2, 1fr); /* Mobile */
  padding: 20px;
}

@media (min-width: 768px) {
  .book-grid {
    grid-template-columns: repeat(3, 1fr); /* Tablet */
  }
}

@media (min-width: 1024px) {
  .book-grid {
    grid-template-columns: repeat(5, 1fr); /* Desktop */
  }
}

/* Book Card */
.book-card {
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 15px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  text-align: left;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.book-card__image {
  width: 100%;
  height: 180px;
  object-fit: cover;
  margin-bottom: 10px;
  border-radius: 4px;
}

.book-card__title {
  font-size: 1.2rem;
  margin: 10px 0 5px;
  font-weight: bold;
  color: #333;
}

.book-card__author,
.book-card__genre,
.book-card__rating {
  font-size: 0.9rem;
  color: #666;
  margin: 5px 0;
}

.stars {
  color: #ffcc00;
}

.badge {
  display: inline-block;
  margin: 10px 0;
  padding: 5px 10px;
  border-radius: 12px;
  font-size: 0.8rem;
  font-weight: bold;
}

.badge--available {
  background-color: #28a745;
  text-align: center;
  color: #fff;
}

.badge--unavailable {
  background-color: #dc3545;
  text-align: center;
  color: #fff;
}

.button {
  background-color: #28a745;
  color: #fff;
  border: none;
  padding: 8px 15px;
  font-size: 0.9rem;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
  margin-top: auto;
}

.button:hover {
  background-color: #218838;
}
  • The .book-container uses CSS Grid for the overall layout.

  • Each .book-card uses Flexbox for the inner layout.

  • BEM CSS is used for naming conventions to maintain modularity and clarity.

  • 2 columns for mobile, 3 columns for tablets, and 5 columns for desktops

Github Link: https://github.com/RodelDecio/Angular-Library-Grid.git

Hosting URL: https://angularlibrarygrid-eaa06.web.app