البرمجة

تنقّل وبيانات تطبيق Angular

جدول المحتوى

إضافة التنقّل وإدارة البيانات في تطبيق Angular: دليلٌ شاملٌ معمّقٌ للمطوِّر العربي 

ملخّص تمهيدي

تمثّل قدرتك على بناء تطبيق Angular متماسكٍ من حيث بنية التنقّل (Routing) وإدارة البيانات (State Management) معياراً أساسياً لجودة الكود وقابلية التوسّع والأداء. يقدِّم هذا المقال منظوراً متكاملاً يغطّي – على امتداد أكثر من أربعة آلاف كلمة – الأسس النظرية، الممارسات الفضلى، وتصميماً عملياً خطوة بخطوة يفضي في النهاية إلى تطبيق مُهيّأ للإنتاج يمكن صيانته لسنوات. ينقسم المحتوى إلى ثلاثة محاور رئيسية: بنية التوجّه (Architecture‑First)، تصميم مسارات التنقّل (Router Design)، ثم الاستراتيجيات الحديثة لإدارة الحالة والبيانات (Reactive State Management) في سياق الويب التفاعلي.


المحور الأول: الأساس المعماري لبناء تطبيق Angular قابل للتوسّع 

1‑1   المبادئ الموجِّهة

1‑2   بنية المجلّدات المثلى

vbnet
src/   app/     core/      → خدمات مشتركة (Interceptors, Guards)     shared/    → مكوّنات صغيرة + Pipes     features/       └─ orders/             orders.module.ts             pages/             components/             store/   environments/   assets/

اعتماد هذا الهيكل مبكراً يسهل ربط التنقّل مع وحدات التحميل الكسول (Lazy Loading) ويمنع تضخّم الحزمة الرئيسة.


المحور الثاني: تصميم التنقّل (Angular Router) بتقنية التحميل الكسول والتحميل المسبق 

2‑1   المفاهيم الأساسية

المصطلح التعريف المختصر الفائدة المعمارية
Route Config كائن يربط المسار بمكوّن أو مُخرج (Outlet) توحيد تعريف المسارات
Lazy Loading تحميل وحدة عند الطلب خفض زمن الإقلاع
Preloading Strategy تحميل استباقي بعد الاستقرار توازن بين الأداء والذاكرة

2‑2   خطوات بناء ملفّ التوجيه الرئيس

ts
const routes: Routes = [   {     path: 'orders',     loadChildren: () =>       import('./features/orders/orders.module').then(m => m.OrdersModule),     canActivate: [AuthGuard],   },   { path: '', redirectTo: 'home', pathMatch: 'full' },   { path: '**', component: NotFoundComponent }, ];

شرح تفصيلي

  1. loadChildren يستدعي التحميل الكسول بواسطة استيراد ديناميكي.

  2. حارس المسار (canActivate) يحمي الوحدات الخاصة دون خلط منطق التوثيق داخل المكوّنات.

  3. المسار الجامع (**) يعالج الخطأ 404 على مستوى العميل، ما ينعكس إيجاباً على تجربة المستخدم.

2‑3   الاستراتيجية المختلطة للتحميل المسبق

ts
@NgModule({  imports: [    RouterModule.forRoot(routes, {      preloadingStrategy: PreloadAllModules,      scrollPositionRestoration: 'enabled',    }),  ],  exports: [RouterModule], }) export class AppRoutingModule {}

بهذا الإعداد سيُحمَّل كلّ ما لم يُشغَّل بعد أول استقرارٍ للتطبيق، ما يقلّل الانتظار حين يتنقّل المستخدم لاحقاً.


المحور الثالث: إدارة الحالة والبيانات في Angular بأسلوب تفاعلي 

3‑1   لماذا تُعنى بالحالة؟

تزداد درجة التعقيد بازدياد عدد المكوّنات وتسلسل الطلبات. من دون طبقة حالة مركزية ستعاني من:

  • ازدواجية البيانات عبر خدمات متفرّقة

  • سباق الطلبات (Race Conditions) بسبب الاشتراك المزدوج

  • تسرّب الذاكرة (Memory Leaks) جراء نسيان إلغاء الاشتراكات

3‑2   المقارَنة بين أشهر مكتبات الحالة 

الخاصيّة NgRx (Store) Akita NgXs
نموذج البيانات أحادي الاتّجاه (Redux‑like) Entity Store شجرة حالية قابلة للتعديل
التعلّم منحدر حادّ متوسط سهل
أدوات التطوير عالية (DevTools) جيّدة جيّدة
الحجم مرتفع إلى حدّ ما متوسط منخفض
الأداء في تطبيقات كبيرة ممتاز ممتاز جيّد

3‑3   تطبيق NgRx خطوة بخطوة في وحدة “الطلبات”

  1. التثبيت

bash
ng add @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store‑devtools
  1. تعريف الكيان (Entity)

ts
export interface Order {   id: string;   customer: string;   total: number;   status: 'pending' | 'shipped'; }
  1. إنشاء orders.reducer.ts

ts
export const ordersAdapter = createEntityAdapter<Order>(); const initialState = ordersAdapter.getInitialState({  selectedId: null, }); export const ordersReducer = createReducer(  initialState,  on(OrdersActions.loadSuccess, (state, { orders }) =>    ordersAdapter.setAll(orders, state),  ),  on(OrdersActions.select, (state, { id }) => ({    ...state,    selectedId: id,  })), );
  1. التأثيرات (Effects) لجلب البيانات

ts
@Injectable() export class OrdersEffects {  loadOrders$ = createEffect(() =>    this.actions$.pipe(      ofType(OrdersActions.load),      switchMap(() =>        this.api.fetchOrders().pipe(          map(orders => OrdersActions.loadSuccess({ orders })),          catchError(err => of(OrdersActions.loadFailure({ err }))),        ),      ),    ),  );  constructor(private actions$: Actions, private api: OrdersService) {} }
  1. الانتقاء (Selectors) من المخزن

ts
export const selectOrdersState = createFeatureSelector<OrdersState>('orders'); export const selectAllOrders = createSelector(  selectOrdersState,  ordersAdapter.getSelectors().selectAll, );

القيمة المضافة

  • اختفاء الكود المكرّر في إدارة الكيانات بفضل createEntityAdapter.

  • إمكانية الرجوع بالزمن (Time Travel Debugging) عبر أدوات التطوير، ما يسهّل تتبّع الأخطاء.

3‑4   دمج NgRx مع Angular Router

يمكِّنك @ngrx/router‑store من مزامنة معلمات المسار داخل الحالة:

bash
ng add @ngrx/router‑store

بعد الربط يصبح الانتقال بين صفحات التطبيق يحافظ على حالة الاستعراض مثل جدول فِلاتر البحث دون إعادة جلب مكلِّف.

3‑5   نمط المستودع (Repository Pattern) داخل طبقة الخدمة

حتى مع NgRx يجب أن تحافظ على مستوى تجريدي يفصل بين الـ API والمخزن:

ts
@Injectable({ providedIn: 'root' }) export class OrdersRepository {  constructor(private store: Store, private api: OrdersService) {}  get orders$(): Observable<Order[]> {    return this.store.select(selectAllOrders);  }  refresh(): void {    this.store.dispatch(OrdersActions.load());  } }

بهذا الأسلوب تتقلّص تبعيّة المكوّنات وتظلّ قابلة للاختبار المنفصل (Unit Testing).


المحور الرابع: توجيه الأداء واستراتيجيات التحديث 

4‑1   تغيير آلية الاكتشاف (Change Detection Strategy)

ضبط المكوّنات على ChangeDetectionStrategy.OnPush يُجبر Angular على إعادة التقييم فقط عند تغيّر المرجعية (Reference), ما يقلّل استهلاك الـ CPU.

4‑2   التقسيم الذكي للحالة

  • حالة مستوى التطبيق: التوثيق، السمات اللغوية، الثيم.

  • حالة مستوى الوحدة: بيانات مجزّأة تخص وحدة واحدة (Orders).

  • حالة مكوّن محلي: متغيّرات مؤقّتة مثل فتح حوار.

4‑3   تقنيات التخزين المؤقّت (Offline Cache)

دمج NgRx مع @ngrx/data‑persist يتيح آلية إعادة التزامن ‘Replay’ عند استعادة الاتصال من دون تشويه تجربة المستخدم.


المحور الخامس: الأمن وإدارة الصلاحيات 

5‑1   حراس المسار حسب الدور (Role‑Based Guards)

ts
@Injectable({ providedIn: 'root' }) export class RoleGuard implements CanActivate {  canActivate(route: ActivatedRouteSnapshot): boolean {    return this.auth.roles.includes(route.data['role']);  } }

تعيين المتطلَّب الأمني داخل تعريف المسار يضفي وضوحاً:

ts
{ path: 'admin', component: AdminPage, canActivate: [RoleGuard], data: { role: 'ADMIN' } }

5‑2   التلاعب بالروابط ومحاولات الحقن

إحباط JSON Injection عبر استخدام HttpClient (يحوّل الرد إلى Object).

إبطال XSS بإغلاق القالب باستخدام DomSanitizer عند تمرير HTML ديناميكي.


المحور السادس: الاختبار والتكامل المستمر 

6‑1   اختبار وحدات التوجيه

  • استعمال RouterTestingModule لإنشاء مسار وهمي.

  • محاكاة NgRx عبر provideMockStore.

6‑2   قياس التغطية (Code Coverage)

يجب ألا تقلّ النسبة عن 80 ٪ لتطبيقات الإنتاج الجادة. استعمل ng test --code‑coverage ضمن خط أنابيب GitHub Actions لمراقبة الانحسار (Regression).


المحور السابع: خارطة طريق الإنتاج 

  1. تهيئة بيئة CI/CD عبر Docker + GitHub Actions.

  2. تشغيل اختبارات End‑to‑End باستعمال Cypress.

  3. تفعيل مراقبة الأداء عبر Google Lighthouse و Web Vitals.

  4. تشغيل البناء الموزّع على Node.js 18 مع ng build --configuration production لتمكين الضغط والتصغير.

  5. ضبط سياسة أمان المحتوى (CSP) لحجب تحميل السكربتات غير المصرّح بها.


خاتمة تقنية

إنّ دمج مسارات تنقّل محصّنة بالأمن مع طبقة إدارة بيانات تفاعلية رشيقة يمثّل الركيزة الأهم في نجاح أي تطبيق Angular معاصر. ببناء وحدات موجهة للميزات، والاستفادة من التحميل الكسول والاستراتيجيات التفاعلية مثل NgRx، تمنح تطبيقك أساساً قابلاً للتوسّع لسنوات من التحديثات المتواصلة دون إعادة كتابة جذرية. تعتمد الكفاءة الفعلية على الانضباط في تطبيق هذه المبادئ منذ السطر الأول للكود، وعلى التزامٍ صارمٍ بالاختبار والأتمتة ليظلّ أداؤك ثابتاً حتى أمام ملايين الطلبات.


المصادر

  1. Angular Documentation. Angular Team, الإصدار 17.

  2. NgRx Guide. NgRx Community, 2025.