Skip to content

Modern multi-step registration wizard built with Angular 20 featuring Signals, standalone components, ControlValueAccessor, IndexedDB persistence, reusable form controls, mock API support, form validation, file upload, and i18n.

License

Notifications You must be signed in to change notification settings

MehdiHadizadeh/stepper-angular

Repository files navigation

Angular 20 Registration Wizard

A production-ready, multi-step registration form demonstrating modern Angular architecture, clean code principles, and enterprise-level patterns.

Angular TypeScript TailwindCSS License: MIT


📋 Project Overview

This project implements a production-ready 4-step registration wizard built with Angular 20 and modern frontend architecture principles. It focuses on scalability, maintainability, and real-world use cases, featuring state persistence, robust form validation, internationalization, and a flexible, configuration-driven design.

✨ Key Features

Core Functionality

  • Dynamic Step Configuration - Add/remove/reorder steps through configuration
  • State Persistence - Automatic IndexedDB save/restore with fallback
  • Mock API Support - Complete mock services with realistic delays
  • Form Validation - Real-time validation with custom validators
  • File Upload - Drag & drop with preview and validation
  • Internationalization - Persian/English support with ngx-translate
  • Responsive Design - Mobile-first approach with TailwindCSS

Advanced Features

  • ControlValueAccessor Pattern - Reusable form components
  • OnPush Change Detection - Optimized performance
  • Signals & Computed - Modern reactive state management
  • Dynamic Component Loading - Configuration-driven step rendering
  • Error Handling - Centralized FormErrorService with i18n
  • Navigation Guards - Smart step validation and navigation rules

🚀 Quick Start

Prerequisites

  • Node.js 18.x or higher
  • npm 9.x or higher

Installation & Running

# Clone repository git clone https://github.com/yourusername/angular-registration-wizard.git cd angular-registration-wizard # Install dependencies npm install # Run with mock API (default) ng serve # Run with real API # 1. Update src/environments/environment.ts # 2. Set useMockData: false # 3. Configure apiUrl ng serve # Open http://localhost:4200

Build for Production

# Production build ng build --configuration production # Output in dist/ folder # Deploy to Netlify, Vercel, or any static hosting

🎯 Architecture & Implementation

1. Dynamic Step Configuration System

Problem Solved: Adding/removing/reordering steps required changes across multiple files (enum, template @switch, service validations).

Solution: Single configuration file with dynamic component loading.

// StepRegistryService - Single source of truth private readonly stepConfigs: StepConfig[] = [ { id: 'personal-info', order: 0, title: 'STEPS.PERSONAL_INFO', component: PersonalInfoComponent, isRequired: true, canNavigateBack: false }, // Add new step - just add to array! { id: 'payment-info', order: 2, title: 'STEPS.PAYMENT', component: PaymentInfoComponent, isRequired: true, canNavigateBack: true } ];

Benefits:

  • No template modifications needed
  • Type-safe with StepConfig interface
  • Easy to maintain and scale
  • Single responsibility principle

2. ControlValueAccessor Pattern

All form controls implement Angular's ControlValueAccessor for seamless Reactive Forms integration:

<app-text-input formControlName="firstName" label="First Name" [required]="true" />

Features:

  • Works seamlessly with Reactive Forms
  • Automatic validation integration
  • Consistent error display with i18n
  • OnPush change detection optimized
  • Reusable across projects

3. State Persistence Strategy

// Automatic save on every change updatePersonalInfo()  Signal Update  Effect  IndexedDB // Automatic restore on page load APP_INITIALIZER  loadInitialState()  Restore from IndexedDB

Edge Cases Handled:

  • Browser doesn't support IndexedDB (graceful fallback)
  • Quota exceeded errors (clear old data)
  • Corrupted data recovery (reset to defaults)
  • Race conditions (debounced saves)

4. Mock API Architecture

Toggle Between Mock and Real:

// environment.ts export const environment = { production: false, useMockData: true // false for production }; // app.config.ts - Conditional provider { provide: LocationHttpService, useClass: environment.useMockData ? LocationMockHttpService : LocationHttpService }

Benefits:

  • Zero backend dependency
  • Realistic network delays (300ms simulated)
  • Same interface as real API
  • Easy testing and demos

5. Form Validation Strategy

Validator Implementation Use Case
Persian Text Custom regex /^[\u0600-\u06FF\s]+$/ Names, addresses
National ID Checksum algorithm (Luhn) Iranian 10-digit ID
No Whitespace Custom validator Prevents only spaces
File Type MIME type check JPG/PNG validation
File Size Byte comparison Max 5MB

6. Internationalization

// Switch language dynamically this.translate.use('en'); // or 'fa' // In templates {{ 'ERRORS.REQUIRED' | translate }} {{ 'ERRORS.MINLENGTH' | translate: {requiredLength: 5} }}

Structure:

public/i18n/ ├── fa.json # Persian (default) └── en.json # English 

🎨 Design Decisions & Rationale

Why IndexedDB over LocalStorage?

  • Capacity: 50MB+ vs 5MB
  • Structure: Stores objects directly (no JSON parsing)
  • Performance: Async, non-blocking operations
  • Future-proof: Can handle large files if needed

Why Signals over RxJS?

  • Simpler: Easier to understand and maintain
  • Performance: Fine-grained reactivity, better change detection
  • Modern: Angular's recommended approach
  • Note: RxJS still used for HTTP (appropriate use case)

Why ControlValueAccessor?

  • Consistency: All form controls work identically
  • Reusability: Use across multiple forms/projects
  • Integration: Built-in Reactive Forms support
  • Type Safety: Full TypeScript integration

Why OnPush Everywhere?

  • Performance: 50-90% fewer change detection cycles
  • Best Practice: Modern Angular default strategy
  • Scalability: Critical for large applications
  • Explicit: Forces intentional state updates

🔧 Customization Guide

Add a New Step

// 1. Create component ng g c features/registration-stepper/components/payment-info // 2. Implement IStepForm interface export class PaymentInfoComponent implements IStepForm { isValid(): boolean { return this.form.valid; } } // 3. Add to StepRegistryService { id: 'payment-info', order: 2, // Insert at desired position title: 'STEPS.PAYMENT', component: PaymentInfoComponent, isRequired: true, canNavigateBack: true } // 4. Add translations // public/i18n/fa.json "STEPS": { "PAYMENT": "اطلاعات پرداخت" } // public/i18n/en.json "STEPS": { "PAYMENT": "Payment Information" }

That's it! No template changes, no enum updates, no additional logic.

Add Custom Validator

// src/app/core/validators/custom.validator.ts export function emailDomainValidator(allowedDomain: string): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { if (!control.value) return null; const email = control.value.toLowerCase(); const isValid = email.endsWith(`@${allowedDomain}`); return isValid ? null : { emailDomain: { allowedDomain } }; }; } // Add translation // public/i18n/fa.json "ERRORS": { "EMAILDOMAIN": "ایمیل باید از دامنه {{allowedDomain}} باشد" } // Usage in form this.form = this.fb.group({ email: ['', [Validators.email, emailDomainValidator('company.com')]] });

Change Theme Colors

// tailwind.config.js module.exports = { theme: { extend: { colors: { primary: { 50: '#eff6ff', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', } } } } }

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: Add amazing feature')
  4. Push to branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Code Style

  • Follow Angular Style Guide
  • Use TypeScript strict mode
  • Write meaningful commit messages

📄 License

This project is licensed under the MIT License - see LICENSE file for details.

📬 Contact

Mehdi Hadizadeh


If you found this project helpful, please give it a star!

About

Modern multi-step registration wizard built with Angular 20 featuring Signals, standalone components, ControlValueAccessor, IndexedDB persistence, reusable form controls, mock API support, form validation, file upload, and i18n.

Topics

Resources

License

Stars

Watchers

Forks

Languages