كل ما تحتاج لمعرفته حول ng-template و ng-content و ng-container و * ngTemplateOutlet in Angular

لقد كان أحد تلك الأيام عندما كنت مشغولاً بالعمل على ميزات جديدة لمشروع مكتبي. فجأة ، لفت انتباهي شيء ما:

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

في البحث عن إجابات لأسئلتي اكتشفت مفهوم . لدهشتي ، كان هناك أيضًا *ngTemplateOutlet. بدأت رحلتي بحثًا عن توضيح حول مفهومين ، لكن الآن لدي أربعة منهم ، تبدو متشابهة تقريبًا!

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

1.

وكما يوحي اسم عنصر القالب الذي يستخدم سيارة أجرة مع توجيهات الهيكلية ( *ngIf، *ngFor، [ngSwitch]والتوجيهات مخصص).

تعمل عناصر القالب هذه فقط في وجود التوجيهات الهيكلية . يلف Angular عنصر المضيف (الذي يتم تطبيق التوجيه عليه) بالداخلويستهلكDOM النهائي عن طريق استبداله بتعليقات التشخيص.

فكر في مثال بسيط لما *ngIfيلي:

الموضح أعلاه هو التفسير الزاوي لـ *ngIf. يضع Angular العنصر المضيف الذي يتم تطبيق التوجيه عليه ويحافظ على المضيف كما هو. يشبه DOM الأخير ما رأيناه في بداية هذه المقالة:

الاستعمال:

لقد رأينا كيف يستخدم Angular ولكن ماذا لو أردنا استخدامه؟ نظرًا لأن هذه العناصر تعمل فقط مع التوجيه الهيكلي ، فيمكننا الكتابة على النحو التالي:

هنا homeهو booleanملك للمجموعة مكون ل trueقيمة. ناتج الكود أعلاه في DOM:

لم يتم تقديم أي شيء! :(

ولكن لماذا لا نرى رسالتنا حتى بعد استخدامها بشكل صحيح مع التوجيه الهيكلي؟

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

لنقارن بين هذين DOMين أعلاه اللذين قدمتهما Angular:

إذا كنت تراقب عن كثب ، فهناك علامة تعليق إضافية واحدة في DOM الأخير من المثال 2 . الكود الذي فسره Angular هو:

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

للتخلص من هذا هناك طريقتان للحصول على النتيجة المرجوة:

طريقة 1:

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

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

الطريقة الثانية:

هذا تنسيق غير مرئي تمامًا ونادرًا ما يستخدم (باستخدام شقيقين ). نحن هنا نعطي مرجعًا نموذجيًا لـ *ngIfin الخاص به thenلإخباره النموذج الذي يجب استخدامه إذا كان الشرط صحيحًا.

لا ينصح باستخدام عدة مثل هذا (يمكنك استخدامها بدلاً من ذلك) لأن هذا ليس الغرض منها. يتم استخدامها كحاوية للقوالب التي يمكن إعادة استخدامها في أماكن متعددة. سنغطي المزيد عن هذا في قسم لاحق من هذه المقالة.

2.

هل سبق لك أن كتبت أو رأيت رمزًا يشبه هذا:

السبب وراء كتابة الكثير منا لهذا الرمز هو عدم القدرة على استخدام توجيهات هيكلية متعددة على عنصر مضيف واحد في Angular. يعمل هذا الرمز الآن بشكل جيد ولكنه يقدم العديد من العناصر الإضافية الفارغة في DOM إذا كانت item.idقيمة خاطئة قد لا تكون مطلوبة.

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

والأسوأ من ذلك هو مستوى التداخل الذي يتعين عليك القيام به لتطبيق التصميم الخاص بك (CSS)!

لا تقلق ، علينا أن ننقذ!

Angular هو عنصر تجميع لا يتداخل مع الأنماط أو التخطيط لأن Angular لا يضعه في DOM .

لذلك إذا كتبنا مثالنا 1 مع :

نحصل على DOM النهائي على النحو التالي:

انظر لقد تخلصنا من تلك الصور الفارغة . يجب أن نستخدم عندما نريد فقط تطبيق توجيهات هيكلية متعددة دون إدخال أي عنصر إضافي في DOM الخاص بنا.

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

3.

يتم استخدامها لإنشاء مكونات قابلة للتكوين. هذا يعني أنه يمكن تكوين المكونات وفقًا لاحتياجات مستخدمها. يُعرف هذا جيدًا باسم Content Projection . المكونات التي يتم استخدامها في المكتبات المنشورة تستخدم لجعل نفسها قابلة للتكوين.

فكر في مكون بسيط :

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

توقعات متعددة:

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

إليك الطريقة:

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

سيبدو استدعاء المكون كما يلي:

4. * ngTemplateOutlet

... يتم استخدامها كحاوية للقوالب التي يمكن إعادة استخدامها في أماكن متعددة. سنغطي المزيد عن هذا في قسم لاحق من هذه المقالة.

... هناك حالة استخدام أخرى حيث يتم استخدامها لإدخال قالب ديناميكيًا في الصفحة. سأغطي حالة الاستخدام هذه في القسم الأخير من هذه المقالة.

هذا هو القسم الذي سنناقش فيه النقطتين المذكورتين أعلاه. *ngTemplateOutletيستخدم لسيناريوهين - لإدراج قالب مشترك في أقسام مختلفة من طريقة العرض بغض النظر عن الحلقات أو الحالة ولإنشاء مكون عالي التكوين.

إعادة استخدام النموذج:

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

فيما يلي مقتطف الشفرة:

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

*ngTemplateOutletيقبل أيضًا كائن سياق يمكن تمريره لتخصيص إخراج القالب المشترك. لمزيد من المعلومات حول كائن السياق ، راجع المستندات الرسمية.

مكونات قابلة للتخصيص:

حالة الاستخدام الثانية *ngTemplateOutletللمكونات المخصصة للغاية. ضع في اعتبارك مثالنا السابق للمكون مع بعض التعديلات:

أعلاه هو الإصدار المعدل للمكون الذي يقبل ثلاث خصائص إدخال -  headerTemplate، bodyTemplate، footerTemplate. فيما يلي مقتطف عن project-content.ts:

ما نحاول تحقيقه هنا هو إظهار الرأس والجسم والتذييل كما تم استلامه من المكون الرئيسي لـ . إذا لم يتم توفير أي منها ، فسيعرض المكون الخاص بنا القالب الافتراضي في مكانه. وبالتالي ، يتم إنشاء مكون مخصص للغاية.

لاستخدام المكون الذي تم تعديله مؤخرًا:

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

ng-content مقابل * ngTemplateOutlet

كلاهما يساعدنا في تحقيق مكونات مخصصة للغاية ولكن أيهما نختار ومتى؟

من الواضح أن ذلك *ngTemplateOutletيمنحنا بعض القوة لإظهار النموذج الافتراضي إذا لم يتم توفير أي شيء.

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

ومع ذلك ، فإن اختيار الاختيار من بين الاثنين يعتمد تمامًا على حالة الاستخدام الخاصة بك. على الأقل لدينا الآن سلاح جديد *ngTemplateOutletفي ترسانتنا يوفر مزيدًا من التحكم في المحتوى بالإضافة إلى ميزات ng-content!