Activity 33: Angular Recipe Grid
Project Structure
Here's the breakdown of the files we'll use in this project:
recipe.service.ts
: Handles the business logic and provides data to the component.recipe.model.ts
: Defines the structure of a recipe object.recipe.component.ts
: Manages the component's logic.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