🧩 المكونات المتقدمة في Angular
في الدرس السابق تعلمنا أساسيات Angular: إنشاء المشروع، المكونات البسيطة، وأنواع الربط. في هذا الدرس، هنغوص أعمق في بناء المكونات المتقدمة وإدارة التواصل بينها. هنتعلم كيفية استخدام Decorators المختلفة مثل @Input و @Output لنقل البيانات بين المكونات، وكيفية استخدام دوال الحياة (Lifecycle Hooks) لتنفيذ كود في أوقات محددة من دورة حياة المكون.
بنهاية هذا الدرس، هتكون قادر على:
- إنشاء مكونات باستخدام Angular CLI بكفاءة
- نقل البيانات من المكون الأب للابن باستخدام
@Input - إرسال أحداث من الابن للأب باستخدام
@OutputوEventEmitter - فهم دورة حياة المكون واستخدام Lifecycle Hooks في الوقت المناسب
- تنظيف الموارد وإلغاء الاشتراكات لتجنب Memory Leaks
@Input) وبيبعت إشعارات للي فوقه (@Output). ده نفس مفهوم Props في React لكن بطريقة أكثر تنظيماً.
⚡ إنشاء Component باستخدام Angular CLI
Angular CLI مش بس بتسهل إنشاء المشاريع، لكنها بتولّد 4 ملفات جاهزة لكل مكون جديد. ده بيوفر عليك وقت كبير وبيضمن إن المكون متوافق مع معايير Angular.
الخطوة 1: تثبيت Angular CLI (لو مش مثبت)
الخطوة 2: إنشاء مكون جديد
ملاحظة مهمة: الأمر ng g c بيعمل 3 حاجات تلقائياً:
- ينشئ المجلد والملفات الأربعة
- يضيف المكون في
declarationsفيapp.module.ts - يضيف
importللمكون في الملفات اللي محتاجاه
ng g c name- مكون جديدng g s name- خدمة جديدة (Service)ng g m name- وحدة جديدة (Module)ng g p name- Pipe جديدng g d name- Directive جديدة
📥 @Input() - استقبال البيانات من المكون الأب
@Input() هو Decorator بيسمح للمكون الابن باستقبال بيانات من المكون الأب. ده مكافئ لـ Props في React، لكن الفرق إن Angular بتستخدم Decorator عشان تعلّم الخاصية إنها "مدخلة" من الخارج.
لما تستخدم @Input()، Angular بتعمل One-way Data Binding من الأب للابن. يعني لو البيانات اتغيرت في الأب، هتتغير في الابن تلقائياً. لكن العكس مش صحيح - الابن مش يقدر يعدل الـ Input مباشرة.
🔍 مثال عملي: بطاقة مستخدم (User Card)
🔍 الاستخدام في المكون الأب:
⚠️ خطأ شائع: نسيان الأقواس المربعة [ ]
غلط: name="user.name" - ده بيعتبرها نص عادي (String).
صح: [name]="user.name" - ده بيربطها بقيمة المتغير (Property Binding).
📤 @Output() - إرسال البيانات والأحداث للأب
@Output() هو Decorator بيسمح للمكون الابن بإرسال أحداث (Events) للمكون الأب. ده مكافئ لـ Callback Props في React (زي onClick أو onDelete).
Angular بتستخدم EventEmitter عشان تنشئ حدث مخصص. لما يحصل حاجة في الابن (زي نقر زر)، الابن بيستدعي .emit() والأب بيستقبل الحدث ويتعامل معاه.
🔍 مثال: زر مخصص يرسل حدث للأب
🔍 مثال متقدم: حذف مستخدم من القائمة
@Input= البيانات بتنزل من فوق (أب → ابن)@Output= الأحداث بتطلع لفوق (ابن → أب)
🔄 Lifecycle Hooks - دوال حياة المكون
كل مكون في Angular بيمر بـ دورة حياة محددة: الإنشاء → التهيئة → التحديثات → التدمير. Angular بتوفّر Interfaces تقدر تطبقها عشان "تتنصت" على مراحل معينة من الدورة.
🔍 ترتيب Lifecycle Hooks:
📋 جدول Lifecycle Hooks:
| الـ Hook | متى يشتغل؟ | الاستخدام الشائع |
|---|---|---|
ngOnChanges |
قبل ngOnInit + كل تغيير في Inputs | تتبع تغييرات Props، منطق عند تغيير بيانات |
ngOnInit |
مرة واحدة بعد الإنشاء | جلب بيانات API، اشتراك في Services |
ngDoCheck |
مع كل تغيير (يدوي) | كشف تغييرات مخصصة |
ngAfterContentInit |
بعد محتوى <ng-content> | تفاعل مع محتوى مُدرج |
ngAfterViewInit |
بعد رسم القالب كاملاً | تفاعل مع DOM، Charts، Libraries خارجية |
ngOnDestroy |
قبل حذف المكون | إلغاء الاشتراكات، تنظيف الموارد |
🔍 مثال عملي: جلب بيانات مع تنظيف
⚠️ خطأ قاتل: نسيان إلغاء الاشتراكات
لو استخدمت .subscribe() من غير takeUntil أو unsubscribe()، الـ Observable هيفضل شغال حتى بعد ما المكون يتحذف. ده بيسبب Memory Leak وبيأثر على أداء التطبيق.
الحل: استخدم takeUntil مع Subject زي المثال اللي فوق، أو استخدم async pipe في Template.
💡 نصائح ومعايير مهمة في Angular
- استخدم @Input لنقل البيانات لأسفل: البيانات بتنزل من الأب للابن، مش العكس.
- استخدم @Output لإرسال الأحداث لأعلى: الابن ما يعدلش State مباشرة، يرسل حدث والأب يعدل.
- استخدم ngOnInit لتهيئة البيانات: مش الـ Constructor. Constructor للـ DI بس.
- استخدم ngOnDestroy لتنظيف الموارد: إلغاء الاشتراكات، إيقاف المؤقتات، إغلاق الاتصالات.
- لا تضع منطق معقد في Constructor: Constructor بس للـ Dependency Injection. المنطق في ngOnInit.
- استخدم OnPush Change Detection للأداء: في المكونات اللي بتعتمد على @Input بس.
- فصّل المكونات لأصغر حجم ممكن: كل مكون مسؤول عن مهمة واحدة (Single Responsibility).
📚 دروس ذات صلة
❓ أسئلة شائعة عن Angular Components
ما الفرق بين Constructor و ngOnInit؟
Constructor بيشتغل لما Angular تنشئ instance من الClass. استخدمه بس للـ Dependency Injection (تمرير Services). ngOnInit بيشتغل بعد ما Angular تهيئ الـ Inputs. استخدمه للمنطق الفعلي زي جلب البيانات من API. السبب: في Constructor، الـ Inputs لسه مش جاهزة.
هل يمكن تمرير Object كامل بدل Properties منفصلة في @Input؟
نعم، ومستحسن كمان! بدل @Input() name; @Input() email;، استخدم @Input() user: User;. ده بيقلل عدد الـ Inputs وبيسهل الصيانة. بس تأكد إنك بتستخدم ChangeDetectionStrategy.OnPush لو الـ Object كبير.
ما الفرق بين EventEmitter و Subject في Angular؟
EventEmitter مخصص للـ @Output في المكونات. Subject جزء من RxJS ويستخدم في Services وإدارة الحالة. EventEmitter أبسط لكن Subject أكثر قوة ومرونة. في Angular 16+، EventEmitter أصبح wrapper حول Subject.
إزاي أتجنب Memory Leaks في Angular؟
3 طرق: (1) استخدم takeUntil مع Subject في ngOnDestroy، (2) استخدم async pipe في Template (بيُلغي الاشتراك تلقائياً)، (3) استخدم .unsubscribe() يدوياً في ngOnDestroy. الطريقة الأولى هي الأكثر شيوعاً.