چند نکتهی تکمیلی در مورد بهبود تعاریف Shared Module و Core Module
الف) چگونه از import ثانویهی Core Module در سایر ماژولها جلوگیری کنیم؟
Core Module فقط باید در AppModule برنامه import شود و نه در هیچجای دیگری. برای جلوگیری اتفاقی از این مساله میتوان سازندهای را به شکل زیر به آن اضافه کرد:
@NgModule({
imports: [CommonModule, RouterModule],
exports: [], // components that are used in app.component.ts will be listed here.
declarations: [], // components that are used in app.component.ts will be listed here.
providers: [BrowserStorageService, AppConfigService] // global singleton services of the whole app will be listed here.
})
export class CoreModule {
constructor( @Optional() @SkipSelf() core: CoreModule) {
if (core) {
throw new Error("CoreModule should be imported ONLY in AppModule.");
}
}
};
روش کار به این صورت است که خود CoreModule را به سازندهی همان کلاس تزریق میکنیم! اگر وهلهای از آن قابل دسترسی بود، یعنی Angular پیشتر این ماژول را import کردهاست. در این حالت با صدور خطایی این مشکل را گوشزد میکنیم.
از همین روش برای تشخیص singleton بودن یک سرویس نیز میتوان استفاده کرد. خودش را به خودش تزریق میکنیم! اگر تزریقی صورت گرفت، یک خطا را صادر میکنیم.
ب) چگونه از وهله سازی مجدد سرویسهای تعریف شدهی در Shared Module در سایر ماژولها جلوگیری کنیم؟
هدف از قسمت providers در Shared Module تنها ارائهی سرویسهایی جهت کامپوننتهای اشتراکی آن است؛ وگرنه سرویسهای سراسری برنامه در CoreModule تعریف میشوند و این ماژول ویژه نیز تنها یکبار و آنهم در AppModule برنامه import خواهد شد. اما در مورد Shared Module اینطور نیست و اگر این ماژول در یک lazy loaded module استفاده شود، سرویسهای آن طول عمر متفاوتی را پیدا خواهند کرد (هر lazy loaded module یک injector و یک طول عمر خاص خودش را تعریف میکند).
در این حالت برای اینکه سرویسهای Shared Module فقط در AppModule وهله سازی شوند و نه در هیچجای دیگری، روش کار به صورت ذیل است:
- ابتدا آرایهی providers را از تعاریف NgModule آن حذف میکنیم.
- سپس متد ویژهای را به نام forRoot، به کلاس آن اضافه خواهیم کرد:
@NgModule({
imports: [CommonModule],
declarations: [], // common and shared components/directives/pipes between more than one module and components will be listed here.
exports: [CommonModule], // common and shared components/directives/pipes between more than one module and components will be listed here.
/* No providers here! Since they’ll be already provided in AppModule. */
})
export class SharedModule {
static forRoot(): ModuleWithProviders {
// Forcing the whole app to use the returned providers from the AppModule only.
return {
ngModule: SharedModule,
providers: [ /* All of your services here. It will hold the services needed by `itself`. */]
};
}
};
متد forRoot به صورت استاتیک تعریف میشود و همچنین خروجی از نوع ModuleWithProviders دارد. توسط ModuleWithProviders سبب خواهیم شد، AppModule، این ماژول را به همراه آرایهی providers آن import کند؛ اما سایر ماژولها خیر.
سایر ماژولها چون دسترسی به آرایهی حذف شدهی providers این ماژول را ندارند، دیگر نمیتوانند سرویسهای آنرا وهله سازی کنند. اما AppModule با فراخوانی
()SharedModule.forRoot در لیست import خود، تنها یکبار سبب وهله سازی سرویسهای آن میگردد.
بنابراین در اینجا AppModule باید ()SharedModule.forRoot را import کند. سایر ماژولها فقط SharedModule را import میکنند (بدون ذکر متد forRoot). به این ترتیب سرویسهای آن تنها یکبار توسط AppModule در طول عمر برنامه به اشتراک گذاشته میشوند و در این حالت تفاوتی نمیکند که SharedModule در یک lazy loaded module استفاده شدهاست یا خیر.
روش تعریف متد forRoot توسط سیستم مسیریابی Angular نیز استفاده میشود و یک الگوی پذیرفته شده در بین توسعه دهندگان Angular است. برای مثال ()RouterModule.forRoot در AppModule تعریف میشود و ()RouterModule.forChild برای سایر ماژولها.
نمونهای از AppModule ، ShardModule و CoreModule