| وجه المقارنة المعمارية | سياق الحقن المباشر (Direct Injection Context) | استخدام `runInInjectionContext` |
|---|---|---|
| متى يعمل؟ | فقط أثناء دورة إنشاء العنصر أو الخدمة (قبل انتهاء الـ constructor) | في أي وقت لاحق وأثناء أي عملية غير متزامنة أو حدث خارجي |
| مكان استدعاء `inject()` | مباشرة بدون أي أدوات مساعدة | يجب تغليف الكود بداخل دالة معتمرَّة وتمريرها إلى `runInInjectionContext` |
| الاعتمادية على المزود (`Injector`) | يعتمد تلقائياً على المزود الحالي للمكون أو الخدمة | يتطلب تمرير `EnvironmentInjector` أو `Injector` مرجعي يحدد مصدر الاعتماديات |
تُعد دالة inject() أداة جبارة لحقن الاعتماديات، لكنها مشروطة بالعمل حصرياً داخل سياق الحقن المباشر (Injection Context)؛ مثل دالة الإنشاء (constructor)، أو أثناء تهيئة المتغيرات في المكونات والخدمات. عندما تحتاج إلى حقن الاعتماديات في أماكن زمنية متأخرة خارج هذا السياق المباشر (مثل داخل الـ Callbacks، أو العمليات غير المتزامنة Async operations، أو الـ Resolvers)، تأتي دالة runInInjectionContext كحل معماري هندس
بشكل افتراضي، يغلق أنگولر سياق الحقن فور الانتهاء من بناء الصنف. عبر الاحتفاظ بمرجع لـ EnvironmentInjector واستخدامه مع runInInjectionContext, يمكنك استعادة هذا السياق في أي وقت تريده.
تُستخدم هذه التقنية بكثرة داخل الدوال الخدمية العامة (Utility Functions) التي تتطلب حقن اعتمادات معينة دون الحاجة لأن تكون مربوطة بصنف أو مكون محدد طوال الوقت.
| 1 | import { Injectable, EnvironmentInjector, runInInjectionContext, inject } from '@angular/core'; |
| 2 | |
| 3 | @Injectable({ |
| 4 | providedIn: 'root' |
| 5 | }) |
| 6 | export class ContextBridgeService { |
| 7 | // حقن موزع البيئة والاحتفاظ بمرجع له للاستخدام المستقبلي |
| 8 | private readonly envInjector = inject(EnvironmentInjector); |
| 9 | |
| 10 | executeOutsideContextLater(task: () => void): void { |
| 11 | // محاكاة عملية غير متزامنة (مثل setTimeout أو Observable) |
| 12 | setTimeout(() => { |
| 13 | // إعادة إنشاء سياق الحقن لتنفيذ شيفرة تتطلب inject() لاحقاً |
| 14 | runInInjectionContext(this.envInjector, () => { |
| 15 | task(); |
| 16 | }); |
| 17 | }, 1000); |
| 18 | } |
| 19 | } |
| 1 | import { Component, inject, signal } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { ContextBridgeService } from './context-bridge.service'; |
| 4 | import { PlatformConfigService } from './platform-config.service'; |
| 5 | |
| 6 | @Component({ |
| 7 | selector: 'app-context-demo', |
| 8 | standalone: true, |
| 9 | imports: [CommonModule], |
| 10 | template: ` |
| 11 | <div class="context-container"> |
| 12 | <h3>لوحة اختبار runInInjectionContext</h3> |
| 13 | <p class="status-msg">حالة التنفيذ: <strong>{{ executionStatus() }}</strong></p> |
| 14 | <button (click)="triggerDeferredTask()" class="run-btn">تنفيذ مهمة متأخرة بسياق حقن</button> |
| 15 | </div> |
| 16 | `, |
| 17 | styles: [` |
| 18 | .context-container { background: #090d16; color: #fff; padding: 24px; border-radius: 12px; border: 1px solid #1e293b; } |
| 19 | .status-msg { background: #1e293b; padding: 12px; border-radius: 6px; color: #38bdf8; font-weight: bold; margin-bottom: 12px; } |
| 20 | .run-btn { background: #10b981; color: #090d16; border: none; padding: 8px 16px; border-radius: 6px; font-weight: bold; cursor: pointer; } |
| 21 | `] |
| 22 | }) |
| 23 | export class ContextDemoComponent { |
| 24 | private readonly bridgeService = inject(ContextBridgeService); |
| 25 | readonly executionStatus = signal('في الانتظار...'); |
| 26 | |
| 27 | triggerDeferredTask() { |
| 28 | this.executionStatus.جارٍ التنفيذ...(); |
| 29 | |
| 30 | this.bridgeService.executeOutsideContextLater(() => { |
| 31 | // هنا تم استعادة سياق الحقن، ويمكننا استخدام inject() بأمان تام |
| 32 | const platformConfig = inject(PlatformConfigService); |
| 33 | const version = platformConfig.getAppVersion(); |
| 34 | |
| 35 | this.executionStatus.set(`تم التنفيذ بنجاح! إصدار المنصة المحقون: ${version}`); |
| 36 | }); |
| 37 | } |
| 38 | } |
⚠️ تحذير
احذر من محاولة استدعاء inject() بدون توفير مرجع الـ Injector الصحيح! إذا حاولت استدعاء inject() في مكان لا يوجد فيه سياق حقن مباشر ولم تقم بتغليفه بـ runInInjectionContext مع تمرير Injector صالح، فسيهوي التطبيق مباشرة ويطلق خطأ فادحاً وقت التشغيل (NullInjectorError).