Building static food ordering application in Angular 19 with Bootstrap 5

Building static food ordering application in Angular 19 with Bootstrap 5

Hello guys how are you? Welcome back on my blog Therichpost. Today in this post I am going to share Building static food ordering application in Angular 19 with Bootstrap 5.

Live Demo

Angular 19 came. If you are new then you must check below two links:

Now guys here is the complete code snippet and please follow carefully:

Here’s a structured plan to build a static food ordering application in Angular 19 with Bootstrap 5. This includes the features you want: a good header and footer, a cart, checkout functionality, and a single item page.


Project Setup

  1. Create a New Angular Project
   ng new food-ordering-app
   cd food-ordering-app

Choose the routing option and select SCSS as the stylesheet format.

  1. Install Bootstrap 5
    Add Bootstrap to your Angular project:
   npm install bootstrap

Update the angular.json file to include Bootstrap styles:

   "styles": [
       "node_modules/bootstrap/dist/css/bootstrap.min.css",
       "src/styles.scss"
   ]
  1. Add FontAwesome (Optional)
    For icons, install FontAwesome:
   npm install @fortawesome/fontawesome-free

Include it in angular.json as well.


Application Structure

  • Header Component: Contains navigation links.
  • Footer Component: Displays footer content.
  • Home Component: Displays a list of food items.
  • Single Item Page: Detailed view of a food item.
  • Cart Component: Displays items in the cart.
  • Checkout Component: Form to input customer details and finalize the order.

Components and Routing

Generate the necessary components:

ng generate component components/header
ng generate component components/footer
ng generate component pages/home
ng generate component pages/single-item
ng generate component pages/cart
ng generate component pages/checkout

Configure routes in app-routs.ts:

import { Routes } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { SingleItemComponent } from './pages/single-item/single-item.component';
import { CartComponent } from './pages/cart/cart.component';
import { CheckoutComponent } from './pages/checkout/checkout.component';

export const routes: Routes = [
    { path: '', component: HomeComponent },
    { path: 'item/:id', component: SingleItemComponent },
    { path: 'cart', component: CartComponent },
    { path: 'checkout', component: CheckoutComponent },
];

Component Details

Header Component

Include navigation links and a cart icon:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <a class="navbar-brand" routerLink="/">Food Ordering</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav ms-auto">
        <li class="nav-item">
          <a class="nav-link" routerLink="/">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="/cart">Cart</a>
        </li>
      </ul>
    </div>
  </div>
</nav>

Footer Component

Create a sticky footer with copyright info:

<footer class="bg-dark text-white text-center py-3">
  © 2025 Food Ordering App. All rights reserved.
</footer>

Home Component

Display a grid of food items:

<div class="container mt-4">
    <div class="row">
      <div class="col-md-4" *ngFor="let item of foodItems">
        <div class="card">
          <img src="{{ item.image }}" class="card-img-top img-fluid"  style="height: 250px; object-fit: cover;" alt="{{ item.name }}">
          <div class="card-body">
            <h5 class="card-title">{{ item.name }}</h5>
            <p class="card-text">{{ item.description }}</p>
            <p class="card-text text-primary">${{ item.price }}</p>
            <a [routerLink]="['/item', item.id]" class="btn btn-primary">View Details</a>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="container mt-5 mb-5">
    <h2>Special Offers</h2>
    <div class="row">
      <div class="col-md-4">
        <div class="card bg-warning text-white">
          <img src="assets/images/pasta.jpg" class="card-img-top" style="height: 250px; object-fit: cover;" alt="Special Offer">
          <div class="card-body">
            <h5 class="card-title">Buy 1 Get 1 Free Pizza</h5>
            <p class="card-text">Order any large pizza and get another one for free! Limited time only.</p>
          </div>
        </div>
      </div>
      <div class="col-md-4">
        <div class="card bg-danger text-white">
          <img src="assets/images/pizza.jpg" class="card-img-top" style="height: 250px; object-fit: cover;" alt="Special Offer">
          <div class="card-body">
            <h5 class="card-title">10% Off on Your First Order</h5>
            <p class="card-text">Enjoy 10% off your first order with us. Use code FIRST10.</p>
          </div>
        </div>
      </div>
      <div class="col-md-4">
        <div class="card bg-success text-white">
          <img src="assets/images/burger.jpg" class="card-img-top" style="height: 250px; object-fit: cover;" alt="Special Offer">
          <div class="card-body">
            <h5 class="card-title">Free Drink with Every Burger</h5>
            <p class="card-text">Get a free soft drink with every burger ordered. Don't miss out!</p>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="container mt-5 py-5 newsletter-section bg-light rounded-3 shadow">
    <div class="row align-items-center">
      <!-- Left Content: Text and Details -->
      <div class="col-md-6 text-center text-md-start">
        <h2 class="fw-bold">Stay Updated!</h2>
        <p class="text-muted">Subscribe to our newsletter and get exclusive offers, updates, and mouth-watering recipes delivered straight to your inbox.</p>
      </div>
      <!-- Right Content: Subscription Form -->
      <div class="col-md-6">
        <form class="d-flex">
          <input type="email" class="form-control me-2 rounded-pill shadow-sm" placeholder="Enter your email" aria-label="Enter your email" required>
          <button class="btn btn-primary px-4 rounded-pill shadow-sm" type="submit">Subscribe</button>
        </form>
        <!-- Optional Privacy Text -->
        <small class="d-block mt-2 text-muted">We respect your privacy. Unsubscribe anytime.</small>
      </div>
    </div>
  </div>
  
  

Single Item Component

Show detailed info and add to cart:

<div class="container mt-4">
    <h2>{{ item.name }}</h2>
    <img [src]="item.image" class="card-img-top img-fluid"  style="height: 350px; object-fit: cover;"  alt="{{ item.name }}">
    <p>{{ item.description }}</p>
    <p class="text-primary">Price: ${{ item.price }}</p>
    <button class="btn btn-success" (click)="addToCart()">Add to Cart</button>
  </div>
  

Cart Component

Display selected items:

<div class="container mt-4">
    <h2>Your Cart</h2>
    <div *ngIf="cartItems.length === 0">Your cart is empty!</div>
    <table class="table" *ngIf="cartItems.length > 0">
      <thead>
        <tr>
          <th>Item</th>
          <th>Price</th>
          <th>Quantity</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let item of cartItems">
          <td>{{ item.name }}</td>
          <td>${{ item.price }}</td>
          <td>{{ item.quantity }}</td>
          <td>${{ item.price * item.quantity }}</td>
        </tr>
      </tbody>
    </table>
    <a routerLink="/checkout" class="btn btn-primary">Proceed to Checkout</a>
  </div>
  

Checkout Component

Form for user details:

<div class="container mt-4">
    <h2>Checkout</h2>
    <form (ngSubmit)="placeOrder()">
      <div class="mb-3">
        <label for="name" class="form-label">Name</label>
        <input type="text" id="name" class="form-control" required>
      </div>
      <div class="mb-3">
        <label for="address" class="form-label">Address</label>
        <input type="text" id="address" class="form-control" required>
      </div>
      <div class="mb-3">
        <label for="card" class="form-label">Card Number</label>
        <input type="text" id="card" class="form-control" required>
      </div>
      <button type="submit" class="btn btn-success">Place Order</button>
    </form>
  </div>
  

Styling

Add global styles in src/styles.scss for consistency:

/* You can add global styles to this file, and also import other style files */
body {
    font-family: 'Arial', sans-serif;
  }
  footer {
    
    width: 100%;
  }
  /* Add to styles.scss */
.navbar {
  position: fixed;
  width: 100%;
  top: 0;
  z-index: 1000;
}

body {
  padding-top: 56px; /* Adjust this value to match navbar height */
}
.newsletter-section {
    background: linear-gradient(135deg, #f8f9fa, #e9ecef); /* Subtle gradient background */
    padding: 2rem 1rem;
  }
  
  .newsletter-section h2 {
    color: #343a40; /* Darker text for contrast */
  }
  
  .newsletter-section .form-control {
    border: 2px solid #dee2e6; /* Slight border for better visibility */
    transition: all 0.3s ease; /* Smooth interaction effect */
  }
  
  .newsletter-section .form-control:focus {
    border-color: #0d6efd; /* Highlight on focus */
    box-shadow: 0 0 10px rgba(13, 110, 253, 0.5); /* Glowing effect */
  }
  
  .newsletter-section .btn-primary {
    background: #0d6efd; /* Bootstrap primary color */
    border: none;
    transition: all 0.3s ease; /* Smooth hover effect */
  }
  
  .newsletter-section .btn-primary:hover {
    background: #084298; /* Darker blue on hover */
  }
  
  .newsletter-section small {
    font-size: 0.85rem;
  }
  

Mock Data

Populate it with sample data and methods for managing the cart.

Create a service to handle food items and cart operations:

Generate the Service

ng generate service services/food

Service Implementation (food.service.ts)

import { Injectable } from '@angular/core';

interface FoodItem {
  id: number;
  name: string;
  description: string;
  price: number;
  image: string;
  quantity?: number; // Optional for cart purposes
}

@Injectable({
  providedIn: 'root',
})
export class FoodService {
  private foodItems: FoodItem[] = [
    { id: 1, name: 'Pizza', description: 'Cheesy delight', price: 10, image: 'assets/images/pizza.jpg' },
    { id: 2, name: 'Burger', description: 'Juicy and tasty', price: 8, image: 'assets/images/burger.jpg' },
    { id: 3, name: 'Pasta', description: 'Italian classic', price: 12, image: 'assets/images/pasta.jpg' },
  ];

  private cart: FoodItem[] = [];

  getFoodItems(): FoodItem[] {
    return this.foodItems;
  }

  getFoodItemById(id: number): FoodItem | undefined {
    return this.foodItems.find(item => item.id === id);
  }

  getCartItems(): FoodItem[] {
    return this.cart;
  }

  addToCart(item: FoodItem): void {
    const cartItem = this.cart.find(cartItem => cartItem.id === item.id);
    if (cartItem) {
      cartItem.quantity! += 1;
    } else {
      this.cart.push({ ...item, quantity: 1 });
    }
  }

  clearCart(): void {
    this.cart = [];
  }
}

Food Item Display

Home Component (home.component.ts)

import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';
import { FoodService } from '../../services/food.service';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [RouterLink, CommonModule],
  templateUrl: './home.component.html',
  styleUrl: './home.component.css'
})
export class HomeComponent {
  foodItems:any;

  constructor(private foodService: FoodService) {}

  ngOnInit(): void {
    this.foodItems = this.foodService.getFoodItems();
  }
}

Single Item Page

Component (single-item.component.ts)

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { FoodService } from '../../services/food.service';

@Component({
  selector: 'app-single-item',
  templateUrl: './single-item.component.html',
  styleUrls: ['./single-item.component.scss'],
})
export class SingleItemComponent implements OnInit {
  item: any;

  constructor(
    private route: ActivatedRoute,
    private foodService: FoodService
  ) {}

  ngOnInit(): void {
    const itemId = Number(this.route.snapshot.paramMap.get('id'));
    this.item = this.foodService.getFoodItemById(itemId);
  }

  addToCart(): void {
    this.foodService.addToCart(this.item);
    alert(`${this.item.name} added to cart!`);
  }
}

Cart Page

Component (cart.component.ts)

import { Component } from '@angular/core';
import { FoodService } from '../../services/food.service';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-cart',
  standalone: true,
  imports: [CommonModule, RouterLink],
  templateUrl: './cart.component.html',
  styleUrl: './cart.component.css'
})
export class CartComponent {
  cartItems:any;

  constructor(private foodService: FoodService) {}

  ngOnInit(): void {
    this.cartItems = this.foodService.getCartItems();
  }
}

Checkout Page

Component (checkout.component.ts)

import { Component } from '@angular/core';
import { FoodService } from '../../services/food.service';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-checkout',
  standalone: true,
  imports: [],
  templateUrl: './checkout.component.html',
  styleUrl: './checkout.component.css'
})
export class CheckoutComponent {
  constructor(private foodService: FoodService) {}

  placeOrder(): void {
    alert('Order placed successfully!');
    this.foodService.clearCart();
  }
}

This should now be a complete static food ordering app with Angular 19 and Bootstrap 5. If you’d like further refinements or additions, let me know and feel free to comment below!

Ajay

Thanks