در این قسمت میخواهیم روش تغییر رنگهای قالبهای پیشفرض Angular Material را به همراه تغییر پویای آنها در زمان اجرا، بررسی کنیم. همچنین Angular Material از راست به چپ نیز به خوبی پشتیبانی میکند که مثالی از آنرا در ادامه بررسی خواهیم کرد.
بررسی ساختار یک قالب Angular Material
قالب، مجموعهای از رنگها است که به کامپوننتهای Angular Material اعمال میشود. هر قالب از چندین جعبهرنگ یا palette تشکیل میشود:
- primary palette: به صورت گستردهای در تمام کامپوننتها مورد استفادهاست.
- accent palette: به المانهای تعاملی انتساب داده میشود.
- warn palette: برای نمایش خطاها و اخطارها بکار میرود.
- foreground palette: برای متون و آیکنها استفاده میشود.
- background palette: برای پسزمینهی المانها بکار میرود.
روش انتخاب این جعبه رنگها نیز به صورت زیر است:
در Angular Material تمام قالبها استاتیک بوده و در زمان کامپایل برنامه به صورت خودکار به آن اضافه میشوند. به همین جهت برنامه نیازی به تشکیل این اجزا و کامپایل یک قالب را در زمان آغاز آن ندارد.
همانطور که در قسمت اول این سری نیز بررسی کردیم، بستهی Angular Material به همراه چندین قالب از پیش طراحی شدهاست (قالبهای از پیش آمادهی متریال را در پوشهی node_modules\@angular\material\prebuilt-themes میتوانید مشاهده کنید) و در حین اجرای برنامه تنها یکی از آنها که در فایل styles.css ذکر شدهاست، مورد استفاده قرار میگیرد.
اگر نیاز به سفارشی سازی بیشتری وجود داشته باشد، میتوان قالبهای ویژهی خود را نیز طراحی کرد. این قالب جدید باید mat-core() sass mixin را import کند که حاوی تمام شیوهنامههای مشترک بین کامپوننتها است. این مورد باید تنها یکبار به کل برنامه الحاق شود تا حجم آنرا بیش از اندازه زیاد نکند. سپس این قالب سفارشی، جعبه رنگهای خاص خودش را معرفی میکند. در ادامه این جعبه رنگها توسط توابع mat-light-theme و یا mat-dark-theme ترکیب شده و مورد استفاده قرار میگیرند. سپس این قالب را include خواهیم کرد. به این ترتیب یک قالب سفارشی Angular Material، چنین طرحی را دارد:
اعداد و ارقامی را که در اینجا ملاحظه میکنید، در سیستم رنگهای طراحی متریال به darker hue و lighter hue تفسیر میشوند. همچنین امکان سفارشی سازی تایپوگرافی آن نیز وجود دارد.
ایجاد یک قالب سفارشی جدید Angular Material
برای ایجاد یک قالب سفارشی نیاز است از فایلهای sass استفاده کرد. بنابراین بهترین روش ایجاد برنامههای Angular Material در ابتدای کار، ذکر صریح نوع style مورد استفاده به sass است:
اگر اینکار را انجام ندادهایم و حالت پیشفرض پروژه همان css است، مهم نیست. میتوان فایل قالب سفارشی را در یک فایل با پسوند custom.theme.scss نیز در پوشهی src قرار داد و سپس آنرا در فایل angular.json مشخص کرد تا به صورت css کامپایل شده و مورد استفاده قرار گیرد:
بدیهی است اگر از ابتدا style=sass را تنظیم کرده بودیم، نیازی به ایجاد این فایل اضافی نبود و همان styles.scss اصلی را میشد ویرایش کرد و در این حالت فایل angular.json بدون تغییر باقی میماند.
پس از افزودن و تنظیم فایل custom.theme.scss، به فایل styles.css مراجعه کرده و قالب فعلی را به صورت comment در میآوریم:
سپس فایل src\custom.theme.scss را به صورت زیر تکمیل میکنیم:
که در اینجا شامل مراحل import فایلهای پایهی Angular Material، تعریف جعبه رنگ جدید، ترکیب آنها و در نهایت include آنها میباشد.
در اینجا روش تعریف یک قالب دوم (alternate-theme) را نیز مشاهده میکنید. علت تعریف قالب دوم در همین فایل جاری، کاهش حجم نهایی برنامه است. از این جهت که اگر alternate-themeها را در فایلهای scss دیگری قرار دهیم، مجبور به import تعاریف اولیهی قالبهای Angular Material در هرکدام به صورت جداگانهای خواهیم بود که حجم قابل ملاحظهای را به خود اختصاص میدهند. به همین جهت قالبهای دیگر را نیز در همینجا به صورت کلاسهای ثانویه تعریف خواهیم کرد.
در این حالت روش استفادهی از این قالب ثانویه به صورت زیر میباشد:
پس از افزودن فایل src\custom.theme.scss به برنامه، اگر آنرا اجرا کنیم به خروجی زیر خواهیم رسید:
افزودن امکان انتخاب پویای قالبها به برنامه
قصد داریم به منوی برنامه که اکنون گزینهی new contact را به همراه دارد، گزینهی toggle theme را هم جهت تغییر پویای قالب اصلی برنامه اضافه کنیم. به همین جهت فایل toolbar.component.html را گشوده و به صورت زیر تغییر میدهیم:
در اینجا دکمهای به منو اضافه شدهاست که سبب صدور رخدادی به والد آن یا همان sidenav خواهد شد. علت اینجا است که تغییر قالب را در sidenav، که در برگیرندهی router-outlet است، میتوان به کل برنامه اعمال کرد.
بنابراین جهت تبادل اطلاعات بین toolbar و sidenav از یک رخداد استفاده خواهیم کرد. برای این منظور فایل toolbar.component.ts را گشوده و این رخداد را به آن اضافه میکنیم:
پس از این تعریف، به sidenav.component.html مراجعه کرده و به این رخداد گوش فرا میدهیم:
متد toggleTheme را نیز به صورت زیر به sidenav.component.ts اضافه میکنیم:
در اینجا یک خاصیت عمومی boolean را با کلیک بر روی گزینهی منوی Toggle theme، به true و یا false تنظیم میکنیم. اکنون از این مقدار جهت تغییر css قالب sidenav استفاده خواهیم کرد:
به این ترتیب اگر مقدار isAlternateTheme مساوی true باشد، کلاس alternate-theme به قالب sidenav به صورت پویا اعمال خواهد شد و برعکس. در تصویر زیر نمونهای از تغییر پویای قالب برنامه را مشاهده میکنید:
افزودن پشتیبانی از راست به چپ به قالب برنامه
اگر به mat-sidenav-container ویژگی dir=rtl را اضافه کنیم، قالب برنامه راست به چپ خواهد شد. در ادامه میخواهیم شبیه به حالت تغییر پویای قالب سایت، گزینهای را به منوی برنامه جهت تغییر جهت برنامه نیز اضافه کنیم. برای این منظور به قالب toolbar.component.html مراجعه کرده و گزینهی Toggle dir را به آن اضافه میکنیم:
سپس این رخداد را که قرار است در نهایت به sidenav منتقل شود، به صورت زیر به toolbar.component.ts اضافه میکنیم:
اکنون در sidenav.component.html به این رخداد گوش فرا خواهیم داد:
متد toggleDir در sidenav.component.ts به صورت زیر پیاده سازی میشود:
و در نهایت این جهت را به mat-sidenav-container در فایل sidenav.component.html اعمال میکنیم:
در تصویر زیر نمونهای از تغییر پویای جهت برنامه را مشاهده میکنید:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-06.zip
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
بررسی ساختار یک قالب Angular Material
قالب، مجموعهای از رنگها است که به کامپوننتهای Angular Material اعمال میشود. هر قالب از چندین جعبهرنگ یا palette تشکیل میشود:
- primary palette: به صورت گستردهای در تمام کامپوننتها مورد استفادهاست.
- accent palette: به المانهای تعاملی انتساب داده میشود.
- warn palette: برای نمایش خطاها و اخطارها بکار میرود.
- foreground palette: برای متون و آیکنها استفاده میشود.
- background palette: برای پسزمینهی المانها بکار میرود.
روش انتخاب این جعبه رنگها نیز به صورت زیر است:
<mat-card> Main Theme: <button mat-raised-button color="primary"> Primary </button> <button mat-raised-button color="accent"> Accent </button> <button mat-raised-button color="warn"> Warning </button> </mat-card>
همانطور که در قسمت اول این سری نیز بررسی کردیم، بستهی Angular Material به همراه چندین قالب از پیش طراحی شدهاست (قالبهای از پیش آمادهی متریال را در پوشهی node_modules\@angular\material\prebuilt-themes میتوانید مشاهده کنید) و در حین اجرای برنامه تنها یکی از آنها که در فایل styles.css ذکر شدهاست، مورد استفاده قرار میگیرد.
اگر نیاز به سفارشی سازی بیشتری وجود داشته باشد، میتوان قالبهای ویژهی خود را نیز طراحی کرد. این قالب جدید باید mat-core() sass mixin را import کند که حاوی تمام شیوهنامههای مشترک بین کامپوننتها است. این مورد باید تنها یکبار به کل برنامه الحاق شود تا حجم آنرا بیش از اندازه زیاد نکند. سپس این قالب سفارشی، جعبه رنگهای خاص خودش را معرفی میکند. در ادامه این جعبه رنگها توسط توابع mat-light-theme و یا mat-dark-theme ترکیب شده و مورد استفاده قرار میگیرند. سپس این قالب را include خواهیم کرد. به این ترتیب یک قالب سفارشی Angular Material، چنین طرحی را دارد:
@import '~@angular/material/theming'; @include mat-core(); $candy-app-primary: mat-palette($mat-indigo); $candy-app-accent: mat-palette($mat-pink, A200, A100, A400); $candy-app-warn: mat-palette($mat-red); $candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn); @include angular-material-theme($candy-app-theme);
ذکر جعبه رنگ اخطار در اینجا اختیاری است و اگر ذکر نشود به قرمز تنظیم خواهد شد.
ایجاد یک قالب سفارشی جدید Angular Material
برای ایجاد یک قالب سفارشی نیاز است از فایلهای sass استفاده کرد. بنابراین بهترین روش ایجاد برنامههای Angular Material در ابتدای کار، ذکر صریح نوع style مورد استفاده به sass است:
ng new MyProjectName --style=sass
"styles": [ "node_modules/material-design-icons/iconfont/material-icons.css", "src/styles.css", "src/custom.theme.scss" ],
پس از افزودن و تنظیم فایل custom.theme.scss، به فایل styles.css مراجعه کرده و قالب فعلی را به صورت comment در میآوریم:
/* @import "~@angular/material/prebuilt-themes/indigo-pink.css"; */ body { margin: 0; }
@import '~@angular/material/theming'; @include mat-core(); $my-app-primary: mat-palette($mat-blue-grey); $my-app-accent: mat-palette($mat-pink, 500, 900, A100); $my-app-warn: mat-palette($mat-deep-orange); $my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn); @include angular-material-theme($my-app-theme); .alternate-theme { $alternate-primary: mat-palette($mat-light-blue); $alternate-accent: mat-palette($mat-yellow, 400); $alternate-theme: mat-light-theme($alternate-primary, $alternate-accent); @include angular-material-theme($alternate-theme); }
در اینجا روش تعریف یک قالب دوم (alternate-theme) را نیز مشاهده میکنید. علت تعریف قالب دوم در همین فایل جاری، کاهش حجم نهایی برنامه است. از این جهت که اگر alternate-themeها را در فایلهای scss دیگری قرار دهیم، مجبور به import تعاریف اولیهی قالبهای Angular Material در هرکدام به صورت جداگانهای خواهیم بود که حجم قابل ملاحظهای را به خود اختصاص میدهند. به همین جهت قالبهای دیگر را نیز در همینجا به صورت کلاسهای ثانویه تعریف خواهیم کرد.
در این حالت روش استفادهی از این قالب ثانویه به صورت زیر میباشد:
<mat-card class="alternate-theme"> Alternate Theme: <button mat-raised-button color="primary"> Primary </button> <button mat-raised-button color="accent"> Accent </button> <button mat-raised-button color="warn"> Warning </button> </mat-card>
پس از افزودن فایل src\custom.theme.scss به برنامه، اگر آنرا اجرا کنیم به خروجی زیر خواهیم رسید:
افزودن امکان انتخاب پویای قالبها به برنامه
قصد داریم به منوی برنامه که اکنون گزینهی new contact را به همراه دارد، گزینهی toggle theme را هم جهت تغییر پویای قالب اصلی برنامه اضافه کنیم. به همین جهت فایل toolbar.component.html را گشوده و به صورت زیر تغییر میدهیم:
<mat-menu #menu="matMenu"> <button mat-menu-item (click)="openAddContactDialog()">New Contact</button> <button mat-menu-item (click)="toggleTheme.emit()">Toggle theme</button> </mat-menu>
بنابراین جهت تبادل اطلاعات بین toolbar و sidenav از یک رخداد استفاده خواهیم کرد. برای این منظور فایل toolbar.component.ts را گشوده و این رخداد را به آن اضافه میکنیم:
export class ToolbarComponent implements OnInit { @Output() toggleTheme = new EventEmitter<void>();
<app-toolbar (toggleTheme)="toggleTheme()" (toggleSidenav)="sidenav.toggle()"></app-toolbar>
export class SidenavComponent { isAlternateTheme = false; toggleTheme() { this.isAlternateTheme = !this.isAlternateTheme; } }
<mat-sidenav-container fxLayout="row" class="app-sidenav-container" fxFill [class.alternate-theme]="isAlternateTheme">
افزودن پشتیبانی از راست به چپ به قالب برنامه
اگر به mat-sidenav-container ویژگی dir=rtl را اضافه کنیم، قالب برنامه راست به چپ خواهد شد. در ادامه میخواهیم شبیه به حالت تغییر پویای قالب سایت، گزینهای را به منوی برنامه جهت تغییر جهت برنامه نیز اضافه کنیم. برای این منظور به قالب toolbar.component.html مراجعه کرده و گزینهی Toggle dir را به آن اضافه میکنیم:
<mat-menu #menu="matMenu"> <button mat-menu-item (click)="openAddContactDialog()">New Contact</button> <button mat-menu-item (click)="toggleTheme.emit()">Toggle theme</button> <button mat-menu-item (click)="toggleDir.emit()">Toggle dir</button> </mat-menu>
export class ToolbarComponent implements OnInit { @Output() toggleDir = new EventEmitter<void>();
<app-toolbar (toggleDir)="toggleDir()" (toggleTheme)="toggleTheme()" (toggleSidenav)="sidenav.toggle()"></app-toolbar>
export class SidenavComponent implements OnInit, OnDestroy { dir = "ltr"; toggleDir() { this.dir = this.dir === "ltr" ? "rtl" : "ltr"; } }
<mat-sidenav-container fxLayout="row" class="app-sidenav-container" fxFill [dir]="dir" [class.alternate-theme]="isAlternateTheme">
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-06.zip
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
در مطلب «کنترل نرخ ورود اطلاعات در برنامههای Angular» جزئیات پیاده سازی جستجوی همزمان با تایپ کاربر، بررسی شدند. در اینجا میخواهیم از اطلاعات آن مطلب جهت پیاده سازی یک AutoComplete جستجوی نام کاربران که اطلاعات آن از سرور تامین میشوند، استفاده کنیم:
استفاده از کامپوننت AutoComplete کتابخانهی Angular Material
کتابخانهی Angular Material به همراه یک کامپوننت Auto Complete نیز هست. در اینجا قصد داریم آنرا در یک صفحهی دیالوگ جدید نمایش دهیم و با انتخاب کاربری از لیست توصیههای آن و کلیک بر روی دکمهی نمایش آن کاربر، جزئیات کاربر یافت شده را نمایش دهیم.
به همین جهت ابتدا کامپوننت جدید search-auto-complete را به صورت زیر به مجموعهی کامپوننتهای تعریف شده اضافه میکنیم:
همچنین چون قصد داریم آنرا درون یک popup نمایش دهیم، نیاز است به ماژول contact-manager\contact-manager.module.ts مراجعه کرده و آنرا به لیست entryComponents نیز اضافه کنیم:
در ادامه برای نمایش این کامپوننت به صورت popup، دکمهی جدید جستجو را به toolbar اضافه میکنیم:
برای این منظور به فایل toolbar\toolbar.component.html مراجعه کرده و دکمهی جستجو را پیش از دکمهی نمایش منو، قرار میدهیم:
با این کدها برای مدیریت متد openSearchDialog در فایل toolbar\toolbar.component.ts
در اینجا توسط سرویس MatDialog، کامپوننت SearchAutoCompleteComponent به صورت پویا بارگذاری شده و به صورت یک popup نمایش داده میشود. سپس مشترک رخداد بسته شدن آن شده و بر اساس اطلاعات کاربری که توسط آن بازگشت داده میشود، سبب هدایت صفحهی جاری به صفحهی جزئیات این کاربر یافت شده، خواهیم شد.
کنترلر جستجوی سمت سرور و سرویس سمت کلاینت استفاده کنندهی از آن
در اینجا کنترلر و اکشن متدی را جهت جستجوی قسمتی از نام کاربران را مشاهده میکنید:
کدهای کامل متد SearchUsersAsync در مخزن کد این سری موجود هستند.
از این کنترلر به نحو ذیل در برنامهی Angular برای ارسال اطلاعات و انجام جستجو استفاده میشود:
در اینجا از اپراتور pipe مخصوص RxJS 6x استفاده شدهاست.
تکمیل کامپوننت جستجوی کاربران توسط یک AutoComplete
پس از این مقدمات که شامل تکمیل سرویسهای سمت سرور و کلاینت دریافت اطلاعات کاربران جستجو شده و نمایش صفحهی جستجو به صورت یک popup است، اکنون میخواهیم محتوای این popup را تکمیل کنیم. البته در اینجا فرض بر این است که مطلب «کنترل نرخ ورود اطلاعات در برنامههای Angular» را پیشتر مطالعه کردهاید و با جزئیات آن آشنایی دارید.
تکمیل قالب search-auto-complete.component.html
در این مثال چون کامپوننت search-auto-complete به صورت یک popup ظاهر خواهد شد، ساختار عنوان، محتوا و دکمههای دیالوگ در آن پیاده سازی شدهاند.
سپس نحوهی اتصال یک Input box معمولی را به کامپوننت mat-autocomplete مشاهده میکنید که شامل این موارد است:
- جعبه متنی که قرار است به یک mat-autocomplete متصل شود، توسط دایرکتیو matAutocomplete به template reference variable تعریف شدهی در آن autocomplete اشاره میکند. برای مثال در اینجا این متغیر auto1 است.
- برای انتقال دکمههای فشرده شدهی در input box به کامپوننت، از رخداد input استفاده شدهاست. این روش با هر دو نوع حالت مدیریت فرمهای Angular سازگاری دارد و کدهای آن یکی است.
در کامپوننت mat-autocomplete این تنظیمات صورت گرفتهاند:
- در لیست ظاهر شدهی توسط یک autocomplete، هر نوع ظاهری را میتوان طراحی کرد. برای مثال در اینجا نام و id کاربر نمایش داده میشوند. اما برای تعیین اینکه پس از انتخاب یک آیتم از لیست، چه گزینهای در input box ظاهر شود، از خاصیت displayWith که در اینجا به متد displayFn کامپوننت متصل شدهاست، کمک گرفته خواهد شد.
- از رخداد optionSelected برای دریافت آیتم انتخاب شده، در کدهای کامپوننت استفاده میشود.
- در آخر کار نمایش لیستی از کاربران توسط mat-optionها انجام میشود. در اینجا برای اینکه بتوان تاخیر دریافت اطلاعات از سرور را توسط یک mat-spinner نمایش داد، از خاصیت isLoading تعریف شدهی در کامپوننت استفاده خواهد شد.
تکمیل کامپوننت search-auto-complete.component.ts
کدهای کامل این کامپوننت را در ادامه مشاهده میکنید:
- در ابتدای کار کامپوننت، یک modelChanged از نوع Subject اضافه شدهاست. در این حالت با فراخوانی متد next آن در onSearchChange که به رخداد input جعبهی متنی دریافت اطلاعات متصل است، کار انتقال این تغییرات به اشتراک ایجاد شدهی به آن در ngOnInit انجام میشود. در اینجا بر اساس نکات مطلب «کنترل نرخ ورود اطلاعات در برنامههای Angular»، عبارات وارد شده، به سمت سرور ارسال و در نهایت نتیجهی آن به خاصیت عمومی filteredUsers که به حلقهی نمایش اطلاعات mat-autocomplete متصل است، انتساب داده میشود. در ابتدای اتصال به سرور، خاصیت isLoading به true و در پایان عملیات به false تنظیم خواهد شد تا mat-spinner را نمایش داده و یا مخفی کند.
- توسط متد displayFn، عبارتی که در نهایت پس از انتخاب از لیست نمایش داده شده در input box قرار میگیرد، مشخص خواهد شد.
- در متد onOptionSelected، میتوان به شیء انتخاب شدهی توسط کاربر از لیست mat-autocomplete دسترسی داشت.
- این شیء انتخاب شده را در متد showUser و توسط سرویس MatDialogRef به کامپوننت toolbar که در حال گوش فرادادن به رخداد بسته شدن کامپوننت جاری است، ارسال میکنیم. به این صورت است که کامپوننت toolbar میتواند کار هدایت به جزئیات این کاربر را انجام دهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
استفاده از کامپوننت AutoComplete کتابخانهی Angular Material
کتابخانهی Angular Material به همراه یک کامپوننت Auto Complete نیز هست. در اینجا قصد داریم آنرا در یک صفحهی دیالوگ جدید نمایش دهیم و با انتخاب کاربری از لیست توصیههای آن و کلیک بر روی دکمهی نمایش آن کاربر، جزئیات کاربر یافت شده را نمایش دهیم.
به همین جهت ابتدا کامپوننت جدید search-auto-complete را به صورت زیر به مجموعهی کامپوننتهای تعریف شده اضافه میکنیم:
ng g c contact-manager/components/search-auto-complete --no-spec
import { SearchAutoCompleteComponent } from "./components/search-auto-complete/search-auto-complete.component"; @NgModule({ entryComponents: [ SearchAutoCompleteComponent ] }) export class ContactManagerModule { }
در ادامه برای نمایش این کامپوننت به صورت popup، دکمهی جدید جستجو را به toolbar اضافه میکنیم:
برای این منظور به فایل toolbar\toolbar.component.html مراجعه کرده و دکمهی جستجو را پیش از دکمهی نمایش منو، قرار میدهیم:
<span fxFlex="1 1 auto"></span> <button mat-button (click)="openSearchDialog()"> <mat-icon>search</mat-icon> </button> <button mat-button [matMenuTriggerFor]="menu"> <mat-icon>more_vert</mat-icon> </button>
@Component() export class ToolbarComponent { constructor( private dialog: MatDialog, private router: Router) { } openSearchDialog() { const dialogRef = this.dialog.open(SearchAutoCompleteComponent, { width: "650px" }); dialogRef.afterClosed().subscribe((result: User) => { console.log("The SearchAutoComplete dialog was closed", result); if (result) { this.router.navigate(["/contactmanager", result.id]); } }); } }
کنترلر جستجوی سمت سرور و سرویس سمت کلاینت استفاده کنندهی از آن
در اینجا کنترلر و اکشن متدی را جهت جستجوی قسمتی از نام کاربران را مشاهده میکنید:
namespace MaterialAspNetCoreBackend.WebApp.Controllers { [Route("api/[controller]")] public class TypeaheadController : Controller { private readonly IUsersService _usersService; public TypeaheadController(IUsersService usersService) { _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); } [HttpGet("[action]")] public async Task<IActionResult> SearchUsers(string term) { return Ok(await _usersService.SearchUsersAsync(term)); } } }
از این کنترلر به نحو ذیل در برنامهی Angular برای ارسال اطلاعات و انجام جستجو استفاده میشود:
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { catchError, map } from "rxjs/operators"; import { User } from "../models/user"; @Injectable({ providedIn: "root" }) export class UserService { constructor(private http: HttpClient) { } searchUsers(term: string): Observable<User[]> { return this.http .get<User[]>(`/api/Typeahead/SearchUsers?term=${encodeURIComponent(term)}`) .pipe( map(response => response || []), catchError((error: HttpErrorResponse) => throwError(error)) ); } }
تکمیل کامپوننت جستجوی کاربران توسط یک AutoComplete
پس از این مقدمات که شامل تکمیل سرویسهای سمت سرور و کلاینت دریافت اطلاعات کاربران جستجو شده و نمایش صفحهی جستجو به صورت یک popup است، اکنون میخواهیم محتوای این popup را تکمیل کنیم. البته در اینجا فرض بر این است که مطلب «کنترل نرخ ورود اطلاعات در برنامههای Angular» را پیشتر مطالعه کردهاید و با جزئیات آن آشنایی دارید.
تکمیل قالب search-auto-complete.component.html
<h2 mat-dialog-title>Search</h2> <mat-dialog-content> <div fxLayout="column"> <mat-form-field class="example-full-width"> <input matInput placeholder="Choose a user" [matAutocomplete]="auto1" (input)="onSearchChange($event.target.value)"> </mat-form-field> <mat-autocomplete #auto1="matAutocomplete" [displayWith]="displayFn" (optionSelected)="onOptionSelected($event)"> <mat-option *ngIf="isLoading" class="is-loading"> <mat-spinner diameter="50"></mat-spinner> </mat-option> <ng-container *ngIf="!isLoading"> <mat-option *ngFor="let user of filteredUsers" [value]="user"> <span>{{ user.name }}</span> <small> | ID: {{user.id}}</small> </mat-option> </ng-container> </mat-autocomplete> </div> </mat-dialog-content> <mat-dialog-actions> <button mat-button color="primary" (click)="showUser()"> <mat-icon>search</mat-icon> Show User </button> <button mat-button color="primary" [mat-dialog-close]="true"> <mat-icon>cancel</mat-icon> Close </button> </mat-dialog-actions>
سپس نحوهی اتصال یک Input box معمولی را به کامپوننت mat-autocomplete مشاهده میکنید که شامل این موارد است:
- جعبه متنی که قرار است به یک mat-autocomplete متصل شود، توسط دایرکتیو matAutocomplete به template reference variable تعریف شدهی در آن autocomplete اشاره میکند. برای مثال در اینجا این متغیر auto1 است.
- برای انتقال دکمههای فشرده شدهی در input box به کامپوننت، از رخداد input استفاده شدهاست. این روش با هر دو نوع حالت مدیریت فرمهای Angular سازگاری دارد و کدهای آن یکی است.
در کامپوننت mat-autocomplete این تنظیمات صورت گرفتهاند:
- در لیست ظاهر شدهی توسط یک autocomplete، هر نوع ظاهری را میتوان طراحی کرد. برای مثال در اینجا نام و id کاربر نمایش داده میشوند. اما برای تعیین اینکه پس از انتخاب یک آیتم از لیست، چه گزینهای در input box ظاهر شود، از خاصیت displayWith که در اینجا به متد displayFn کامپوننت متصل شدهاست، کمک گرفته خواهد شد.
- از رخداد optionSelected برای دریافت آیتم انتخاب شده، در کدهای کامپوننت استفاده میشود.
- در آخر کار نمایش لیستی از کاربران توسط mat-optionها انجام میشود. در اینجا برای اینکه بتوان تاخیر دریافت اطلاعات از سرور را توسط یک mat-spinner نمایش داد، از خاصیت isLoading تعریف شدهی در کامپوننت استفاده خواهد شد.
تکمیل کامپوننت search-auto-complete.component.ts
کدهای کامل این کامپوننت را در ادامه مشاهده میکنید:
import { Component, OnDestroy, OnInit } from "@angular/core"; import { MatAutocompleteSelectedEvent, MatDialogRef } from "@angular/material"; import { Subject, Subscription } from "rxjs"; import { debounceTime, distinctUntilChanged, finalize, switchMap, tap } from "rxjs/operators"; import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component({ selector: "app-search-auto-complete", templateUrl: "./search-auto-complete.component.html", styleUrls: ["./search-auto-complete.component.css"] }) export class SearchAutoCompleteComponent implements OnInit, OnDestroy { private modelChanged: Subject<string> = new Subject<string>(); private dueTime = 300; private modelChangeSubscription: Subscription; private selectedUser: User = null; filteredUsers: User[] = []; isLoading = false; constructor( private userService: UserService, private dialogRef: MatDialogRef<SearchAutoCompleteComponent>) { } ngOnInit() { this.modelChangeSubscription = this.modelChanged .pipe( debounceTime(this.dueTime), distinctUntilChanged(), tap(() => this.isLoading = true), switchMap(inputValue => this.userService.searchUsers(inputValue).pipe( finalize(() => this.isLoading = false) ) ) ) .subscribe(users => { this.filteredUsers = users; }); } ngOnDestroy() { if (this.modelChangeSubscription) { this.modelChangeSubscription.unsubscribe(); } } onSearchChange(value: string) { this.modelChanged.next(value); } displayFn(user: User) { if (user) { return user.name; } } onOptionSelected(event: MatAutocompleteSelectedEvent) { console.log("Selected user", event.option.value); this.selectedUser = event.option.value as User; } showUser() { if (this.selectedUser) { this.dialogRef.close(this.selectedUser); } } }
- توسط متد displayFn، عبارتی که در نهایت پس از انتخاب از لیست نمایش داده شده در input box قرار میگیرد، مشخص خواهد شد.
- در متد onOptionSelected، میتوان به شیء انتخاب شدهی توسط کاربر از لیست mat-autocomplete دسترسی داشت.
- این شیء انتخاب شده را در متد showUser و توسط سرویس MatDialogRef به کامپوننت toolbar که در حال گوش فرادادن به رخداد بسته شدن کامپوننت جاری است، ارسال میکنیم. به این صورت است که کامپوننت toolbar میتواند کار هدایت به جزئیات این کاربر را انجام دهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
اشتراکها
قالب ادمین مبتنی بر angular material
کتابخانهی Angular Material تعدادی کامپوننت زیبای با قابلیت استفادهی مجدد، به خوبی آزمایش شده و با قابلیت دسترسی بالا را بر اساس الگوهای Material Design ارائه میدهد. برای توسعه دهندگان Angular، کتابخانهی Angular Material پیاده سازی مرجع رهنمودهای طراحی متریال گوگل است که توسط تیم اصلی Angular پیاده سازی و توسعه داده میشود. در این سری، مفاهیم طراحی نگارش 6x این کتابخانه را به همراه نحوهی برپایی و تنظیم آن و همچنین کار با کامپوننتهای پیشرفتهی آن، بررسی خواهیم کرد.
منابع و مآخذ مرتبط با کتابخانهی Angular Material
در اینجا مآخذ اصلی کار با این کتابخانه را ملاحظه میکنید که شامل اصول طراحی متریال و مخازن اصلی توسعهی آن میباشند:
Material Design Specification
- https://material.io/design
Angular Material
- https://material.angular.io
- https://github.com/angular/material2
مفاهیم پایهی طراحی متریال
چرا «زیبایی» رابط کاربری مهم است؟
در ابتدای معرفی کتابخانهی Angular Material عنوان شد که این مجموعه به همراه تعدادی کامپوننت «زیبا» است. بنابراین این سؤال مطرح میشود که چرا و یا تا چه اندازه «زیبایی» رابط کاربری اهمیت دارد؟ مهمترین دلیل آن بهبود تجربهی کاربری است. بر اساس تحقیقاتی که بر روی کاربران بسیاری صورت گرفتهاست، مشخص شدهاست کاربران، با رابطهای کاربری زیبا نتایج بهتری را از لحاظ کاهش زمان اتمام کار و تعداد خطاهای مرتبط دریافت میکنند.
اما ... طراحی برنامههای زیبا مشکل است. به همین جهت استفاده از کتابخانههای غنی مانند طراحی متریال که این امر را سهولت میبخشند، ضروری است. طراحی متریال یک زبان کامل طراحی برنامههای زیبا است. توسط گوگل طراحی شدهاست و دو هدف اصلی را دنبال میکند:
- وفاداری به اصول کلاسیک طراحی رابط کاربری
- ارائهی تجربهی کاربری یکدست و هماهنگ، در بین وسایل و اندازههای صفحات نمایشی مختلف
اصول پایهی طراحی متریال نیز شامل موارد زیر است:
- «متریال» یک متافور است و بر اساس مطالعهی نحوهی کار با کاغذ، مرکب و ارتباط بین اشیاء در دنیای واقعی پدید آمدهاست.
- اشیاء در دنیای واقعی دارای ارتباطهای ابعادی و حجمی هستند. برای مثال دو برگهی کاغذ یک فضا را اشغال نمیکنند. طراحی متریال برای نمایش این ارتباط سه بعدی بین اشیاء، از نور و سایه استفاده میکند.
- در دنیای واقعی، اشیاء از درون یکدیگر رد نمیشوند. این مورد در طراحی متریال نیز صادق است.
- طراحی متریال به همراه جعبهی رنگ مخصوص و بکارگیری فضاهای خالی و عناوین درشت بسیار مشخص، واضح و عمدی است.
- طراحی متریال به همراه حرکت و پویانمایی، جهت ارائهی مفاهیم مختلف به کاربر، جهت درک بهتر او از برنامه است.
برپایی پیشنیازهای ابتدایی کار با Angular Material
پیش از ادامهی بحث فرض بر این است که آخرین نگارش Angular CLI را نصب کردهاید و اگر پیشتر آنرا نصب کردهاید، یکبار دستور ذیل را اجرا کنید تا تمام وابستگیهای سراسری نصب شدهی در سیستم به صورت خودکار به روز رسانی شوند:
سپس برنامهی کلاینت Angular این سری را به همراه تنظیمات ابتدایی مسیریابی آن از طریق صدور فرمان ذیل آغاز میکنیم:
پس از ایجاد ساختار اولیهی برنامه و نصب خودکار وابستگیهای آن، جهت آزمایش برنامه، به پوشهی آن وارد شده و آنرا اجرا میکنیم:
که به این ترتیب برنامه در آدرس http://localhost:4200 و مرورگر پیشفرض سیستم نمایش داده خواهد شد.
افزودن کتابخانهی Angular Material به برنامه
در طول این سری از سایت https://material.angular.io زیاد استفاده خواهیم کرد. همواره به روزترین روش افزودن کتابخانهی Angular Material به یک برنامهی موجود را در آدرس https://material.angular.io/guide/getting-started میتوانید مشاهده کنید که خلاصهی آن به صورت زیر است:
البته در Angular 6 روش تفصیلی نصب فوق که شامل 6 مرحلهاست، به صورت زیر هم خلاصه شدهاست:
متاسفانه در زمان نگارش این مطلب، نگارش 6.3.1 آن توسط دستور فوق نصب نشد و خطای «Error: Collection "@angular/material" cannot be resolved.» ظاهر گردید. البته روش رفع آن در اینجا بحث شدهاست که مهم نیست و در نگارشهای رسمی بعدی حتما لحاظ خواهد شد. به همین جهت روش تفصیلی آنرا که همیشه کار میکند، در ادامه پیگیری میکنیم. ابتدا بستههای ذیل را نصب کنید:
- دستور اول angular/cdk و angular/material را نصب میکند. cdk در اینجا به معنای کیت توسعهی کامپوننتهای Angular است که امکان استفادهی از ویژگیهای Angular Material را بدون الزامی به پیروی از زبان طراحی متریال، میسر میکند.
- همانطور که عنوان شد، طراحی متریال مبتنی بر حرکت و پویانمایی است. به همین جهت تعدادی از کامپوننتهای آن نیاز به بستهی angular/animations را دارند که توسط دستور دوم نصب میشود.
- دستور سوم نیز کامپوننتهای slide و slider را پشتیبانی میکند (Gesture Support). البته پس نصب این وابستگی، نیاز است به فایل src/main.ts مراجعه کرده و یک سطر زیر را نیز افزود:
در ادامه پس از نصب بستهی پویانمایی، به فایل app.module.ts مراجعه کرده و BrowserAnimationsModule را به لیست imports اضافه میکنیم:
مدیریت بهتر import کامپوننتهای Angular Material
در ادامه به ازای هر کامپوننت Angular Material باید ماژول آنرا به لیست imports افزود که پس از مدتی به یک فایل app.module.ts بسیار شلوغ خواهیم رسید. برای مدیریت بهتر این فایل، از روش مطرح شدهی در مطلب «سازماندهی برنامههای Angular» استفاده خواهیم کرد.
به همین جهت دو پوشهی core و shared را درون پوشهی src/app ایجاد میکنیم:
محتویات فایل src\app\core\core.module.ts به صورت زیر است:
در مورد جزئیات آن در مطلب «سازماندهی برنامههای Angular توسط ماژولها» کاملا بحث شدهاست.
محتویات فایل src\app\shared\shared.module.ts نیز به صورت زیر است:
سپس تعاریف import این دو فایل را به فایل app.module.ts اضافه میکنیم:
پس از این مقدمات، فایل جدید src\app\shared\material.module.ts را در پوشهی shared ایجاد میکنیم تا بتوانیم مداخل کامپوننتهای Angular Material را صرفا به آن اضافه کنیم؛ با این محتوا:
در اینجا هر کامپوننت مورد نیاز، به قسمتهای import و exports اضافه شدهاند.
سپس MaterialModule را نیز به قسمتهای imports و exports فایل src\app\shared\shared.module.ts اضافه خواهیم کرد:
به این ترتیب در هر ماژول جدیدی که به برنامه اضافه شود و نیاز به کار با Angular Material را داشته باشد، تنها کافی است SharedModule را import کرد؛ مانند app.module.ts برنامه (البته بدون ذکر متد forRoot آن که این forRoot فقط محض ماژول اصلی برنامه است).
تا اینجا جهت اطمینان از اجرای برنامه، دستور ng serve -o را از ابتدا اجرا کنید.
افزودن چند کامپوننت مقدماتی متریال به برنامه
بهترین روش کار با این مجموعه، بررسی مستندات آن در سایت https://material.angular.io/components است. برای مثال برای افزودن دکمه، به مستندات آن مراجعه کرده و بر روی دکمهی view source کلیک میکنیم:
سپس کدهای قسمت HTML آنرا به برنامه و فایل app.component.html اضافه خواهیم کرد:
به همین ترتیب مستندات check box را یافته و آنرا نیز اضافه میکنیم:
تا اینجا اگر برنامه را توسط دستور ng serve -o اجرا کنیم، یک چنین خروجی حاصل میشود:
البته شکل ظاهری آنها تا اینجا آنچنان مطلوب نیست. برای رفع این مشکل، نیاز است یک قالب را به این کنترلها و کامپوننتها اعمال کرد. به همین جهت فایل styles.css واقع در ریشهی برنامه را گشوده و قالب پیشفرض متریال را به آن اضافه میکنیم:
قالبهای از پیش آمادهی متریال را در پوشهی node_modules\@angular\material\prebuilt-themes میتوانید مشاهده کنید.
پس از اعمال قالب، اکنون است که شکل ظاهری کنترلهای آن بسیار بهتر شدهاند و همچنین کار با آنها به همراه پویانمایی نیز شدهاست:
افزودن آیکنهای متریال به برنامه
مرحلهی آخر این تنظیمات، افزودن آیکنهای متریال به برنامهاست. برای این منظور فایل src\index.html را گشوده و یک سطر ذیل را به head اضافه کنید:
برای آزمایش آن، به فایل app.component.html مراجعه کرده و تعریف دکمهای را که اضافه کردیم، به صورت ذیل با افزودن mat-icon تغییر میدهیم:
که این خروجی را تولید میکند:
لیست کامل این آیکنها را به همراه توضیحات تکمیلی آنها، در آدرس ذیل میتوانید ملاحظه کنید:
http://google.github.io/material-design-icons
البته چون ما نمیخواهیم این آیکنها را از وب بارگذاری کنیم، برای نصب محلی آنها ابتدا دستور زیر را در ریشهی پروژه صادر کنید:
این آیکن فونتها پس از نصب، در مسیر node_modules\material-design-icons\iconfont قابل مشاهده هستند:
همانطور که مشاهده میکنید، برای استفادهی از این فایلهای آیکن فونت محلی، تنها کافی است فایل material-icons.css را به برنامه معرفی کنیم. برای این منظور فایل angular.json را گشوده و قسمت styles آنرا به صورت زیر تکمیل میکنیم:
اکنون دیگر نیازی به ذکر link href اضافه شدهی به فایل src\index.html نداریم و باید از آن حذف شود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-01.zip
برای اجرای آن نیز ابتدا فایل restore.bat و سپس فایل ng-serve.bat را اجرا کنید.
منابع و مآخذ مرتبط با کتابخانهی Angular Material
در اینجا مآخذ اصلی کار با این کتابخانه را ملاحظه میکنید که شامل اصول طراحی متریال و مخازن اصلی توسعهی آن میباشند:
Material Design Specification
- https://material.io/design
Angular Material
- https://material.angular.io
- https://github.com/angular/material2
مفاهیم پایهی طراحی متریال
چرا «زیبایی» رابط کاربری مهم است؟
در ابتدای معرفی کتابخانهی Angular Material عنوان شد که این مجموعه به همراه تعدادی کامپوننت «زیبا» است. بنابراین این سؤال مطرح میشود که چرا و یا تا چه اندازه «زیبایی» رابط کاربری اهمیت دارد؟ مهمترین دلیل آن بهبود تجربهی کاربری است. بر اساس تحقیقاتی که بر روی کاربران بسیاری صورت گرفتهاست، مشخص شدهاست کاربران، با رابطهای کاربری زیبا نتایج بهتری را از لحاظ کاهش زمان اتمام کار و تعداد خطاهای مرتبط دریافت میکنند.
اما ... طراحی برنامههای زیبا مشکل است. به همین جهت استفاده از کتابخانههای غنی مانند طراحی متریال که این امر را سهولت میبخشند، ضروری است. طراحی متریال یک زبان کامل طراحی برنامههای زیبا است. توسط گوگل طراحی شدهاست و دو هدف اصلی را دنبال میکند:
- وفاداری به اصول کلاسیک طراحی رابط کاربری
- ارائهی تجربهی کاربری یکدست و هماهنگ، در بین وسایل و اندازههای صفحات نمایشی مختلف
اصول پایهی طراحی متریال نیز شامل موارد زیر است:
- «متریال» یک متافور است و بر اساس مطالعهی نحوهی کار با کاغذ، مرکب و ارتباط بین اشیاء در دنیای واقعی پدید آمدهاست.
- اشیاء در دنیای واقعی دارای ارتباطهای ابعادی و حجمی هستند. برای مثال دو برگهی کاغذ یک فضا را اشغال نمیکنند. طراحی متریال برای نمایش این ارتباط سه بعدی بین اشیاء، از نور و سایه استفاده میکند.
- در دنیای واقعی، اشیاء از درون یکدیگر رد نمیشوند. این مورد در طراحی متریال نیز صادق است.
- طراحی متریال به همراه جعبهی رنگ مخصوص و بکارگیری فضاهای خالی و عناوین درشت بسیار مشخص، واضح و عمدی است.
- طراحی متریال به همراه حرکت و پویانمایی، جهت ارائهی مفاهیم مختلف به کاربر، جهت درک بهتر او از برنامه است.
برپایی پیشنیازهای ابتدایی کار با Angular Material
پیش از ادامهی بحث فرض بر این است که آخرین نگارش Angular CLI را نصب کردهاید و اگر پیشتر آنرا نصب کردهاید، یکبار دستور ذیل را اجرا کنید تا تمام وابستگیهای سراسری نصب شدهی در سیستم به صورت خودکار به روز رسانی شوند:
npm update -g
ng new MaterialAngularClient --routing
cd MaterialAngularClient ng serve -o
افزودن کتابخانهی Angular Material به برنامه
در طول این سری از سایت https://material.angular.io زیاد استفاده خواهیم کرد. همواره به روزترین روش افزودن کتابخانهی Angular Material به یک برنامهی موجود را در آدرس https://material.angular.io/guide/getting-started میتوانید مشاهده کنید که خلاصهی آن به صورت زیر است:
البته در Angular 6 روش تفصیلی نصب فوق که شامل 6 مرحلهاست، به صورت زیر هم خلاصه شدهاست:
ng add @angular/material
npm install --save @angular/material @angular/cdk npm install --save @angular/animations npm install --save hammerjs
- همانطور که عنوان شد، طراحی متریال مبتنی بر حرکت و پویانمایی است. به همین جهت تعدادی از کامپوننتهای آن نیاز به بستهی angular/animations را دارند که توسط دستور دوم نصب میشود.
- دستور سوم نیز کامپوننتهای slide و slider را پشتیبانی میکند (Gesture Support). البته پس نصب این وابستگی، نیاز است به فایل src/main.ts مراجعه کرده و یک سطر زیر را نیز افزود:
import "hammerjs";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule ] }) export class AppModule { }
مدیریت بهتر import کامپوننتهای Angular Material
در ادامه به ازای هر کامپوننت Angular Material باید ماژول آنرا به لیست imports افزود که پس از مدتی به یک فایل app.module.ts بسیار شلوغ خواهیم رسید. برای مدیریت بهتر این فایل، از روش مطرح شدهی در مطلب «سازماندهی برنامههای Angular» استفاده خواهیم کرد.
به همین جهت دو پوشهی core و shared را درون پوشهی src/app ایجاد میکنیم:
محتویات فایل src\app\core\core.module.ts به صورت زیر است:
import { CommonModule } from "@angular/common"; import { NgModule, Optional, SkipSelf } from "@angular/core"; import { RouterModule } from "@angular/router"; @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: [ /* ``No`` global singleton services of the whole app should be listed here anymore! Since they'll be already provided in AppModule using the `tree-shakable providers` of Angular 6.x+ (providedIn: 'root'). This new feature allows cleaning up the providers section from the CoreModule. But if you want to provide something with an InjectionToken other that its class, you still have to use this section. */ ] }) export class CoreModule { constructor(@Optional() @SkipSelf() core: CoreModule) { if (core) { throw new Error("CoreModule should be imported ONLY in AppModule."); } } }
محتویات فایل src\app\shared\shared.module.ts نیز به صورت زیر است:
import { CommonModule } from "@angular/common"; import { HttpClientModule } from "@angular/common/http"; import { ModuleWithProviders, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; @NgModule({ imports: [ CommonModule, FormsModule, HttpClientModule ], entryComponents: [ // All components about to be loaded "dynamically" need to be declared in the entryComponents section. ], declarations: [ // common and shared components/directives/pipes between more than one module and components will be listed here. ], exports: [ // common and shared components/directives/pipes between more than one module and components will be listed here. CommonModule, FormsModule, HttpClientModule, ] /* 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`. */] }; } }
import { CoreModule } from "./core/core.module"; import { SharedModule } from "./shared/shared.module"; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, CoreModule, SharedModule.forRoot(), AppRoutingModule ] }) export class AppModule { }
import { CdkTableModule } from "@angular/cdk/table"; import { NgModule } from "@angular/core"; import { MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatExpansionModule, MatFormFieldModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatNativeDateModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatStepperModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, } from "@angular/material"; @NgModule({ imports: [ MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatExpansionModule, MatFormFieldModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatNativeDateModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule, MatStepperModule, MatSortModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, CdkTableModule ], exports: [ MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatExpansionModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatNativeDateModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule, MatStepperModule, MatSortModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, CdkTableModule ] }) export class MaterialModule { }
سپس MaterialModule را نیز به قسمتهای imports و exports فایل src\app\shared\shared.module.ts اضافه خواهیم کرد:
import { MaterialModule } from "./material.module"; @NgModule({ imports: [ CommonModule, FormsModule, HttpClientModule, MaterialModule ], exports: [ // common and shared components/directives/pipes between more than one module and components will be listed here. CommonModule, FormsModule, HttpClientModule, MaterialModule ] }) export class SharedModule { }
تا اینجا جهت اطمینان از اجرای برنامه، دستور ng serve -o را از ابتدا اجرا کنید.
افزودن چند کامپوننت مقدماتی متریال به برنامه
بهترین روش کار با این مجموعه، بررسی مستندات آن در سایت https://material.angular.io/components است. برای مثال برای افزودن دکمه، به مستندات آن مراجعه کرده و بر روی دکمهی view source کلیک میکنیم:
سپس کدهای قسمت HTML آنرا به برنامه و فایل app.component.html اضافه خواهیم کرد:
<button mat-button>Click me!</button>
<mat-checkbox>Check me!</mat-checkbox>
البته شکل ظاهری آنها تا اینجا آنچنان مطلوب نیست. برای رفع این مشکل، نیاز است یک قالب را به این کنترلها و کامپوننتها اعمال کرد. به همین جهت فایل styles.css واقع در ریشهی برنامه را گشوده و قالب پیشفرض متریال را به آن اضافه میکنیم:
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
پس از اعمال قالب، اکنون است که شکل ظاهری کنترلهای آن بسیار بهتر شدهاند و همچنین کار با آنها به همراه پویانمایی نیز شدهاست:
افزودن آیکنهای متریال به برنامه
مرحلهی آخر این تنظیمات، افزودن آیکنهای متریال به برنامهاست. برای این منظور فایل src\index.html را گشوده و یک سطر ذیل را به head اضافه کنید:
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<button mat-button> <mat-icon>face</mat-icon> Click me! </button> <mat-checkbox>Check me!</mat-checkbox>
لیست کامل این آیکنها را به همراه توضیحات تکمیلی آنها، در آدرس ذیل میتوانید ملاحظه کنید:
http://google.github.io/material-design-icons
البته چون ما نمیخواهیم این آیکنها را از وب بارگذاری کنیم، برای نصب محلی آنها ابتدا دستور زیر را در ریشهی پروژه صادر کنید:
npm install material-design-icons --save
همانطور که مشاهده میکنید، برای استفادهی از این فایلهای آیکن فونت محلی، تنها کافی است فایل material-icons.css را به برنامه معرفی کنیم. برای این منظور فایل angular.json را گشوده و قسمت styles آنرا به صورت زیر تکمیل میکنیم:
"styles": [ "node_modules/material-design-icons/iconfont/material-icons.css", "src/styles.css" ],
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-01.zip
برای اجرای آن نیز ابتدا فایل restore.bat و سپس فایل ng-serve.bat را اجرا کنید.
مسیرراهها
اجزاء معماری سیستم عامل اندروید
- اجزاء معماری سیستم عامل اندروید :: بخش اول
- اجزاء معماری سیستم عامل اندروید :: بخش دوم
- اجزاء معماری سیستم عامل اندروید (قسمت اول معماری امنیتی اندروید) :: بخش سوم
- اجزاء معماری سیستم عامل اندروید (قسمت دوم معماری امنیتی اندروید) :: بخش چهارم
- اجزاء معماری سیستم عامل اندروید (قسمت اول رمزنگاری اندروید) :: بخش پنجم
- اجزاء معماری سیستم عامل اندروید (قسمت دوم رمزنگاری اندروید) :: بخش ششم
- اجزاء معماری سیستم عامل اندروید (بررسی مجوزها و مفهوم Intent در اندروید) :: بخش هفتم
- اجزاء معماری سیستم عامل اندروید (قسمت اول :: امنیت سازمانی پروژههای اندرویدی) :: بخش هشتم
مسیرراهها