شرح حقن التبعية الزاويّة بأمثلة

ما هو حقن التبعية؟

التحفيز

غالبًا ما يشار إلى حقن التبعية ببساطة باسم DI. النموذج موجود في جميع أنحاء Angular. إنها تحافظ على الكود مرنًا وقابلًا للاختبار وقابل للتغيير. يمكن للفئات أن ترث المنطق الخارجي دون معرفة كيفية إنشائه. لا يحتاج أي مستهلك لهذه الفئات أيضًا إلى معرفة أي شيء.

تحافظ DI على الفئات والمستهلكين على حد سواء من الاضطرار إلى معرفة أكثر من اللازم. ومع ذلك ، فإن الكود معياري كما كان من قبل بفضل الآليات التي تدعم DI في Angular.

الخدمات هي المستفيد الرئيسي من DI. يعتمدون على النموذج للحقن في مختلف المستهلكين. يمكن لهؤلاء المستهلكين بعد ذلك الاستفادة من تلك الخدمة المقدمة و / أو إعادة توجيهها إلى مكان آخر.

الخدمة ليست وحدها. التوجيهات والأنابيب والمكونات وما إلى ذلك: يستفيد كل تخطيطي في Angular من DI بطريقة أو بأخرى.

عن طريق الحقن

الحاقنات هي هياكل بيانات تخزن التعليمات التي توضح بالتفصيل مكان وكيفية تشكل الخدمات. إنهم يعملون كوسطاء داخل نظام Angular DI.

تحتوي فئات الوحدات النمطية والتوجيهية والمكونات على بيانات وصفية خاصة بالحاقنات. مثيل حاقن جديد يرافق كل فئة من هذه الفئات. بهذه الطريقة ، تعكس شجرة التطبيق تسلسلها الهرمي للحاقنات.

و providers: []الفوقية يقبل الخدمات التي تسجل ثم مع حاقن الفئة '. يضيف حقل الموفر هذا التعليمات اللازمة لعمل الحاقن. تقوم فئة (بافتراض أنها تحتوي على تبعيات) بإنشاء مثيل لخدمة من خلال أخذ فئتها كنوع بياناتها. يقوم الحاقن بمحاذاة هذا النوع بإنشاء مثيل لتلك الخدمة نيابة عن الفئة.

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

يمكن للخدمات التسجيل في أي حاقن داخل التطبيق. تدخل الخدمات في providers: []مجال البيانات الوصفية لوحدات الفصل أو التوجيهات أو المكونات. يمكن لأطفال الفصل إنشاء مثيل لخدمة مسجلة في حاقن الفئة. حاقن الأطفال يتراجعون عن الحقن الأبوي بعد كل شيء

حقن التبعية

ألق نظرة على الهياكل العظمية لكل فئة: الخدمة والوحدة والتوجيه والمكون.

// service import { Injectable } from '@angular/core'; @Injectable({ providedIn: /* injector goes here */ }) export class TemplateService { constructor() { } }
// module import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [], providers: [ /* services go here */ ] }) export class TemplateModule { }
// directive import { Directive } from '@angular/core'; @Directive({ selector: '[appTemplate]', providers: [ /* services go here */ ] }) export class TemplateDirective { constructor() { } }
//component import { Component } from '@angular/core'; @Component({ selector: 'app-template', templateUrl: './template.component.html', styleUrls: ['./template.component.css'], providers: [ /* services go here */ ] }) export class TemplateComponent { // class logic ... }

يمكن لكل هيكل عظمي تسجيل الخدمات للحاقن. في الحقيقة ، TemplateService هي خدمة. اعتبارًا من Angular 6 ، يمكن للخدمات الآن التسجيل مع الحاقنات باستخدام @Injectableالبيانات الوصفية.

على كل حال

لاحظ providedIn: string( @Injectable) و providers: []( @Directive، @Componetو @Module) الفوقية. يخبرون الحاقنين أين وكيف يتم إنشاء الخدمة. خلاف ذلك ، فإن المحاقن لن يعرف كيفية إنشاء مثيل.

ماذا لو كانت الخدمة لها تبعيات؟ أين ستذهب النتائج؟ يجيب مقدمو الخدمة على هذه الأسئلة حتى يتمكن المحقن من إنشاء مثيل لها بشكل صحيح.

تشكل الحاقنات العمود الفقري لإطار DI. يقومون بتخزين التعليمات لإنشاء الخدمات بحيث لا يضطر المستهلكون إلى ذلك. يتلقون مثيلات الخدمة دون الحاجة إلى معرفة أي شيء عن تبعية المصدر!

يجب أن أشير أيضًا إلى أن المخططات الأخرى التي لا تحتوي على محاقن لا يزال بإمكانها استخدام حقن التبعية. لا يمكنهم تسجيل خدمات إضافية ولكن لا يزال بإمكانهم إنشاء مثيل لها عن طريق الحقن.

الخدمات

و providedIn: stringالفوقية من @Injectableيحدد التي حاقن للتسجيل مع. باستخدام هذه الطريقة ، واعتمادًا على ما إذا تم استخدام الخدمة ، قد تسجل الخدمة أم لا مع الحاقن. الزاوي يدعو هذه الشجرة اهتزاز .

بشكل افتراضي ، يتم تعيين القيمة على ‘root’. هذا يترجم إلى حاقن الجذر للتطبيق. في الأساس ، تحديد المجال ‘root’لجعل الخدمة متاحة في أي مكان.

ملاحظة سريعة

كما ذكرنا سابقًا ، يتراجع حاقن الأطفال عن والديهم. تضمن هذه الإستراتيجية الاحتياطية عدم اضطرار الآباء إلى إعادة التسجيل لكل حاقن. راجع هذه المقالة حول الخدمات والحاقنات للحصول على توضيح لهذا المفهوم.

الخدمات المسجلة فردية . بمعنى ، توجد إرشادات إنشاء الخدمة على حاقن واحد فقط. هذا يفترض أنه لم يتم تسجيله صراحة في مكان آخر.

الوحدة النمطية والتوجيه والمكون

كل من الوحدات والمكونات لها مثيل حاقن خاص بها. هذا واضح بالنظر إلى providers: []مجال البيانات الوصفية. يأخذ هذا الحقل مجموعة من الخدمات ويسجلها مع حاقن الوحدة النمطية أو فئة المكون. يحدث هذا النهج في @NgModule، @Directiveأو @Componentالديكور.

تتجاهل هذه الاستراتيجية اهتزاز الشجرة أو الإزالة الاختيارية للخدمات غير المستخدمة من المحاقن. تعيش مثيلات الخدمة على أدوات الحقن الخاصة بها طوال عمر الوحدة النمطية أو المكون.

تجسيد المراجع

يمكن إنشاء مثيل للإشارات إلى DOM من أي فئة. ضع في اعتبارك أن المراجع لا تزال خدمات. إنها تختلف عن الخدمات التقليدية في تمثيل حالة شيء آخر. تتضمن هذه الخدمات وظائف للتفاعل مع مرجعها.

التوجيهات في حاجة دائمة لمراجع DOM. تقوم التوجيهات بإجراء طفرات على عناصرها المضيفة من خلال هذه المراجع. انظر المثال التالي. يقوم حاقن التوجيه بإنشاء مثيل مرجعي لعنصر المضيف في مُنشئ الفئة.

// directives/highlight.directive.ts import { Directive, ElementRef, Renderer2, Input } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor( private renderer: Renderer2, private host: ElementRef ) { } @Input() set appHighlight (color: string) { this.renderer.setStyle(this.host.nativeElement, 'background-color', color); } }
// app.component.html 

Highlighted Text!

Renderer2يحصل أيضًا على مثيل. من أي حاقن تأتي هذه الخدمات؟ حسنًا ، مصدر كل خدمة يأتي من @angular/core. يجب أن تسجل هذه الخدمات بعد ذلك مع حاقن جذر التطبيق.

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { HighlightDirective } from './directives/highlight.directive'; @NgModule({ declarations: [ AppComponent, HighlightDirective ], imports: [ BrowserModule ], providers: [], bootstrap: [ AppComponent ] }) export class AppModule { }

مجموعة موفري فارغة !؟ لا تخاف. يسجل Angular العديد من الخدمات باستخدام حاقن الجذر تلقائيًا. هذا يشمل ElementRefو Renderer2. في هذا المثال ، ندير عنصر المضيف من خلال واجهته الناشئة عن إنشاء مثيل ElementRef. Renderer2يتيح لنا تحديث DOM من خلال نموذج عرض Angular.

You can read more about views from this article. They are the preferred method for DOM/view updates in Angular applications.

It is important recognize the role that injectors play in the above example. By declaring variable types in the constructor, the class obtains valuable services. Each parameter’s data type maps to a set of instructions within the injector. If the injector has that type, it returns an instance of said type.

Instantiating Services

The Services and Injectors article explains this section to an extent. Though, this section rehashes the previous section or the most part. Services will often provide references to something else. They may just as well provide an interface extending a class’ capabilities.

The next example will define a logging service that gets added to a component’s injector via its providers: [] metadata.

// services/logger.service.ts import { Injectable } from '@angular/core'; @Injectable() export class LoggerService { callStack: string[] = []; addLog(message: string): void { this.callStack = [message].concat(this.callStack); this.printHead(); } clear(): void { this.printLog(); this.callStack = []; console.log(“DELETED LOG”); } private printHead(): void  null);  private printLog(): void { this.callStack.reverse().forEach((log) => console.log(message)); } }
// app.component.ts import { Component } from '@angular/core'; import { LoggerService } from './services/logger.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', providers: [LoggerService] }) export class AppComponent { constructor(private logger: LoggerService) { } logMessage(event: any, message: string): void { event.preventDefault(); this.logger.addLog(`Message: ${message}`); } clearLog(): void { this.logger.clear(); } }
// app.component.html 

Log Example

SUBMIT

Delete Logged Messages

CLEAR

Focus on the AppComponent constructor and metadata. The component injector receives instructions from the provider’s metadata field containing LoggerService. The injector then knows what to instantiate LoggerService from requested in the constructor.

The constructor parameter loggerService has the type LoggerService which the injector recognizes. The injector follows through with the instantiation as mentioned.

Conclusion

Dependency injection (DI) is a paradigm. The way it works in Angular is through a hierarchy of injectors. A class receives its resources without having to create or know about them. Injectors receive instruction and instantiate a service depending on which one was requested.

يظهر DI كثيرًا في Angular. توضح وثائق Angular الرسمية سبب انتشار هذا النموذج. ويواصلون أيضًا وصف حالات الاستخدام العديدة لـ DI بطريقة Angular بما يتجاوز ما تمت مناقشته في هذه المقالة. تحقق من ذلك بالضغط أدناه!

المزيد عن حقن التبعية:

  • مقدمة عن حقن التبعية الزاويّة
  • مقدمة سريعة لحقن التبعية