Activity 33: Angular Recipe Grid

This activity focuses on building an Angular component that displays a recipe grid. Each recipe will be represented by a card showing its title, ingredients, image, and a button to view the details. This hands-on task will help you understand how to organize and display data using Angular's powerful features like *ngFor for dynamic rendering, event handling, and property binding for images. By the end of this activity, you'll have a functional recipe grid layout suitable for any recipe-based application.

recipe.service.ts

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

@Injectable({
  providedIn: 'root',
})
export class RecipeService {
  getRecipes(): Recipe[] {
    return [
      {
        id: 1,
        title: 'Spaghetti Bolognese',
        ingredients: ['Spaghetti', 'Minced beef', 'Tomato sauce', 'Garlic', 'Onion'],
        instructions: 'Cook spaghetti. Brown beef. Add sauce, garlic, and onion.',
        image: 'assets/images/spaghetti.jpg',
      },
      {
        id: 2,
        title: 'Chicken Curry',
        ingredients: ['Chicken', 'Curry powder', 'Coconut milk', 'Garlic', 'Onion'],
        instructions: 'Cook chicken, add spices and coconut milk, simmer until tender.',
        image: 'assets/images/chicken-curry.jpg',
      },
      {
        id: 3,
        title: 'Caesar Salad',
        ingredients: ['Lettuce', 'Croutons', 'Caesar dressing', 'Parmesan'],
        instructions: 'Toss lettuce, croutons, and dressing, top with Parmesan.',
        image: 'assets/images/caesar-salad.jpg',
      },
      {
        id: 4,
        title: 'Beef Tacos',
        ingredients: ['Ground beef', 'Taco shells', 'Lettuce', 'Cheese', 'Salsa'],
        instructions: 'Brown beef, fill taco shells with beef and toppings.',
        image: 'assets/images/beef-tacos.jpg',
      },
      {
        id: 5,
        title: 'Vegetable Stir Fry',
        ingredients: ['Bell peppers', 'Carrot', 'Broccoli', 'Soy sauce', 'Ginger'],
        instructions: 'Stir fry vegetables in soy sauce and ginger until tender.',
        image: 'assets/images/vegetable-stir-fry.jpg',
      },
      {
        id: 6,
        title: 'Chicken Alfredo',
        ingredients: ['Chicken', 'Fettuccine', 'Cream', 'Parmesan', 'Garlic'],
        instructions: 'Cook chicken and fettuccine, mix with Alfredo sauce and Parmesan.',
        image: 'assets/images/chicken-alfredo.jpg',
      },
      {
        id: 7,
        title: 'Margarita Pizza',
        ingredients: ['Pizza dough', 'Tomato sauce', 'Mozzarella', 'Basil'],
        instructions: 'Top pizza dough with sauce, cheese, and basil, then bake.',
        image: 'assets/images/margarita-pizza.jpg',
      },
      {
        id: 8,
        title: 'Beef Burger',
        ingredients: ['Ground beef', 'Buns', 'Lettuce', 'Tomato', 'Cheese'],
        instructions: 'Grill beef patties and assemble burgers with toppings.',
        image: 'assets/images/beef-burger.jpg',
      },
      {
        id: 9,
        title: 'Fried Rice',
        ingredients: ['Rice', 'Egg', 'Carrot', 'Peas', 'Soy sauce'],
        instructions: 'Stir fry rice with eggs, vegetables, and soy sauce.',
        image: 'assets/images/fried-rice.jpg',
      },
      {
        id: 10,
        title: 'Pancakes',
        ingredients: ['Flour', 'Egg', 'Milk', 'Baking powder', 'Sugar'],
        instructions: 'Mix ingredients and cook on a griddle until golden brown.',
        image: 'assets/images/pancakes.jpg',
      },
    ];
  }
}
  1. id: A unique identifier for each recipe (used for tracking or referencing).

  2. title: The name of the recipe (e.g., "Spaghetti Bolognese").

  3. ingredients: An array of ingredients required for the recipe (e.g., ['Spaghetti', 'Minced beef', 'Tomato sauce']).

  4. instructions: A string describing how to prepare the recipe.

  5. image: The file path to an image representing the recipe.

The @Injectable decorator with { providedIn: 'root' } ensures that the service is available globally and can be injected into any component or other service.

recipe.model.ts

export interface Recipe {
  id: number;
  title: string;
  ingredients: string[];
  instructions: string;
  image: string;
}
  • id: A unique number identifying each recipe.

  • title: A string representing the name of the recipe (e.g., "Spaghetti Bolognese").

  • ingredients: An array of strings, where each string is an ingredient required for the recipe.

  • instructions: A string providing step-by-step instructions on how to prepare the recipe.

  • image: A string containing the file path or URL to an image of the recipe.

recipe.component.ts

import { Component } from '@angular/core';
import { RecipeService } from './recipe.service';
import { Recipe } from './recipe.model';
import { CommonModule } from '@angular/common';  

@Component({
  selector: 'app-recipe',
  standalone: true,  
  imports: [CommonModule],  
  templateUrl: './recipe.component.html',
  styleUrls: ['./recipe.component.css'],
  providers: [RecipeService],
})
export class RecipeComponent {
  recipes: Recipe[] = [];

  constructor(private recipeService: RecipeService) {}

  ngOnInit(): void {
    this.recipes = this.recipeService.getRecipes();
  }

  viewDetails(recipe: Recipe): void {
    alert('Details for: ' + recipe.title);
  }
}
  1. Imports:

    • RecipeService: A service that provides the recipe data.

    • Recipe: The interface defining the structure of each recipe.

    • CommonModule: Importing the CommonModule to access common Angular directives like ngFor that are needed to render the recipe list.

  2. Component Decorator:

    • selector: 'app-recipe': Specifies the HTML tag where this component will be used.

    • standalone: true: Indicates that this component is standalone, so it doesn't need to be declared in any NgModule's declarations array.

    • imports: [CommonModule]: Imports CommonModule to ensure common directives like ngFor work in the template.

    • templateUrl: './recipe.component.html': The path to the component's HTML template.

    • styleUrls: ['./recipe.component.css']: The path to the component's CSS styles.

    • providers: [RecipeService]: Declares that RecipeService will be available for dependency injection in this component.

  3. Class:

    • The RecipeComponent class defines the component's behavior.

    • recipes: Recipe[] = []: An array to hold the list of recipes.

    • Constructor: It injects the RecipeService to fetch recipe data.

    • ngOnInit(): When the component initializes, it calls getRecipes() from the RecipeService to load the list of recipes.

    • viewDetails(): This method is triggered when a user clicks on a recipe's "View Details" button. It shows an alert with the title of the selected recipe.

This component fetches the recipe data from the RecipeService and displays it in the template, allowing users to view details of a specific recipe.

recipe.component.html

<div class="recipe-list">

  <div class="recipe-list__items">
    <div class="recipe-list__item" *ngFor="let recipe of recipes">
      <div class="recipe-card">
        <img class="recipe-card__image" [src]="recipe.image" [alt]="recipe.title" />
        <h3 class="recipe-card__title">{{ recipe.title }}</h3>
        <p class="recipe-card__ingredients">Ingredients: {{ recipe.ingredients.join(', ') }}</p>
        <p class="recipe-card__instructions">{{ recipe.instructions }}</p>
        <button class="recipe-card__button" (click)="viewDetails(recipe)">View Details</button>
      </div>
    </div>
  </div>
</div>
  • Container Div:

    • <div class="recipe-list">: The main container for the recipe list.

    • <div class="recipe-list__items">: A wrapper that contains all recipe items.

  • Recipe Item Loop:

    • <div class="recipe-list__item" *ngFor="let recipe of recipes">: The *ngFor directive iterates over the recipes array in the component, creating one <div> for each recipe.

    • recipe: Each recipe object will be available inside the loop, and its properties will be used to display the content.

  • Recipe Card:

    • <div class="recipe-card">: The wrapper for each recipe item, styled as a card.

    • <img class="recipe-card__image" [src]="recipe.image" [alt]="recipe.title" />: The recipe image is displayed. The [src] is bound to the image property of the recipe object, and [alt] is set to the title for accessibility.

    • <h3 class="recipe-card__title">{{ recipe.title }}</h3>: Displays the recipe's title.

    • <p class="recipe-card__ingredients">Ingredients: {{ recipe.ingredients.join(', ') }}</p>: Lists the ingredients as a comma-separated string using join(', ').

    • <p class="recipe-card__instructions">{{ recipe.instructions }}</p>: Displays the instructions for the recipe.

    • <button class="recipe-card__button" (click)="viewDetails(recipe)">View Details</button>: A button that triggers the viewDetails() method when clicked, passing the current recipe as an argument.

OUTPUT:

DESKTOP

TABLET

MOBILE

Github Link: github.com/MJDayunot/Angular-Recipe-Grid

Hosting Url: angular-recipe-grid-4bbee.web.app