انتقل إلى المحتوى
في هذه الصفحة

أنجولار - Content Projection

Content Projection في Angular تتيح إدراج مكون داخل مكون آخر. هذا يغير كيفية تصميم مطور Angular لتطبيقه أو مكتبته نحو مكونات مرنة وقابلة لإعادة الاستخدام.

هذا المنشور يشرح أنواع Content Projection المختلفة، ومتى تستخدم كل نوع، مع مثال عملي.

أنواع Content Projection:

Content Projection بفتحة واحدة

في هذا النوع، لا تتوفر سوى فتحة واحدة لإدراج مكون في المكون الحاوي.

على سبيل المثال، لنفترض أن لديك مكون تذييل يجب أن يتضمن نوعا ما من مكونات المحتوى (الطفل). هناك سيناريوهات كثيرة لاستخدام Content Projection هنا منذ البداية:

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

يتم توفير فتحة لمكون التذييل حيث يمكن إدراج مكون المحتوى باستخدام ng-content:

import { Component } from '@angular/core';

@Component({
  selector: 'app-footer',
  template: `
  <div class="footer">
    <ng-content></ng-content>   // content slot
  </div>
`
})
export class FooterComponent {}

يمكن أن يكون مكون المحتوى أي شيء، وللحفاظ على التركيز على موضوع Content Projection هنا، لنفترض أنه مجرد زر “about”:

import { Component } from '@angular/core';

@Component({
  selector: 'app-button',
  template: `
    <button>about</button>
  `
})
export class ButtonComponent {}

يمكن الآن استخدام مكون التذييل داخل مكون التطبيق، ويمكن إدراج/إسقاط مكون الزر في مكانه:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <app-footer>
      <app-button></app-button>
    </app-footer>
  `
})
export class AppComponent {}

هذا كل شيء.

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

العودة للأعلى

Content Projection بفتحات متعددة:

يسمح هذا النوع من الإسقاط بإدراج مكونات محتوى متعددة في المكون الحاوي. هذا يعني أن هناك أكثر من فتحة ng-content.

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

لإسقاط محتويات متعددة، يجب تحديث مكون التذييل وجعله يقبل فتحتين في هذه الحالة، وتأخذ كل فتحة سمة select تحدد أي محتوى يُعرض في أي فتحة:

import { Component } from '@angular/core';

@Component({
  selector: 'app-footer',
  style: `
    .footer {
      display: flex;
      justify-content: space-between;
    }
  `,
  template: `
  <div class="footer">
    <ng-content select=[left]></ng-content>
    <ng-content select=[right]></ng-content>
  </div>
`
})
export class FooterComponent {}

تُحدد هذه السمات (left, right) أي محتوى يُعرض أين، لذا لتلبية المتطلب، يجب إعطاء الزر السمة left، وإعطاء مكون الرابط السمة right.

import { Component } from '@angular/core';

@Component({
  selector: 'app-button',
  template: `
    <button>about</button>
  `
})
export class ButtonComponent {}

@Component({
  selector: 'app-link',
  template: `
    <a href="#">about</a>
  `
})
export class LinkComponent {}

@Component({
  selector: 'app-root',
  template: `
    <app-footer>
      <app-button left></app-button>
      <app-link right></app-link>
    </app-footer>
  `
})
export class AppComponent {}

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

العودة للأعلى

Content Projection الشرطي:

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

لاستخدام Content Projection الشرطي، يجب استخدام ng-container و ng-template.

باستخدام ng-content، سيقوم أنجولار دائماً بتهيئة مكون المحتوى حتى لو كان داخل تعليمة ngIf.

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

دعنا نعود إلى مثال مكون التذييل ونعدل هذا المطلوب بحيث لا يمكن عرض محتوى واحد فقط في كل مرة، بناءً على بعض الشروط.

لتطبيق هذه المتطلبات، يجب أن يحدث التغيير

@HostListener('window:resize', ['$event'])
onResize(event) {
  this.innerWidth = window.innerWidth;
}

العودة للأعلى