🏗️ مشروع Angular عملي: بناء تطبيق قائمة مهام (Todo App) كامل
تقدمك في المستوى الرابع91%
📋 لماذا نبني مشروع عملي؟
في الدرس السابق تعلمنا النماذج (Forms) في Angular. دلوقتي نجمع كل اللي تعلمناه في مشروع عملي كامل: تطبيق قائمة مهام (Todo App). المشروع ده هيجمع: Components, Services, Dependency Injection, Forms, و Directives - كلهم في تطبيق واحد مترابط.
المشروع هيكون بسيط لكن كامل: تقدر تضيف مهمة، تسميها منجزة، تحذفها، وتشوف إحصائيات. ده نفس المفهوم اللي بنيناه في React في الدرس 5، لكن دلوقتي بنستخدم Angular عشان تشوف الفرق بين الإطارين.
بنهاية هذا الدرس، هتكون قادر على:
بناء تطبيق Angular كامل من الصفر باستخدام CLI
تنظيم المشروع إلى Components و Services منفصلة
استخدام Reactive Forms لإدخال البيانات
تطبيق التحقق من الصحة (Validation) على النماذج
استخدام Directives (ngFor, ngIf, ngClass) في Template
حفظ البيانات في localStorage عشان ماتضيعش
🎯 هدف المشروع: بناء Todo App يحتوي على: إضافة مهمة، تحديد كمنجزة، حذف مهمة، فلترة (الكل/المنجزة/المتبقية)، وحفظ في localStorage.
⚡ إعداد المشروع
أول خطوة: ننشئ مشروع Angular جديد ونضيف المكونات اللي محتاجينها.
الخطوة 1: إنشاء المشروع
# إنشاء مشروع جديد
ng new angular-todo-app --routing --style=css
cd angular-todo-app
# تشغيل السيرفر
ng serve
الخطوة 2: إنشاء المكونات
# إنشاء المكونات
ng g c components/todo-list
ng g c components/todo-item
ng g c components/todo-form
ng g c components/todo-stats
# إنشاء الـ Service
ng g s services/todo
# إنشاء الـ Interface
ng g interface models/todo
الخطوة 3: هيكل المشروع النهائي
src/app/
├── components/
│ ├── todo-list/ // قائمة المهام الرئيسية
│ ├── todo-item/ // عنصر مهمة واحدة
│ ├── todo-form/ // نموذج إضافة مهمة
│ └── todo-stats/ // إحصائيات المهام
├── services/
│ └── todo.service.ts // إدارة البيانات
├── models/
│ └── todo.ts // تعريف نوع البيانات
├── app.component.html
├── app.component.ts
└── app.module.ts
📦 الخطوة 4: تعريف نوع البيانات (Interface)
أول حاجة: نعرف شكل بيانات المهمة. ده بيساعد TypeScript يكتشف الأخطاء قبل ما التطبيق يشتغل.
// models/todo.ts - تعريف شكل بيانات المهمةexport interfaceTodo {
id: number; // معرف فريدtitle: string; // عنوان المهمةcompleted: boolean; // هل تمت؟createdAt: Date; // تاريخ الإنشاءpriority: 'low' | 'medium' | 'high'; // الأولوية
}
⚙️ الخطوة 5: إنشاء TodoService - إدارة البيانات
الـ Service هو "عقل" التطبيق. هنا بنخزن المهام، بنضيف، بنحذف، ونحفظ في localStorage.
// services/todo.service.tsimport { Injectable } from'@angular/core';
import { BehaviorSubject, Observable } from'rxjs';
import { Todo } from'../models/todo';
@Injectable({ providedIn: 'root' })
export classTodoService {
// ✅ BehaviorSubject: بيحفظ آخر قيمة ويبعتها لأي مشترك جديدprivatetodosSubject = newBehaviorSubject<Todo[]>([]);
todos$: Observable<Todo[]> = this.todosSubject.asObservable();
privatefilterSubject = newBehaviorSubject<string>('all');
filter$: Observable<string> = this.filterSubject.asObservable();
privatereadonlySTORAGE_KEY = 'angular_todo_app';
constructor() {
// تحميل المهام من localStorage لما الـ Service يتنشئthis.loadFromStorage();
}
// ➕ إضافة مهمة جديدةaddTodo(title: string, priority: 'low' | 'medium' | 'high' = 'medium') {
const newTodo: Todo = {
id: Date.now(),
title: title.trim(),
completed: false,
createdAt: newDate(),
priority: priority
};
const currentTodos = this.todosSubject.getValue();
this.todosSubject.next([newTodo, ...currentTodos]);
this.saveToStorage();
}
// ✅ تبديل حالة المهمة (منجزة/غير منجزة)toggleTodo(id: number) {
const todos = this.todosSubject.getValue().map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
this.todosSubject.next(todos);
this.saveToStorage();
}
// 🗑️ حذف مهمةdeleteTodo(id: number) {
const todos = this.todosSubject.getValue().filter(todo => todo.id !== id);
this.todosSubject.next(todos);
this.saveToStorage();
}
// 🧹 حذف كل المهام المنجزةclearCompleted() {
const todos = this.todosSubject.getValue().filter(todo => !todo.completed);
this.todosSubject.next(todos);
this.saveToStorage();
}
// 🔍 تغيير الفلترsetFilter(filter: 'all' | 'active' | 'completed') {
this.filterSubject.next(filter);
}
// 💾 حفظ في localStorageprivatesaveToStorage() {
const todos = this.todosSubject.getValue();
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(todos));
}
// 📂 تحميل من localStorageprivateloadFromStorage() {
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
const todos = JSON.parse(stored).map((todo: any) => ({
...todo,
createdAt: newDate(todo.createdAt)
}));
this.todosSubject.next(todos);
}
}
}
📌 ليه استخدمنا BehaviorSubject مش Array عادي؟
BehaviorSubject بيسمح لأي مكون يشترك في المهام ويتحدث تلقائياً لما المهام تتغير. ده نفس مفهوم Redux أو Context API في React. لو استخدمنا Array عادي، المكونات مش هتعرف إن البيانات اتغيرت.
BehaviorSubject بيحفظ آخر قيمة ويبعتها لأي مشترك جديد. يعني لو مكون اشترك بعد ما البيانات اتحملت، هياخد آخر قيمة على طول. Subject عادي مش بيعمل كده - المشترك الجديد هيفضل مستنى الحدث الجاي.
هل localStorage آمن لحفظ البيانات؟
لا للبيانات الحساسة (كلمات مرور، بيانات بنكية). أي حد يقدر يفتح DevTools ويقرأ localStorage. لكن للبيانات العامة زي قائمة المهام، هو كافي. للبيانات الحساسة، استخدم Backend + Database.
إزاي أضيف Backend حقيقي للمشروع ده؟
استخدم Node.js + Express + MongoDB. غير الـ TodoService عشان يستخدم HttpClient بدل localStorage. أضف Endpoints: GET /todos, POST /todos, PUT /todos/:id, DELETE /todos/:id. المشروع هيبقى Full Stack حقيقي!