Dynamic component
Dynamic component
جدول المصطلحات والملاحظات
المقدمة
ما هي المكونات الديناميكية؟
Dynamic Components في Angular هي مكونات لا تكون موجودة في القالب (Template) من البداية، بل يتم إنشاؤها برمجياً أثناء تشغيل التطبيق. تخيل أنك تبني تطبيقاً يمكن للمستخدم إضافة "قطع" (Widgets) إلى لوحة التحكم حسب رغبته - هنا تكمن قوة المكونات الديناميكية.
الفرق الجوهري
لماذا نستخدمها؟
تحسين الأداء (Performance Optimization)
تحميل المكونات فقط عند الحاجة (Lazy Loading)
تقليل حجم التطبيق الأولي
المرونة (Flexibility)
واجهات مستخدم قابلة للتخصيص
سلوك ديناميكي بناءً على البيانات
فصل المسؤوليات (Separation of Concerns)
فصل منطق إنشاء المكونات عن منطق العرض
المكونات الأساسية المطلوبة
1. ViewContainerRef - الحاوية الذكية
typescript
// هذا هو المكان الذي سنضيف فيه المكونات الجديدة
@ViewChild('container', { read: ViewContainerRef })
container: ViewContainerRef;
الدور: مثل "صندوق الإضافة" الذي تستطيع وضع المكونات الجديدة داخله.
2. ComponentFactoryResolver - المنشئ
typescript
// في Angular < 13، نحتاج هذه الخدمة
constructor(private resolver: ComponentFactoryResolver) {}
الدور: يعطينا "قالب الإنشاء" للمكون الذي نريده.
3. ComponentRef - جواز السفر
typescript
// بعد الإنشاء، نحصل على هذا المرجع للتحكم بالمكون
const componentRef = container.createComponent(factory);
الدور: يمثل المكون المنشأ ويمكننا من:
تمرير البيانات إليه (componentRef.instance.property = value)
الاستماع لأحداثه (componentRef.instance.event.subscribe())
تدميره عند الانتهاء (componentRef.destroy())
حالات عملية من الواقع
الحالة 1: نظام التنبيهات (Notification System)
typescript
// عندما يحدث خطأ
showError(message: string) {
// 1. إنشاء المكون ديناميكياً
const alertRef = this.createDynamicComponent(AlertComponent);
// 2. تمرير البيانات
alertRef.instance.message = message;
alertRef.instance.type = 'error';
// 3. إغلاق تلقائي بعد 5 ثوان
setTimeout(() => alertRef.destroy(), 5000);
}
الحالة 2: مشغل الوسائط الديناميكي
typescript
// حسب نوع الملف
playMedia(fileType: string) {
let playerComponent;
if (fileType === 'video') {
playerComponent = VideoPlayerComponent;
} else if (fileType === 'audio') {
playerComponent = AudioPlayerComponent;
} else {
playerComponent = ImageViewerComponent;
}
// إنشاء المشغل المناسب ديناميكياً
this.createDynamicComponent(playerComponent);
}
الحالة 3: لوحة تحكم قابلة للتخصيص
المستخدم يختار الـ Widgets المفضلة، والتطبيق ينشئها ديناميكياً عند كل زيارة.
المزايا الرئيسية
1. تحسين أداء التحميل (Load Performance)
مقارنة:
التطبيق التقليدي: يحمل كل المكونات مع التطبيق
التطبيق الديناميكي: يحمل المكونات عند الحاجة فقط
2. مرونة التصميم (Design Flexibility)
typescript
// يمكن تغيير المكون حسب دور المستخدم
if (user.isAdmin) {
this.loadComponent(AdminPanelComponent);
} else {
this.loadComponent(UserPanelComponent);
}
3. إدارة ذكية للذاكرة (Smart Memory Management)
المكون يُحمّل → يستخدم الذاكرة
المكون يُدمر → يحرر الذاكرة
لا تسرب ذاكرة (No Memory Leaks)
التحديات والحلول
التحدي 1: إدارة الذاكرة (Memory Management)
المشكلة: نسيان تدمير المكونات → تسرب ذاكرة
typescript
// ❌ خطأ شائع
createComponent() {
const ref = this.container.createComponent(MyComponent);
// نسيان تخزين المرجع للتدمير لاحقاً
}
// ✅ الحل الصحيح
private componentRefs: ComponentRef<any>[] = [];
createComponent() {
const ref = this.container.createComponent(MyComponent);
this.componentRefs.push(ref); // حفظ المرجع
}
destroyAll() {
this.componentRefs.forEach(ref => ref.destroy());
this.componentRefs = [];
}
التحدي 2: تمرير البيانات (Data Passing)
typescript
// ✅ الطريقة الصحيحة
const ref = this.container.createComponent(UserCardComponent);
ref.instance.user = currentUser; // Input
ref.instance.onDelete.subscribe(() => { // Output
this.deleteUser();
});
التحدي 3: اكتشاف التغيرات (Change Detection)
typescript
// عند تعديل البيانات بعد الإنشاء
ref.instance.data = newData;
ref.changeDetectorRef.detectChanges(); // 🔄 إعادة الاكتشاف
متى نختار المكونات الديناميكية؟
مقارنة سريعة
قاعدة 80/20
80% من الحالات: *ngIf أو *ngSwitch تكفي
20% من الحالات: تحتاج Dynamic Components حقاً
مثال حي من تطبيق بنكي
سيناريو: تطبيق بنك إلكتروني
typescript
// بناء الواجهة حسب نوع العملية
loadTransactionInterface(transactionType: string) {
switch(transactionType) {
case 'transfer':
this.loadComponent(MoneyTransferComponent);
break;
case 'investment':
this.loadComponent(InvestmentComponent);
break;
case 'loan':
this.loadComponent(LoanApplicationComponent);
break;
default:
this.loadComponent(DefaultTransactionComponent);
}
}
النتيجة:
واجهة خفيفة وسريعة
كل مستخدم يحصل على الواجهة المناسبة لعمليته
ذاكرة مُدارة بكفاءة
نصائح عملية للمطورين
للبدء (For Beginners)
ابدأ بمشروع تجريبي صغير
استخدم Dynamic Components لشيء بسيط أولاً (مثل Notification)
تأكد من فهم إدارة الذاكرة قبل التوسع
للمتقدمين (For Advanced)
فكر في Design Patterns مثل Factory Pattern
استخدم Service لحفظ وإدارة المراجع
اعمل على نظام Module Loading متقدم
للأداء (For Performance)
استخدم Lazy Loading مع Dynamic Components
ضع حداً أقصى للمكونات النشطة
طبق Virtual Scrolling إذا كان هناك العديد من المكونات
الخلاصة
المكونات الديناميكية (Dynamic Components) هي أداة متقدمة تمنحك تحكماً دقيقاً في:
أداء التطبيق - عن طريق التحميل عند الطلب
تجربة المستخدم - عن طريق واجهات مخصصة
إدارة الموارد - عن طريق تحرير الذاكرة غير المستخدمة
التحدي: تحتاج إلى فهم عميق لإدارة دورة حياة المكونات (Component Lifecycle).
ليست هناك تعليقات: