جاوااسکریپت - چرا/چه زمانی ng-content میتواند یک روش خوب باشد؟
شاید یک سؤال عجیب در مورد ng-content/Content projection باشد. من این ایده را میفهمم که میتوان به صورت پویا 'محتوا' را وارد کرد که میتواند از طریق ng-content نمایش داده شود. یک کار دیگری که میتوانم برای رسیدن به این هدف انجام دهم این است که یک ویژگی Input بسازم تا اطلاعاتی که میخواهم در کامپوننت نمایش دهم، منتقل کنم.
من به این پروژه برخوردم؛ https://trimox.github.io/angular-mdc-web/#/home که اجزای متریال جدید برای انگولار است. یک چیزی که هنگام مرور کد متوجه شدم این است که او به شدت از ng-content برای پروژکت کردن برخی 'زیرجزءها' استفاده میکند. نمیتوانم جلوی خودم را بگیرم که تعجب نکنم... چرا؟؟ چون من هم میتوانم سعی کنم این را با یک کامپوننت کارت و یک پیکربندی شبیه JSON/شیء که منتقل میکنم حل کنم.
<mdc-card class="demo-card">
<mdc-card-primary-action mdc-ripple>
<mdc-card-media class="demo-card__media--16-9" [wide]="true"></mdc-card-media>
<div class="demo-card__primary">
<h2 class="demo-card__title" mdcHeadline6>Our Changing Planet</h2>
<h3 class="demo-card__subtitle" mdcSubtitle2>by Kurt Wagner</h3>
</div>
<div class="demo-card__secondary" mdcBody2>
Visit ten places on our planet that are undergoing the biggest changes today.
</div>
</mdc-card-primary-action>
<mdc-card-actions>
<mdc-card-action-buttons>
<button mdc-button mdcCardAction="button">Read</button>
<button mdc-button mdcCardAction="button">Bookmark</button>
</mdc-card-action-buttons>
<mdc-card-action-icons>
<button mdcIconButton mdcCardAction="icon" iconOn="favorite" iconOff="favorite_border"></button>
<mdc-icon mdcCardAction="icon" mdcRipple>share</mdc-icon>
<mdc-icon mdcCardAction="icon" mdcRipple>more_vert</mdc-icon>
</mdc-card-action-icons>
</mdc-card-actions>
در بالا میتوانید برای مثال یک مؤلفه «کارت» با چندین مورد در آن ببینید. همچنین من منبع این مؤلفهها را پیدا کردهام؛ https://github.com/trimox/angular-mdc-web/blob/master/packages/card/card.ts.
سوال اصلی من همچنان باقی است؛ چه زمانی استفاده از ng-content یک روش خوب است و چه زمانی نیست. و چرا این یک روش خوب است؟ من همچنین دیدهام که DevExpress از آن در کتابخانه devextreme خود استفاده میکند؛ https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/RecordGrouping/Angular/Light/
متشکرم
پاسخ شماره ۱
بگذارید سوال شما را سادهتر کنم. فرض کنید میخواهید یک کامپوننت card ایجاد کنید. شما میتوانید کامپوننتهای فرزند خود را بنویسید (همراه با کدی برای اجرای پرتاب محتوای لازم)، مانند این:
<card>
<card-title>My Title</card-title>
<card-content>
<p>Content here...</p>
<card-action>Some button here</card-action>
</card-content>
<card-footer>My footer</card-footer>
</card>
...یا شما میتوانید فقط یک کامپوننت <card> ایجاد کنید و بگذارید که یک شیء پیکربندی مانند این دریافت کند:
config = {
title: "My title",
content: {
innerHTML: "<p>Content here...</p>",
actions: [ {type: "button", text: "Some button here", onClick: "myFunction()"} ]
},
footer: "My footer"
}
پس چرا میخواهی گزینه اول را انتخاب کنی؟
1. HTML معنایی
یکی از دلایل این است که به طور کلی خوب است که ساختار (HTML)، ارائه (CSS) و رفتار (JS/TS) را جدا کنید. از آنجایی که یک کامپوننت card احتمالاً شامل عناصر ساختاری دیگری مانند عنوان، محتوا، اقدامات، پاورقی و غیره خواهد بود، بهتر است این ساختار را در HTML بیان کنید تا اینکه از طریق JavaScript/TypeScript انجام دهید. شما باید اجازه دهید که نشانهگذاری HTML شما معنای زیرین محتوای شما را منتقل کند. انجام این کار کد شما را معناییتر، تمیزتر، قابل نگهداریتر و قابل خواندن برای انسان میکند. شاید اگر فقط خودتان یک برنامه توسعه میدهید (به جای کار در تیم)، نیاز کمتری باشد که آن را برای دیگران معنایی و قابل خواندن کنید زیرا شما کد خود را درک میکنید. اما هنوز هم باید آن را برای خود آیندهتان قابل خواندن کنید زیرا اگر بخواهید یک سال بعد کد خود را بررسی کنید، احتمالاً میخواهید کدتان معنایی و به راحتی قابل خواندن باشد.
۲. استفاده از نگارش قالب انگولار
دلیل دیگری برای انتخاب گزینه اول (استفاده از پروژهبندی محتوا + کامپوننتهای فرزند) این است که میتوانید از امکانات قدرتمند انگولار، مانند data binding و *ngFor، در HTML خود استفاده کنید. برای مثال، اگر بخواهید فهرستی از کارتها را نمایش دهید، به طوری که هر کارت یک پست شبکه اجتماعی (مانند در فیسبوک) را نشان دهد، میتوانید به راحتی بنویسید:
<card *ngFor="let post of posts">
<card-title>{{post.title}}</card-title>
<card-content>
<p>{{post.content}}</p>
<card-action>
<card-button (click)="likePost(post)">Like</card-button>
<card-button (click)="sharePost(post)">Share</card-button>
</card-action>
</card-content>
<card-footer>{{post.footer}}</card-footer>
</card>
در حالی که احتمالاً اجرای همان کار با یک شیء پیکربندی JS/TS دشوارتر خواهد بود.
پاسخ شماره ۲
تصور کنید که میخواهید دادهها را از والد به فرزند انتقال دهید. معمولاً ما از اتصال ویژگی Input استفاده میکنیم و دادهها را در فایل ts فرزند دریافت کرده و دادهها را متصل میکنیم. به جای انجام این کار میتوانیم از ng-content استفاده کنیم، محتوا را از والد داخل تگ کامپوننت قرار دهیم و دادههای والد خود را متصل کنیم و از ng-content در فرزند استفاده کنیم تا از اتصال ویژگی Input جلوگیری کنیم.
کامپوننت والد
<parent>
<child>
<div class="head">{{heading}}</div>
</child>
</parent>
کامپوننت فرزند
<ng-content select=".head"></ng-content>
اینجا ما دادههای والد را مستقیماً متصل میکنیم بهجای استفاده از اتصال ویژگی ورودی برای پردازش دادهها
