Activity 32: Angular Library Grid

The purpose of the Books interface is to define a consistent structure for book objects in your Angular application. It ensures that whenever you work with a book, it has all the necessary details like its ID, title, author, genre, and an image URL. This helps prevent errors and makes the code easier to manage, especially when handling book data in components, services, or APIs. It's like setting rules to make sure every book object is complete and correctly formatted.

// book.model.ts
export interface Books  {
  bookID: number;
  bookTitle: string;
  author: string;
  genre: string;
  image: string;
}
// book.service.ts

import { Injectable } from '@angular/core';
import { Books } from './books';

@Injectable({
  providedIn: 'root',
})
export class BooksService {
  constructor() {}

  private booklist: Books[] = [
    {
      bookID: 1,
      bookTitle: 'To Kill a Mockingbird',
      genre: 'Fiction',
      author: 'Harper Lee',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS68RY84hFCFEv79YFlT9_dhetWuxhIO9Un6w&s',
    },

    {
      bookID: 2,
      bookTitle: 'The catcher in the rye',
      genre: 'Fiction',
      author: 'J.D Salinger',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSxmaay9kLmr69JLsXnSzLtEIdw6v0pNMQARg&s',
    },
    {
      bookID: 3,
      bookTitle: 'Pride and Prejudice',
      genre: 'Fiction',
      author: 'J.D Salinger',
      image:
        'https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1320399351i/1885.jpg',
    },
    {
      bookID: 4,
      bookTitle: 'Romeo and Juliet',
      genre: 'Fiction',
      author: 'William Shakespear',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQo9CwLCGNtv7Ywx_Rr3slD_oE190A2KvGvng&s',
    },
    {
      bookID: 5,
      bookTitle: '1984',
      genre: 'Fiction',
      author: 'George Orwell',
      image:
        'https://d3525k1ryd2155.cloudfront.net/f/935/524/9780451524935.RH.0.m.jpg',
    },
    {
      bookID: 6,
      bookTitle: 'Hamlet',
      genre: 'Fiction',
      author: 'William Shakespear',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSSHjZWUiPR-paryJXkATaigCJhhFrZrys4XA&s',
    },
    {
      bookID: 7,
      bookTitle: 'Brave New World',
      genre: 'Fiction',
      author: 'Aldous Leonard Huxley',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTzKGL7JRnlQuFYM59VIpLa20kjhjpvfJawMA&s',
    },
    {
      bookID: 8,
      bookTitle: 'The fellowship of the Ring',
      genre: 'Fiction',
      author: 'J.R.R Tolkien',
      image:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTaT8GXFdSmDSwqZPCEvkBryTncntoVBJUe_A&s',
    },
  ];

  getBooks(): Books[] {
    return this.booklist;
  }
}
  • The BooksService is marked with the @Injectable decorator and registered as a singleton service in the root injector. This means it can be injected and used throughout the application.

  • A private property, booklist, is defined to store an array of book objects. Each object follows the Books interface, ensuring a consistent structure with properties like bookID, bookTitle, author, genre, and image.

  • The getBooks() method is a public function that simply returns the booklist array. This allows other parts of the application (like components) to access the list of books.

  • When a component or another service needs the list of books, it injects this BooksService and calls the getBooks() method to fetch the data. This keeps the book data centralized and accessible in one place.

  1. The BooksComponent is designed to display a list of books. It declares a books property, initialized as an empty array, to store the retrieved book data.

  2. The BooksService is injected into the component using the constructor. This allows the component to access the methods of the service.

  3. In the ngOnInit() lifecycle method, the component calls the getBooks() method from the BooksService to fetch the list of books. The returned data is then assigned to the books property.

  4. The fetched books are logged to the console for debugging and can also be used in the component's template for display. This shows how the component interacts with the service to manage and display data.

//book.component.ts

import { Component } from '@angular/core';
import { Books } from './books';
import { BooksService } from './books.service';

@Component({
  selector: 'app-books',
  templateUrl: './books.component.html',
  styleUrl: './books.component.css'
})
export class BooksComponent {


  books: Books[] = [];

  constructor(private bookService: BooksService) {}

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

}
  • The outermost <div> with the wrapper class acts as a container for all the content.

  • Inside the wrapper, there is a wrapper__content div that holds the dynamically generated book cards. The *ngFor directive iterates over the books array, creating one wrapper__content-card div for each book.

  • Each card displays the book's data:

    • An <img> element dynamically sets the source ([src]) to the book's image property.

    • The card__title displays the book's title using {{ book.bookTitle }}.

    • The card__author shows the author of the book with {{ book.author }}.

  • The static card__rating section displays a star-based rating for the book. Currently, the stars are hardcoded, but they can be made dynamic in the future.

  • Two buttons are added for interaction:

    • "Available" (styled as a primary button).

    • "View Details" (styled as a secondary button).

<div class="wrapper">
  <div class="wrapper__content">
    <div *ngFor="let book of books" class="wrapper__content-card">
      <img [src]="book.image" class="card__image" />
      <div class="card__content">
        <h3 class="card__title">{{ book.bookTitle }}</h3>
        <p class="card__author">{{ book.author }}</p>
        <p class="card__rating">
          Rating:<i class="fa-solid fa-star"></i><i class="fa-solid fa-star"></i
          ><i class="fa-solid fa-star"></i><i class="fa-solid fa-star"></i>
          <i class="fa-regular fa-star"></i>
        </p>
        <button class="btn btn-primary">Available</button> <br />
        <button class="btn btn-secondary">View Details</button>
      </div>
    </div>
  </div>
</div>
  • .wrapper:
    This class is applied to the main container, giving it an orange border, padding of 3rem, and a minimum height of 100vh (making sure it takes up at least the full height of the viewport).

  • .wrapper__content:
    Inside the wrapper, this class is used for the layout of the book cards. It uses Flexbox to arrange the cards, with a gap of 3rem between them. The flex-wrap: wrap; allows the cards to wrap onto new lines when the space is filled. It also centers the cards both horizontally and vertically with justify-content: center; and align-items: center;.

  • .card__image:
    The images inside each book card are constrained to a height of 200px and width of 150px to maintain a consistent size.

  • .wrapper__content-card:
    This class is applied to the individual book cards. The cards have a background color of #F5F8FA, a width of 208px, and padding of 3rem. The content is displayed in a vertical column with Flexbox (flex-direction: column;), and the items are centered within each card (align-items: center;).

  • .card__content:
    This is the section within each card where the book's title, author, and buttons are displayed. It has a top margin (margin-top: 1rem;) and a line-height of 2 to ensure the text is spaced out evenly. (Note: background-color: lightblue; is commented out but can be enabled for styling.)

  • .btn:
    This class styles both buttons with padding, no borders, a 12px border-radius for rounded corners, and a small right margin to separate them when placed next to each other.

  • .btn-primary:
    This class styles the "Available" button with a border color of rgba(6, 178, 3, 0.695) (a shade of green) and a transparent background.

  • .btn-secondary:
    The "View Details" button is styled with a background color of rgba(6, 178, 3, 0.507) (a lighter green), no border, and white text to contrast with the background color.

.wrapper { 
  border: 2px solid orange;
  min-height: 100vh;
  padding: 3rem;
}

.wrapper__content { 
  display: flex;
  gap: 3rem;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

.card__image { 
  height: 200px;
  width: 150px;
}

.wrapper__content-card { 
  background-color: #F5F8FA;
  height:auto;
  width: 208px;
  padding: 3rem;

  display: flex;
  flex-direction: column;
  align-items: center;
}


.card__content { 
  /* background-color: lightblue; */
  width: 100%;
  margin-top: 1rem;
  line-height: 2;
}

.btn { 
  padding: 5px 10px;
  border: none;
  margin-right: 3px;
  border-radius: 12px;
}

.btn-primary { 
  border: 2px solid rgba(6, 178, 3, 0.695);
  background: transparent;
}

.btn-secondary { 
  background: rgba(6, 178, 3, 0.507);
  border: none;
  color: white;
}

github repository: https://github.com/thisisnotsnorlax/AngularLIbraryGrid.git

Firebase: https://angularlibrarygrid.web.app/