کار را با طراحی و پیاده سازی TabService شروع میکنیم. برای این منظور یک سرویس را در فولدر services موجود در کنار CoreModule ایجاد خواهیم کرد؛ به این جهت ابتدا مدلهای زیر را خواهیم داشت:
import { Type, ValueProvider } from '@angular/core';
export interface OpenNewTabModel {
label: string;
componentType: Type<any>;
iconName: string;
modulePath?: string;
data?: ValueProvider[];
}
واسط تعریف شدهی در بالا به عنوان قرارداد مدل ورودی متد open مرتبط با سرویس TabService استفاده میشود. در اینجا componentType، نوع کامپوننت را مشخص میکند که قرار است داخل برگهی جدید نمایش داده شود. modulePath هم به مسیر ماژولی که باید به صورت پویا بارگذاری شود، اشاره میکند. دلیل وجود خصوصیت data را نیز در ادامه خواهیم دید.
import { TabItemComponent } from './tab-item-component';
export interface TabItem {
label: string;
iconName: string;
component: TabItemComponent;
}
OpenNewTabModel برای ارسال داده توسط مصرف کننده از این سرویس در نظر گرفته شده است. ولی واسط TabItem دارای خصوصیاتی میباشد که ما برای نمایش یک برگهی جدید نیازمندیم. TabItemComponent نیز دارای خصوصیاتی است که مورد نیاز دایرکتیو« NgComponentOutlet» است.
import { Component, OnInit } from '@angular/core';
import { TabService } from './../../../services/tab.service';
@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.scss']
})
export class TabsComponent implements OnInit {
constructor(public service: TabService) {}
ngOnInit() {}
}
<mat-tab-group>
<mat-tab
*ngFor="let tabItem of (service.tabItems$ | async); let index = index"
>
<ng-template mat-tab-label>
<mat-icon
class="icon"
aria-label="icon for tab"
>{{tabItem.iconName}}</mat-icon>
<span class="full">{{ tabItem.label }}</span>
<mat-icon
class="close"
(click)="service.close(index)"
aria-label="close tab button"
>close</mat-icon
>
<!-- </button> -->
</ng-template>
<ng-container *ngIf="tabItem.component.moduleFactory">
<ng-container
*ngComponentOutlet="
tabItem.component.componentType;
ngModuleFactory: tabItem.component.moduleFactory;
injector: tabItem.component.injector
"
>
</ng-container>
</ng-container>
<ng-container *ngIf="!tabItem.component.moduleFactory">
<ng-container
*ngComponentOutlet="
tabItem.component.componentType;
injector: tabItem.component.injector
"
>
</ng-container>
</ng-container>
</mat-tab>
</mat-tab-group>
در تکه کد بالا، ابتدا با استفاده از وهله تزریق شده TabService در کامپوننت مذکور، به شکل زیر، مشترک تغییرات لیست برگهها شدهایم و با استفاده از دایرکتیو *ngFor به ازای تک تک tabItemهای درخواست شده برای گشوده شدن، به شکل زیر کار وهله سازی پویا از کامپوننت مشخص شده انجام میشود:
خوب، با استفاده از آنچه تا اینجای مطلب بررسی شد، میتوان یک سیستم راهبری مبتنی بر Tab را نیز برپا کرد که مطلب جدایی را میطلبد. برای تکمیل مکانیزم بارگذاری پویای ماژولها، نیاز است تا مسیر ماژول مورد نظر را در فایل angular.json و بخش lazyModules به شکل زیر معرفی کنید:
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/MaterialAngularTabLayout",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"lazyModules": [
"src/app/lazy/lazy.module"
],
"scripts": []
},
به عنوان مثال قصد داریم ماژول LazyModule را به صورت پویا بارگذاری کرده و LazyComponent موجود در این ماژول را به صورت پویا در برگهی جدیدی نمایش دهیم. برای این منظور کدهای فایل AppComponent.ts را به شکل زیر تغییر خواهیم داد:
import { Component } from '@angular/core';
import { IdModel } from './core/models/id-model';
import { LazyComponent } from './lazy/lazy.component';
import { OpenNewTabModel } from './core/models/open-new-tab-model';
import { TabService } from './core/services/tab.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'MaterialAngularTabLayout';
constructor(private tabService: TabService) {}
loadLazyComponent() {
this.tabService.open(<OpenNewTabModel>{
label: 'Loaded Lazy Component',
iconName: 'thumb_up',
componentType: LazyComponent,
modulePath: 'src/app/lazy/lazy.module#LazyModule',
data: [{ provide: IdModel, useValue: <IdModel>{ id: 1 } }]
});
}
}
در تکه کد بالا با تزریق TabService به سازندهی آن، قصد گشودن برگهی جدیدی را توسط متد open آن، داریم. در بدنهی متد loadLazyComponent یک شیء با قرارداد OpenNewTabModel ایجاد شده و به عنوان آرگومان به متد open ارسال شده است. توجه داشته باشید که modulePath اینجا نیز به مانند خصوصیت loadChildren مرتبط با اشیاء مسیریابی، باید مقدار دهی شود. همچنین قصد داشتیم اطلاعاتی را نیز به کامپوننت مورد نظر ارسال کنیم؛ همانند مکانیزم مسیریابی که با پارامترها این چنین کارهایی صورت میپذیرد. در اینجا از یک کلاس به شکل زیر استفاده شدهاست:
export class IdModel {
constructor(public id: number) {}
}
در این صورت پیاده سازی LazyComponent نیز به شکل زیر خواهد بود:
import { Component, OnInit } from '@angular/core';
import { IdModel } from './../core/models/id-model';
@Component({
selector: 'app-lazy',
templateUrl: './lazy.component.html',
styleUrls: ['./lazy.component.scss']
})
export class LazyComponent implements OnInit {
constructor(private model: IdModel) {}
ngOnInit() {
console.log(this.model);
}
}
البته فراموش نکنید کامپوننتی را که نیاز است به صورت پویا بارگذاری شود، در قسمت entryComponents مرتبط با NgModule متناظر به شکل نیز معرفی کنید:
import { CommonModule } from '@angular/common';
import { LazyComponent } from './lazy.component';
import { NgModule } from '@angular/core';
@NgModule({
imports: [CommonModule],
declarations: [LazyComponent],
entryComponents: [LazyComponent]
})
export class LazyModule {}
با خروجی زیر:
و chunk تولید شده برای ماژول مورد نظر:
در صورتیکه در حالت production پروژه را بیلد کنید، هش مرتبط برای chunk تولید شده نیز ایجاد خواهد شد.
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید.