کتابخانه ngx-ui
Style and Component Library for Angular 2 and beyond! Demo
The State of Developer Ecosystem 2023
This report is the culmination of insights gathered from 26,348 developers from all around the globe. The world of developers is vast and diverse, making it an endlessly fascinating realm for exploration and learning. Through yearly research initiatives like this one, our goal is to explore this captivating world, uncover valuable insights about developers and their craft, and then share these facts with the community.
<Comp1>This is a content coming from the parent</Comp1>
معرفی مفهوم Render Fragment
برای درج محتوای تامین شدهی توسط کامپوننت والد در یک کامپوننت فرزند، از ویژگی به نام Render Fragment استفاده میشود. مثالی جهت توضیح جزئیات آن:
در ابتدا یک کامپوننت والد جدید را در مسیر Pages\LearnBlazor\ParentComponent.razor به صورت زیر تعریف میکنیم:
@page "/ParentComponent" <h1 class="text-danger">Parent Child Component</h1> <ChildComponent Title="This title is passed as a parameter from the Parent Component"> A `Render Fragment` from the parent! </ChildComponent> <ChildComponent Title="This is the second child component"></ChildComponent> @code { }
- سپس دوبار کامپوننت فرضی ChildComponent به همراه پارامتر Title و یک محتوای جدید قرار گرفتهی در بین تگهای آن، در صفحه تعریف شدهاند.
- بار دومی که ChildComponent در صفحه قرار گرفتهاست، به همراه محتوای جدیدی در بین تگهای خود نیست.
برای دسترسی به این کامپوننت از طریق منوی برنامه، مدخل منوی آنرا به کامپوننت Shared\NavMenu.razor اضافه میکنیم:
<li class="nav-item px-3"> <NavLink class="nav-link" href="ParentComponent"> <span class="oi oi-list-rich" aria-hidden="true"></span> Parent/Child Relation </NavLink> </li>
<div> <div class="alert alert-info">@Title</div> <div class="alert alert-success"> @if (ChildContent == null) { <span> Hello, from Empty Render Fragment </span> } else { <span>@ChildContent</span> } </div> </div> @code { [Parameter] public string Title { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } }
- خاصیت عمومی Title که توسط ویژگی Parameter مزین شدهاست، امکان تنظیم مقدار مشخصی را توسط کامپوننت دربرگیرندهی ChildComponent میسر میکند.
- در اینجا پارامتر عمومی دیگری نیز تعریف شدهاست که اینبار از نوع ویژهی RenderFragment است. توسط آن میتوان به محتوایی که در کامپوننت والد ChildComponent در بین تگهای آن تنظیم شدهاست، دسترسی یافت. همچنین اگر این محتوا توسط کامپوننت والد تنظیم نشده باشد، مانند دومین باری که ChildComponent در صفحه قرار گرفتهاست، میتوان با بررسی نال بودن آن، یک محتوای پیشفرض را نمایش داد.
با این خروجی:
روش دیگری برای فراخوانی Event Call Back ها
در قسمت قبل روش انتقال اطلاعات را از کامپوننتهای فرزند، به والد مشاهده کردیم. فراخوانی آنها در سمت Child Component نیاز به یک متد اضافی داشت و همچنین تنها یک پارامتر را هم ارسال کردیم. برای ساده سازی این عملیات از روش زیر نیز میتوان استفاده کرد:
<button class="btn btn-danger" @onclick="@(() => OnClickBtnMethod.InvokeAsync((1, "A message from child!")))"> Show a message from the child! </button> @code { // ... [Parameter] public EventCallback<(int, string)> OnClickBtnMethod { get; set; } }
- همچنین فراخوانی OnClickBtnMethod.InvokeAsync را نیز در محل تعریف onclick@ بدون نیازی به یک متد اضافی، مشاهده میکنید. نکتهی مهم آن، قرار دادن این قطعه کد داخل ()@ است تا ابتدا و انتهای کدهای #C مشخص شود؛ وگرنه کامپایل نمیشود.
در سمت کامپوننت والد برای دسترسی به OnClickBtnMethod که اینبار یک tuple را ارسال میکند، میتوان به صورت زیر عمل کرد:
@page "/ParentComponent" <h1 class="text-danger">Parent Child Component</h1> <ChildComponent OnClickBtnMethod="ShowMessage" Title="This title is passed as a parameter from the Parent Component"> A `Render Fragment` from the parent! </ChildComponent> <ChildComponent Title="This is the second child component"> <p><b>@MessageText</b></p> </ChildComponent> @code { string MessageText = ""; private void ShowMessage((int Value, string Message) args) { MessageText = args.Message; } }
امکان تعریف چندین RenderFragment
تا اینجا یک RenderFragment را در کامپوننت فرزند تعریف کردیم. امکان تعریف چندین RenderFragment در ChildComponent.razor نیز وجود دارند:
@code { // ... [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public RenderFragment DangerChildContent { get; set; } }
<ChildComponent OnClickBtnMethod="ShowMessage" Title="This title is passed as a parameter from the Parent Component"> <ChildContent> A `Render Fragment` from the parent! </ChildContent> <DangerChildContent> A danger content from the parent! </DangerChildContent> </ChildComponent>
از آنجائیکه ذکر این تگها اختیاری است، نیاز است در ChildComponent.razor بر اساس null بودن آنها، تصمیم به رندر محتوایی پیشفرض گرفت:
@if(DangerChildContent == null) { @if (ChildContent == null) { <span> Hello, from Empty Render Fragment </span> } else { <span>@ChildContent</span> } } else { <span>@DangerChildContent</span> }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-08.zip
Angular Essentials
این افزونه گروهی از مهمترین افزونههای موجود را به صورت بسته بندی شده ارائه میدهد و با نصب آن، تعدادی از افزونههایی را که در ادامه نامبرده خواهند شد، به صورت یکجا و خودکار دریافت خواهید کرد.
Angular Language Service
نگارشهای اخیر Angular به همراه یک سرویس زبان نیز میباشند که به ادیتورهای مختلف این امکان را میدهد تا توسط این ویژگی بتوانند قابلیتهای ویرایشی بهتری را جهت برنامههای Angular ارائه کنند. برای مثال ویرایش مطلوب قالبهای کامپوننتهای Angular و استفادهی از Syntax خاص آن، موردی است که توسط هیچکدام از HTML ادیتورهای موجود پشتیبانی نمیشود. اکنون به کمک سرویس زبان Angular و افزونهی ویژهی آن برای VSCode که توسط تیم اصلی Angular توسعه یافتهاست، امکان ویرایش غنی قالبهای HTML ایی آن فراهم شدهاست. این افزونه یک چنین قابلیتهایی را فراهم میکند:
الف) AOT Diagnostic messages
اگر قالب HTML ایی مورد استفاده (چه به صورت inline و چه در یک فایل html مجزا) به خاصیتی تعریف نشده اشاره کند، بلافاصله خطای مرتبطی ظاهر خواهد شد:
ب) Completions lists یا همان Intellisense
ج) امکان Go to definition با کلیک راست بر روی خواص و متدهای ذکر شدهی در قالب.
د) Quick info که با نزدیک کردن اشارهگر ماوس به خاصیت یا متدی در صفحه، اطلاعات بیشتری را در مورد آن نمایش میدهد.
angular2-inline
علاوه بر افزونهی سرویس زبانهای Angular، این افزونه نیز قابلیت درک قالبهای inline کامپوننتها را داشته و به همراه syntax highlighting و همچنین Intellisense است.
Auto Import
حین کار با TypeScript، هر ماژولی که در صفحه ارجاعی داشته باشد، باید در ابتدای فایل جاری import شود. افزونهی Auto Import با بررسی ماژولهای موجود و فراهم آوردن Intellisense ایی بر اساس آنها، اینکار را سادهتر میکند:
بنابراین این افزونه صرفا مختص به Angular نیست و برای کارهای متداول TypeScript نیز بسیار مفید است.
TSLint
این افزونه ابزار TSLint را با VSCode یکپارچه میکند. بنابراین نیاز است پیش از نصب این افزونه، وابستگیهای ذیل را نیز به صورت سراسری نصب کرد:
> npm install -g tslint typescript
تعدادی از امکانات آنرا پس از نصب، با فشردن دکمهی F1 میتوان مشاهده کرد:
برای مثال تولید فایل tslint.json، امکان سفارشی سازی موارد بررسی شوندهی توسط این افزونه را فراهم میکند و اگر برنامهی خود را توسط Angular CLI ایجاد کردهاید، این فایل هم اکنون در ریشهی پروژه قرار دارد.
در مورد TSLint در مطلب «Angular CLI - قسمت دوم - ایجاد یک برنامهی جدید» بیشتر توضیح داده شدهاست و اینبار به کمک این افزونه، خطاهای یاد شده را دقیقا درون محیط ادیتور و به صورت خودکار و یکپارچهای مشاهده خواهید کرد.
Angular v4 TypeScript Snippets
سیستم کار VSCode مبتنی بر ایجاد فایلهای خالی است و مفهوم قالبهای از پیش آمادهی فایلها در آن وجود ندارد. اما با کمک Code Snippets میتوان این خلاء را پر کرد. افزونهی Angular v4 TypeScript Snippets دقیقا به همین منظور طراحی شدهاست و زمانیکه حروف -a یا -rx را در صفحه تایپ میکنید، منویی ظاهر خواهد شد که توسط آن میتوان قالب ابتدایی شروع به کار با انواع و اقسام جزئیات پروژههای Angular را تهیه کرد.
Path Intellisense
این افزونه مسیر فایلهای موجود را به صورت یک Intellisense ارائه میکند و به این صورت به سادگی میتوان مسیرهای اسکریپتها و یا شیوهنامهها را در ادیتور انتخاب و وارد کرد.
مروری بر نحوهی کارکرد مسیریابی اصلی برنامه
به router-outlet ایی که در فایل قالب src\app\app.component.html قرار گرفتهاست، primary outlet میگویند. زمانیکه کاربر، برنامه را در مرورگر مشاهده میکند، با هربار کلیک بر روی یکی از لینکهای منوی بالای سایت، قالب آنرا در این primary outlet مشاهده میکند. اگر بخواهیم پنل دیگری را در همین صفحه و در همین سطح از نمایش، درج کنیم، نیاز به تعریف outlet دیگری است که به همراه مسیرهای ثانویهای نیز خواهد بود.
تعریف یک router-outlet نامدار
با توجه به اینکه هر پنل به همراه مسیریابی ثانویه، نیاز به router-outlet خودش را خواهد داشت، مسیریاب برای اینکه بداند محتوای آنها را در کجای صفحه درج کند، به نامهای آنها مراجعه میکند. به این ترتیب میتوان چندین router-outlet را در یک سطح از نمایش تعریف کرد؛ اما هرکدام باید دارای نامی منحصربفرد باشند.
در مثال این سری میخواهیم پنلی را در سمت راست صفحهی اصلی درج کنیم. برای تعریف آن در همان سطحی که router-outlet اصلی قرار دارد، نیاز است فایل src\app\app.component.html را ویرایش کنیم:
<div class="container"> <div class="row"> <div class="col-md-10"> <router-outlet></router-outlet> </div> <div class="col-md-2"> <router-outlet name="popup"></router-outlet> </div> </div> </div>
افزودن ماژول جدید پیامهای سیستم
در ادامه ماژول جدید پیامهای سیستم را به همراه تنظیمات ابتدایی مسیریابی آن اضافه خواهیم کرد که در آن ماژول، مدیریت نمایش پیامهای مختلفی در router-outlet ثانویه popup صورت خواهد گرفت:
>ng g m message --routing
در ادامه نیاز است MessageModule را به قسمت imports فایل src\app\app.module.ts نیز معرفی کنیم (پیش از AppRoutingModule که حاوی مسیریابی catch all است):
import { MessageModule } from './message/message.module'; @NgModule({ declarations: [ ], imports: [ BrowserModule, FormsModule, HttpModule, InMemoryWebApiModule.forRoot(ProductData, { delay: 1000 }), ProductModule, UserModule, MessageModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
سپس کامپوننت جدید Message را به ماژول Message برنامه اضافه میکنیم:
>ng g c message/message
پس از آن یک سرویس ابتدایی پیامهای کاربران را نیز اضافه خواهیم کرد:
>ng g s message/message -m message/message.module
installing service create src\app\message\message.service.spec.ts create src\app\message\message.service.ts update src\app\message\message.module.ts
پس از ایجاد قالب ابتدایی فایل message.service.ts آنرا به نحو ذیل تکمیل میکنیم:
import { Injectable } from '@angular/core'; @Injectable() export class MessageService { private messages: string[] = []; isDisplayed = false; addMessage(message: string): void { let currentDate = new Date(); this.messages.unshift(message + ' at ' + currentDate.toLocaleString()); } }
اکنون جهت تکمیل کامپوننت پیامها، ابتدا فایل قالب message.component.html را به نحو ذیل تکمیل میکنیم:
<div class="row"> <h4 class="col-md-10">Message Log</h4> <span class="col-md-2"> <a class="btn btn-default" (click)="close()">x</a> </span> </div> <div *ngFor="let message of messageService.messages; let i=index"> <div *ngIf="i<10" class="message-row"> {{ message }} </div> </div>
کدهای کامپوننت این قالب به صورت ذیل است:
import { MessageService } from './../message.service'; import { Router } from '@angular/router'; import { Component, OnInit } from '@angular/core'; @Component({ //selector: 'app-message', templateUrl: './message.component.html', styleUrls: ['./message.component.css'] }) export class MessageComponent implements OnInit { constructor(private messageService: MessageService, private router: Router) { } ngOnInit() { } close(): void { // Close the popup. this.router.navigate([{ outlets: { popup: null } }]); this.messageService.isDisplayed = false; } }
تکمیل سایر کامپوننتهای برنامه در جهت استفاده از سرویس پیامها
ابتدا به فایل src\app\product\product-edit\product-edit.component.ts مراجعه کرده و سرویس جدید پیامها را به سازندهی آن تزریق میکنیم:
import { MessageService } from './../../message/message.service'; @Component({ selector: 'app-product-edit', templateUrl: './product-edit.component.html', styleUrls: ['./product-edit.component.css'] }) export class ProductEditComponent implements OnInit { constructor(private productService: ProductService, private messageService: MessageService, private route: ActivatedRoute, private router: Router) { }
onSaveComplete(message?: string): void { if (message) { this.messageService.addMessage(message); }
تنظیم مسیرهای ثانویه
نحوهی تعریف مسیریابیهای مرتبط با router-outletهای غیراصلی برنامه، همانند سایر مسیریابیهای برنامهاست؛ با این تفاوت که در اینجا خاصیت outlet نیز به تنظیمات مسیر اضافه خواهد شد. به این ترتیب مشخص خواهیم کرد که محتوای این مسیر باید دقیقا در کدام router-outlet نامدار، درج شود.
برای این منظور فایل src\app\message\message-routing.module.ts را گشوده و تنظیمات مسیریابی آنرا که به صورت RouterModule.forChild تعریف میشوند (چون ماژول اصلی برنامه نیستند)، تکمیل خواهیم کرد:
const routes: Routes = [ { path: 'messages', component: MessageComponent, outlet: 'popup' } ];
فعالسازی یک مسیر ثانویه
در اینجا نیز همانند سایر مسیریابیها، از دایرکتیو routerLink برای فعالسازی مسیرهای ثانویه استفاده میکنیم؛ اما syntax آن کمی متفاوت است:
<a [routerLink]="[{ outlets: { popup: ['messages'] } }]">Messages</a> <a [routerLink]="['/products', product.id, 'edit', { outlets: { popup: ['summary', product.id] } }]">Messages</a>
در دومین لینک تعریف شده، ابتدا یک مسیر اصلی فعال شده و سپس یک مسیر ثانویه نمایش داده میشود.
یک نکته: هرچند به primary outlet نامی انتساب داده نمیشود، اما نام آن دقیقا primary است و میتوان قسمت outlets را به صورت ذیل نیز تعریف کرد:
{ outlets: { primary: ['/products', product.id,'edit'], popup: ['summary', product.id] }}
در ادامه فایل src\app\app.component.html را ویرایش کرده و لینک Show Messages را به آن اضافه میکنیم:
<ul class="nav navbar-nav navbar-right"> <li *ngIf="authService.isLoggedIn()"> <a>Welcome {{ authService.currentUser.userName }}</a> </li> <li> <a [routerLink]="[{ outlets: { popup: ['messages'] } }]">Show Messages</a> </li>
آدرس آن نیز چنین شکلی را پیدا میکند:
http://localhost:4200/products(popup:messages)
اکنون میخواهیم قابلیت مخفی سازی این پنل را نیز پیاده سازی کنیم. به همین جهت از خاصیت isDisplayed سرویس پیامها که توسط دکمهی بستن MessageComponent مدیریت میشود، استفاده خواهیم کرد. بنابراین لینک جدیدی را که در فایل src\app\app.component.html اضافه کردیم، به نحو ذیل تغییر خواهیم داد:
<li *ngIf="!messageService.isDisplayed"> <a (click)="displayMessages()">Show Messages</a> </li> <li *ngIf="messageService.isDisplayed"> <a (click)="hideMessages()">Hide Messages</a> </li>
import { MessageService } from './message/message.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private authService: AuthService, private router: Router, private messageService: MessageService) { } displayMessages(): void { this.router.navigate([{ outlets: { popup: ['messages'] } }]); this.messageService.isDisplayed = true; } hideMessages(): void { this.router.navigate([{ outlets: { popup: null } }]); this.messageService.isDisplayed = false; } }
برای فعالسازی یک مسیرثانویه توسط متدهای برنامه، نیاز است از سرویس مسیریاب و متد navigate آن استفاده کرد که نمونههایی از آنرا در اینجا ملاحظه میکنید. پارامترهای ذکر شدهی در اینجا نیز همانند دایرکتیو routerLink هستند.
یک نکته: اگر به متد hideMessages دقت کنید، مقدار value کلید popup به نال تنظیم شدهاست. این مورد سبب خواهد شد تا outlet آن خالی شود. به این ترتیب متد hideMessages علاوه بر مخفی کردن لینک نمایش پیامها، پنل آنرا نیز از صفحه حذف میکند. شبیه به همین نکته در متد close کامپوننت پیامها که دکمهی بستن آنرا به همراه دارد، پیاده سازی شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-07.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
سازماندهی بهتر کامپوننتهای 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 { }
اکنون برای استفادهی از SharedBootstrapModule اختصاصی فوق، میتوان دو روش را بکار برد:
الف) import مستقیم آن در فایل app.module.ts
import { SharedBootstrapModule } from './shared/shared.bootstrap.module'; @NgModule({ imports: [BrowserModule, SharedBootstrapModule], // ... }) export class AppModule {}
و یا اگر فایل 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(); } }
سپس با فراخوانی متد 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 dialog، کار دریافت تائید و یا لغو انجام یک عملیات را انجام دهیم. چون این کامپوننت عمومی قرار است در بیش از یک ماژول استفاده شود، بنابراین نیاز است آنرا در Shared Module ثبت کرد. به همین جهت این کامپوننت را به نحو ذیل در پوشهی Shared ایجاد میکنیم:
ng g c Shared/ConfirmModal --skip-import
import { ConfirmModalComponent } from "./confirm-modal/confirm-modal.component"; @NgModule({ imports: [ ], entryComponents: [ ConfirmModalComponent ], declarations: [ ConfirmModalComponent ] }) export class SharedModule {}
این کامپوننت دریافت تائید کاربر به صورت ذیل تعریف میشود:
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; }
قالب این کامپوننت نیز بدون استفاده از 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>
تا اینجا یک کامپوننت نمایش دریافت تائید انجام عملیات را تهیه کردیم. در ادامه نیاز است یک سرویس را جهت بارگذاری پویای اینگونه کامپوننتهای مودال طراحی کنیم. این سرویس عمومی در پوشهی Core و CoreModule ثبت خواهد شد:
>ng g s Core/Modal
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(); }; }); } }
یک مودال در سه حالت ممکن است بسته شود:
الف) کلیک بر روی دکمهی 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 {}
<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!"; } }); } }
توسط this.modalService.show میتوان انواع و اقسام کامپوننتهای مودال را به صورت پویا بارگذاری کرد و نمایش داد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
Angular
1.3 might be the best Angular yet, but there's still lots of high impact work
to be done on the 1.x branch. We still have Material Design, our new router,
and improved internationalization of Angular apps still to come ...
The Angular 1.x project needs someone to be fully committed to keeping v1 moving forward, and supporting the Angular ecosystem that has grown around it.
That's why I'm delighted that Pete Bacon Darwin has agreed to take over the leadership role for Angular v1.
But Pete can't do this
alone. He needs Brian, Caitlin, and Chirayu to help make v1 even more awesome.
Jeff will help out with google3 sync and releases, and familiar faces like
Matias Niemela and Pawel Kozlowski (who co-wrote one of the first books on Angular with
Pete) will be ongoing contributors. We're also looking forward to meeting new
faces. Over time, some of these folks will shift their focus towards Angular 2,
but for the immediate future, what's most important is that Angular 1.x is and
continues to be well taken care of.
Intermediate & Advanced