در مطلب «
Angular CLI - قسمت ششم - استفاده از کتابخانههای ثالث» با نحوهی دریافت، نصب و راه اندازی کتابخانهی
ngx-bootstrap آشنا شدیم. در اینجا میخواهیم نحوهی کار با کامپوننت Modal آن را بررسی کنیم.
سازماندهی بهتر کامپوننتهای ngx-bootstrap
پس از نصب بستهی npm کتابخانهی ngx-bootstrap و تنظیم فایل angular-cli.json. که در مطلب «
Angular CLI - قسمت ششم - استفاده از کتابخانههای ثالث» بررسی شدند، برای کار با کامپوننتهای این کتابخانه باید متدهای BsDropdownModule.forRoot، TooltipModule.forRoot، ModalModule.forRoot و ... را به قسمت imports فایل app.module.ts اضافه کرد. با انجام اینکار پس از مدتی به یک فایل بسیار شلوغ app.module.ts خواهیم رسید. برای مدیریت بهتر آن میتوان شبیه به مطلب «
سازماندهی برنامههای Angular توسط ماژولها» در پوشهی Shared برنامه، ماژول ذیل را تدارک دید. برای اینکار ابتدا فایل جدید src\app\shared\shared.bootstrap.module.ts را ایجاد نمائید. سپس کامپوننتهای این کتابخانه را به صورت ذیل در این تک ماژول اختصاصی قرار دهید:
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { BsDropdownModule } from "ngx-bootstrap/dropdown";
import { TooltipModule } from "ngx-bootstrap/tooltip";
import { ModalModule } from "ngx-bootstrap/modal";
@NgModule({
imports: [
CommonModule,
BsDropdownModule.forRoot(),
TooltipModule.forRoot(),
ModalModule.forRoot()
],
exports: [
BsDropdownModule,
TooltipModule,
ModalModule
]
})
export class SharedBootstrapModule { }
متدهای forRoot در قسمت imports قرار میگیرند (فلسفهی وجودی این متد و الگوی ویژه را در مطلب «
سازماندهی برنامههای Angular توسط ماژولها» پیشتر بررسی کردهایم). سپس برای اینکه این کامپوننتها در سایر ماژولهای برنامه قابل استفاده باشند، باید نام ماژول مرتبط با هر کدام را در قسمت exports نیز ذکر کرد.
اکنون برای استفادهی از SharedBootstrapModule اختصاصی فوق، میتوان دو روش را بکار برد:
الف) import مستقیم آن در فایل app.module.ts
import { SharedBootstrapModule } from './shared/shared.bootstrap.module';
@NgModule({
imports: [BrowserModule, SharedBootstrapModule],
// ...
})
export class AppModule {}
ب) import آن در SharedModule
و یا اگر فایل src\app\shared\shared.module.ts را مطابق مطلب «
سازماندهی برنامههای Angular توسط ماژولها» ایجاد کردهاید، این ماژول به صورت ذیل، در دو قسمت imports و exports آن اضافه خواهد شد:
import { SharedBootstrapModule } from "./shared.bootstrap.module";
@NgModule({
imports: [
CommonModule,
SharedBootstrapModule
],
exports: [
CommonModule,
SharedBootstrapModule
]
})
نمایش یک modal dialog توسط کامپوننت Modal
پس از تعریف ModalModule.forRoot، اکنون میتوان به کامپوننت Modal این ماژول دسترسی یافت. برای این منظور کامپوننتی که قرار است یک Modal را نمایش دهد، چنین ساختاری را پیدا میکند:
import { Component, OnInit, TemplateRef } from "@angular/core";
import { BsModalRef, BsModalService } from "ngx-bootstrap";
@Component({
selector: "app-modal-dialog-test",
templateUrl: "./modal-dialog-test.component.html",
styleUrls: ["./modal-dialog-test.component.css"]
})
export class ModalDialogTestComponent implements OnInit {
modalRef: BsModalRef;
constructor(private modalService: BsModalService) { }
openModal(template: TemplateRef<any>) {
this.modalRef = this.modalService.show(template,
{ animated: true, keyboard: true, backdrop: true, ignoreBackdropClick: false });
}
closeModal() {
this.modalRef.hide();
}
}
توسط سرویس BsModalService که به سازندهی کلاس کامپوننت تزریق شدهاست، میتوان به متد show آن دسترسی یافت. این متد یک
ng-template را قبول میکند. بنابراین در قالب این کامپوننت باید قسمتی را که قرار است به صورت modal نمایش داده شود، توسط یک ng-template تعریف کرد.
سپس با فراخوانی متد this.modalService.show میتوان این قالب را نمایش داد. خروجی این متد ارجاعی را به این modal بازگشت میدهد. از این ارجاع میتوان در جهت بستن آن استفاده کرد (مانند متد closeModal).
بنابراین در ادامه، قالب کامپوننت مثال این قسمت، یک چنین شکلی را پیدا میکند:
<h1>Displaying modal bootstrap dialogs</h1>
<button type="button" class="btn btn-info" (click)="openModal(template1)">Create template modal</button>
<ng-template #template1>
<div class="modal-header">
<h4 class="modal-title pull-left">Modal</h4>
<button type="button" class="close pull-right" aria-label="Close" (click)="closeModal()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
This is a modal.
</div>
</ng-template>
در اینجا محتوای modal داخل یک ng-template قرار گرفتهاست و این قالب توسط یک template reference variable به نام template1 مشخص شدهاست. این نام را در متد openModal(template1) استفاده خواهیم کرد تا به متد show سرویس نمایش modal منتقل شود.
طراحی یک کامپوننت عمومی مودال جهت دریافت تائید انجام عملیات
در ادامه میخواهیم توسط یک modal dialog، کار دریافت تائید و یا لغو انجام یک عملیات را انجام دهیم. چون این کامپوننت عمومی قرار است در بیش از یک ماژول استفاده شود، بنابراین نیاز است آنرا در Shared Module ثبت کرد. به همین جهت این کامپوننت را به نحو ذیل در پوشهی Shared ایجاد میکنیم:
ng g c Shared/ConfirmModal --skip-import
پرچم skip-import نیز ذکر شدهاست، چون قصد نداریم به صورت مستقیم از طریق درج selector آن در صفحه، با آن کار کنیم. سرویس سفارشی مودالی که برای این منظور تدارک خواهیم دید، کار نمایش آنرا انجام میدهد.
import { ConfirmModalComponent } from "./confirm-modal/confirm-modal.component";
@NgModule({
imports: [
],
entryComponents: [
ConfirmModalComponent
],
declarations: [
ConfirmModalComponent
]
})
export class SharedModule {}
نحوهی درج و تعریف این کامپوننت اندکی متفاوت است. چون این کامپوننت قرار است «
به صورت پویا» توسط متد show سرویس BsModalService نمایش داده شود (پارامتر اول آن میتواند یک قالب و یا یک کامپوننت کامل باشد)، باید در قسمت
entryComponents و declarations مربوط به SharedModule درج شود. آنرا در قسمت exports ذکر نمیکنیم، چون قرار نیست با درج مستقیم selector آن در صفحه، آنرا نمایش دهیم.
این کامپوننت دریافت تائید کاربر به صورت ذیل تعریف میشود:
import { Component } from "@angular/core";
@Component({
selector: "app-confirm-modal",
templateUrl: "./confirm-modal.component.html",
styleUrls: ["./confirm-modal.component.css"]
})
export class ConfirmModalComponent {
args: {
title: string;
message: string;
};
close: (val?: any) => void;
}
در اینجا args آن توسط سرویسی که در ادامه طراحی میکنیم، مقدار دهی خواهد شد (طراحی args در اینجا کاملا دلخواه است و در کامپوننتهای مشابه دیگر میتواند متفاوت باشد). متد close آن نیز کار گزارش دهی به فراخوان را انجام میدهد.
قالب این کامپوننت نیز بدون استفاده از ng-template تعریف میشود:
<div class="modal-header">
<h4 class="modal-title pull-left">{{ args?.title }}</h4>
<button type="button" class="close pull-right" aria-label="Close" (click)="close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>{{ args?.message }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-danger" (click)="close(true)">Yes</button>
<button class="btn btn-primary" (click)="close()">Cancel</button>
</div>
چون این کامپوننت قرار است به صورت پویا توسط متد show بارگذاری شود، نیازی نیست محتوای قالب آنرا توسط ng-template مخفی کرد و سپس نمایش داد. زمانیکه این کامپوننت بارگذاری شد، یعنی قصد داریم یک modal کامل را نمایش دهیم.
تا اینجا یک کامپوننت نمایش دریافت تائید انجام عملیات را تهیه کردیم. در ادامه نیاز است یک سرویس را جهت بارگذاری پویای اینگونه کامپوننتهای مودال طراحی کنیم. این سرویس عمومی در پوشهی Core و CoreModule ثبت خواهد شد:
با این محتوا
import { Injectable } from "@angular/core";
import { BsModalService } from "ngx-bootstrap";
@Injectable()
export class ModalService {
constructor(private bsModalService: BsModalService) { }
show(component: any, args?: any, options?: any): Promise<any> {
return new Promise(resolve => {
options = options || {};
const modal = this.bsModalService.show(component, options);
let result: any;
const sub = this.bsModalService.onHidden.subscribe(() => {
sub.unsubscribe();
resolve(result);
});
modal.content.args = args;
modal.content.close = (val?: any) => {
result = val;
modal.hide();
};
});
}
}
کار این سرویس، نمایش یک کامپوننت مودال مانند ConfirmModalComponent به صورت پویا است؛ از این جهت که متد this.bsModalService.show هم امکان نمایش یک ng-template را دارد و هم یک کامپوننت کامل را به صورت پویا.
یک مودال در سه حالت ممکن است بسته شود:
الف) کلیک بر روی دکمهی close و یا cancel
ب) کلیک بر روی علامت ضربدر درج شدهی در یک سمت عنوان آن
ج) کلیک بر روی قسمتی از صفحه، خارج از مودال
در حالات ب و ج، رخداد this.bsModalService.onHidden فراخوانی میشود. در حالت الف، همان متد close درج شدهی در کامپوننت فراخوانی میشود.
برای اینکه بتوان نتیجهی عملیات را از طرف یک سرویس به کامپوننت فراخوان آن گزارش دهیم، یکی از روشها، استفاده از Promiseها است که مشاهده میکنید. با فراخوانی resolve(result)، کار ارسال نتیجهی فراخوانی متدهای close(true) و ()close صورت میگیرد (یا true و یا undefined).
خاصیت modal.content امکان دسترسی به خواص عمومی کامپوننت در حال استفاده را میسر میکند (content به کامپوننت بارگذاری شده اشاره میکند). اینجا است که میتوان برای مثال به خاصیت args یک کامپوننت، مقادیری را نسبت داد و یا به متد close آن دسترسی یافت.
پس از افزودن این سرویس، محل تعریف آن در قسمت providers مربوط به CoreModule است تا در تمام برنامه قابل دسترسی شود:
import { ModalService } from "./modal.service";
@NgModule({
providers: [
ModalService
]
})
export class CoreModule {}
در پایان برای آزمایش این سرویس جدید، یک دکمه و یک برچسب را به قالب کامپوننت ModalDialogTestComponent ابتدای بحث اضافه میکنیم:
<button type="button" class="btn btn-danger" (click)="deleteRecord()">Delete record</button>
<div *ngIf="confirmResult" class="alert alert-info">{{confirmResult}}</div>
با این کدها:
import { ModalService } from "./../../core/modal.service";
import { ConfirmModalComponent } from "./../../shared/confirm-modal/confirm-modal.component";
export class ModalDialogTestComponent implements OnInit {
confirmResult: string;
constructor(private modalService: ModalService) { }
deleteRecord() {
this.confirmResult = "";
this.modalService.show(
ConfirmModalComponent,
{
title: "Confirm", message: "Do you want to delete this record?"
},
{
animated: true, keyboard: true, backdrop: true, ignoreBackdropClick: false
}).then(confirmed => {
if (confirmed) {
this.confirmResult = "Deleted!";
} else {
this.confirmResult = "Canceled!";
}
});
}
}
در اینجا نحوهی استفادهی از این ModalService سفارشی را ملاحظه میکنید. ابتدا به سازندهی کلاس تزریق شدهاست و سپس در متد deleteRecord توسط متد show آن، کامپوننت ConfirmModalComponent به صورت پویا بارگذاری شدهاست. همچنین خاصیت args آن نیز با خواص title و message سفارشی، مقدار دهی شدهاست. چون این متد یک Promise را باز میگرداند، میتوان مشترک آن شد و نتیجهی نهایی را از آن دریافت کرد و بر اساس آن تصمیم گرفت که آیا باید عملیاتی رخدهد، یا خیر.
توسط this.modalService.show میتوان انواع و اقسام کامپوننتهای مودال را به صورت پویا بارگذاری کرد و نمایش داد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.