Activity 33: Angular Recipe Grid

Project Structure

Here's the breakdown of the files we'll use in this project:

  1. recipe.service.ts: Handles the business logic and provides data to the component.

  2. recipe.model.ts: Defines the structure of a recipe object.

  3. recipe.component.ts: Manages the component's logic.

  4. recipe.component.html: Displays the recipe data.


Create the Recipe Model

The model defines the structure of a recipe.

// recipe.model.ts
export interface Recipe {
    id: number;
    name: string;
    category: string;
    tags: string[];
    imageUrl: string;
  }

Create the Recipe Service

The service provides a list of recipes to the component.

// recipe.service.ts
import { Injectable } from '@angular/core';
import { Recipe } from '../models/recipe.model';

@Injectable({
  providedIn: 'root',
})
export class RecipeService {
  getRecipes(): Recipe[] {
    return [
      {
        id: 1,
        name: 'Pancakes',
        category: 'Breakfast',
        tags: ['Simple', 'Trending'],
        imageUrl: 'https://th.bing.com/th/id/OIP.zmdy6RSEZeDiWxMNS2hN-gAAAA?rs=1&pid=ImgDetMain',
      },
      {
        id: 2,
        name: 'Lemon Eton Mess',
        category: 'Dessert',
        tags: ['Easy', 'Trending'],
        imageUrl: 'https://th.bing.com/th/id/OSK.HEROz1VGKrhR6UKOuq53TxPwFyb4HiyZ_JgIliy70S3hIdM?rs=1&pid=ImgDetMain',
      },
      {
        id: 3,
        name: 'Classic Cheesecake',
        category: 'Dessert',
        tags: ['Rich', 'Creamy'],
        imageUrl: 'https://bing.com/th?id=OSK.7051dbd80c33fe2009b79722e730d7d9',
      },
      {
        id: 4,
        name: 'Berry Smoothie Bowl',
        category: 'Breakfast',
        tags: ['Healthy', 'Fresh'],
        imageUrl: 'https://th.bing.com/th/id/OIP.EDdtpL_FJUoJLIJYUfy2tAHaLH?rs=1&pid=ImgDetMain',
      },
      {
        id: 5,
        name: 'Spaghetti Carbonara',
        category: 'Dinner',
        tags: ['Italian', 'Quick'],
        imageUrl: 'https://th.bing.com/th/id/OIP.dchqjONJhrMllqIkMeIZbwHaLH?rs=1&pid=ImgDetMain',
      },
      {
        id: 6,
        name: 'Avocado Toast',
        category: 'Breakfast',
        tags: ['Simple', 'Healthy'],
        imageUrl: 'https://www.jessicagavin.com/wp-content/uploads/2020/07/avocado-toast-11.jpg',
      },
      {
        id: 7,
        name: 'Grilled Chicken Salad',
        category: 'Lunch',
        tags: ['Healthy', 'Protein-Rich'],
        imageUrl: 'https://i.pinimg.com/originals/68/52/4f/68524f554edc553908cb778f09424a2a.jpg',
      },
      {
        id: 8,
        name: 'Chocolate Lava Cake',
        category: 'Dessert',
        tags: ['Decadent', 'Trending'],
        imageUrl: 'https://bing.com/th?id=OSK.22e35e6896601da85dfe55568476266e',
      },
      {
        id: 9,
        name: 'Vegetable Stir Fry',
        category: 'Dinner',
        tags: ['Quick', 'Vegan'],
        imageUrl: 'https://bing.com/th?id=OSK.644af19e702c44f1b9d0d28a089b279a',
      },
      {
        id: 10,
        name: 'Choc-Crackle Peanut Butter Crunch',
        category: 'Snacks',
        tags: ['Awesome', 'Trending'],
        imageUrl: 'https://bing.com/th?id=OSK.3a9c58666b93e2f5b6fdc093bcb3aab0',
      },
    ];
  }
}

Create the Recipe Component

The component fetches data from the service and prepares it for display.

// recipe.component.ts
import { Component, Input } from '@angular/core';
import { Recipe } from '../../models/recipe.model';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-recipe', 
  imports: [CommonModule],
  templateUrl: './recipe.component.html',
  styleUrls: ['./recipe.component.css']
})
export class RecipeComponent {
  @Input() recipe!: Recipe; 
recipes: any;
}

Create the Recipe Template

The HTML file displays the recipes in a card layout.

recipe.component.html 
<div class="recipe-card">
    <img class="recipe-card__image" [src]="recipe.imageUrl" [alt]="recipe.name" />
    <h3 class="recipe-card__name">{{ recipe.name }}</h3>
    <div class="recipe-card__tags">
      <span class="recipe-card__tag" *ngFor="let tag of recipe.tags">{{ tag }}</span>
    </div>
    <button class="recipe-card__button">Add to Cart</button>
  </div>

Step 5: Styling the Component

(Optional) Add some basic CSS for styling.

/* recipe.component.css */
.recipe-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center; 
    gap: 20px; 
    padding: 20px; 
    box-sizing: border-box; 
  }

  .recipe-card {
    flex: 1 1 calc(20% - 20px); 
    max-width: 200px; 
    min-width: 200px; 
    height: 350px;
    box-sizing: border-box; 
    display: flex;
    flex-direction: column;
    align-items: center; 
    justify-content: space-between; 
    text-align: center; 
    background: #9c5757; 
    border: 1px solid #ccc; 
    border-radius: 8px;
    overflow: hidden;
    padding: 20px; 
    box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); 
    transition: transform 0.2s ease-in-out; 
  }

  .recipe-card:hover {
    transform: scale(1.05); 
  }

  .recipe-card img {
    width: 100%; 
    height: 150px; 
    object-fit: cover; 
    border-radius: 8px 8px 0 0;
    margin-bottom: 10px; 
  }

  .recipe-card__title {
    font-size: 1rem;
    font-weight: bold;
    margin: 5px 0; 
  }

  .recipe-card__category {
    font-size: 0.9rem;
    color: #666; 
    margin-bottom: 5px; 
  }

  .recipe-card__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px; 
    margin-bottom: 5px; 
  }

  .recipe-card__tag {
    background: #007bff;
    color: #fff;
    padding: 0.3rem 0.5rem;
    border-radius: 4px;
    font-size: 0.8rem;
  }

  .recipe-card__button {
    margin-top: auto; 
    padding: 8px 12px;
    background: #ff0000; 
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.2s ease-in-out;
  }

  .recipe-card__button:hover {
    background: #0056b3; 
  }

  @media (max-width: 1024px) {
    .recipe-card {
      flex: 1 1 calc(33.33% - 20px); 
      height: 350px; 
    }
  }

  @media (max-width: 768px) {
    .recipe-card {
      flex: 1 1 calc(50% - 20px); 
      height: 350px; 
    }
  }

  @media (max-width: 480px) {
    .recipe-card {
      flex: 1 1 100%; 
      height: 350px; 
    }
  }

output:

githublink:https://github.com/RodelCalda/ACT-33.git

firebaselink:activity-33-63241.web.app