- امکان عدم ذکر بعضی از پارامترهای Lambdas
- امکان تعریف متدهای static anonymous
امکان عدم ذکر بعضی از پارامترهای Lambdas در C# 9.0
مثال زیر را در نظر بگیرید:
Action<object, EventArgs> handler1 = (object obj, EventArgs args) => ShowDialog();
Action<object, EventArgs> handler2 = (object _, EventArgs _) => ShowDialog(); // OR Action<object, EventArgs> handler3 = (_, _) => ShowDialog();
نمونهی دیگر آن در حین تعریف رخدادگردانها است:
button1.Click += (s, e) => ShowDialog();
button1.Click += (_, _) => ShowDialog();
امکان تعریف Static anonymous functions در C# 9.0
برای کاهش میزان تخصیص حافظهی در حین کار با عبارات lambda ای که از متغیرهای محلی استفاده میکنند، اینبار در C# 9.0 میتوان این عبارات را static تعریف کرد. برای نمونه مثال زیر را درنظر بگیرید:
namespace CS9Features { public class LambdasTests { public void Test() { string text = "{0} is a good user !"; PrintItems(item => string.Format(text, item)); } private void PrintItems(Func<string, string> formatFunc) { foreach (var item in new[] { "User 1", "User 2" }) { Console.WriteLine(formatFunc(item)); } } } }
const string text = "{0} is a good user !"; PrintItems(static item => string.Format(text, item));
- این متد به عنوان static anonymous function شناخته میشود.
- دیگر نمیتواند دسترسی به حالت scope جاری را داشته باشد. بنابراین دیگر دسترسی به متغیرها، پارامترها و حتی شیء this را هم نخواهد داشت.
- میتواند با سایر اعضای استاتیک scope جاری کار کند.
- میتواند به تعاریف const مربوط به scope جاری، دسترسی داشته باشد.
در نگارشهای اخیر داتنت، NET CLI. به همراه تغییرات قابل توجهی بودهاست که در این مطلب و نظرات آن، موارد مهم این تغییرات را بررسی خواهیم کرد.
console logger بهبود یافتهی داتنت 8
یکی از تغییرات بسیار جالب توجه و مفید NET CLI. در داتنت 8، امکان دسترسی به خروجی لاگهای ساختار یافتهی اعمال خط فرمان آن است:
اگر پروژهی خود را با استفاده از دستور dotnet build، کامپایل میکنید، خروجی پیشفرض این دستور خط فرمان، کلی و بدون ارائهی جزئیات است؛ اما میتوان آنرا در داتنت 8، به شکل تصویر فوق، تغییر داد و به این مزایا رسید:
- امکان مشاهدهی زمان کامپایل هر قسمت به صورت جداگانه
- امکان مشاهدهی پویای درصد انجام عملیات
- امکان مشاهدهی جزئیات کامپایل هر target framework به صورت مجزا
- دسترسی به یک خروجی رنگی و زیباتر
این خروجی را که به صورت پیشفرض فعال نیست، میتوان به دو صورت:
الف) سراسری و با اجرای دستور PowerShell زیر:
[Environment]::SetEnvironmentVariable("MSBUILDTERMINALLOGGER", "auto", "User")
که متغیر محیطی MSBUILDTERMINALLOGGER را به auto تنظیم میکند،
ب) و یا با استفاده از سوئیچ tl-- به ازای هر دستور dotnet build، به صورت جداگانهای فعال کرد:
dotnet build --tl
یک نکته: این قابلیت جالب و مهم، در دات نت 9، به صورت پیشفرض فعال است و نیازی به تنظیم خاصی ندارد.
بررسی عمیق بهبودهای کارآیی در NET 9.
Performance Improvements in .NET 9
Each year, summer arrives to find me daunted and excited to write about the performance improvements in the upcoming release of .NET. “Daunted,” because these posts, covering .NET 8, .NET 7, .NET 6, .NET 5, .NET Core 3.0, .NET Core 2.1, and .NET Core 2.0, have garnered a bit of a reputation I want to ensure the next iteration lives up to. And “excited,” because there’s such an abundance of material to cover due to just how much goodness has been packed into the next .NET release, I struggle to get it all written down as quickly as my thoughts whirl.
تزئین مسیر انتخاب شده در منوی سایت
برای بهبود ظاهر برنامه نیاز است منوی سایت را به نحوی تغییر دهیم که مشخص کند، اکنون کاربر کدام گزینه را انتخاب کردهاست. این مورد شامل سلسه مراتب مسیریابیها نیز میشود؛ برای مثال فعالسازی حالت انتخاب شدهی منوی سایت، به همراه برگهی انتخاب شده در یکی از Child Routes.
برای پیاده سازی این قابلیت، دایرکتیو ویژهای به نام routerLinkActive تدارک دیده شدهاست. این دایرکتیو را میتوان به یک anchor tag و یا المان والد آن انتساب داد. مقدار آنرا نیز میتوان به یکی از کلاسهای CSS برنامه مانند کلاس active تعریف شدهی در بوت استرپ تنظیم کرد. هر زمانیکه این مسیریابی فعال شود، مسیریاب به صورت خودکار این کلاس را با درج آن، به المان مرتبط اضافه میکند و برعکس.
برای نمونه فایل src\app\product\product-edit\product-edit.component.html را گشوده و سپس تغییرات ذیل را اعمال کنید:
<div class="wizard"> <a [routerLink]="['info']" routerLinkActive="active"> Basic Information </a> <a [routerLink]="['tags']" routerLinkActive="active"> Search Tags </a> </div>
یک نکته: از آنجائیکه در اینجا مقدار active یک string است و نه یک خاصیت یا عبارت متغیر، به همین جهت نیازی نیست تا این دایرکتیو را به صورت [routerLinkActive] تعریف کنیم.
همانطور که مشاهده میکنید، همین دو تنظیم ساده سبب مشخص شدن برگهی انتخابی شدهاند.
منوی بالای سایت نیز چنین تنظیماتی را نیاز دارد. برای این منظور به فایل src\app\app.component.html که دربرگیرندهی منوی سایت است مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
<ul class="nav navbar-nav"> <li routerLinkActive="active"> <a [routerLink]="['/home']">Home</a> </li> <li routerLinkActive="active"> <a [routerLink]="['/products']">Product List</a> </li> <li routerLinkActive="active"> <a [routerLink]="['/products', 0, 'edit']">Add Product</a> </li> </ul>
همانطور که مشاهده میکنید، در این حالت انتخاب منوی نمایش لیست محصولات، سبب تزئین آن به حالت انتخاب شده نیز گردیدهاست.
مشکل! در همین حالت که مسیر نمایش لیست محصولات انتخاب شدهاست، لینک افزودن یک محصول جدید را نیز انتخاب کنید:
اینبار هر دو گزینه با هم انتخاب شدهاند. علت اینجا است که این دو مسیر دارای root URL segment یکسانی هستند؛ یا همان products/ در اینجا. به همین جهت routerLinkActive هر دو را به عنوان فعال انتخاب کردهاست. برای مدیریت میدان دید آن میتوان از دایرکتیو دیگری به نام routerLinkActiveOptions استفاده کرد:
<li routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"> <a [routerLink]="['/products']">Product List</a> </li>
اکنون کاربران بهتر میتوانند درک کنند در کجای برنامه قرار دارند.
افزودن آیکن خطا به برگهای که دارای مشکل اعتبارسنجی است
در ادامه میخواهیم اگر برگهای دارای مشکلات اعتبارسنجی بود، آیکن خطایی را در کنار برچسب آن برگه نمایش دهیم. به این ترتیب مدیریت چندین برگه برای کاربران سادهتر خواهد شد و به سادگی میتوانند برگههای مشکل دار را پیدا کنند.
در انتهای مطلب «مسیریابی در Angular - قسمت پنجم - تعریف Child Routes» متد isValid را تعریف کردیم. این متد مسیر یک tab را دریافت کرده و اگر اعتبارسنجی آن مشکلی نداشت، مقدار true را بر میگرداند. از این متد جهت نمایش آیکن خطای اعتبارسنجی برگهها استفاده خواهیم کرد.
<div class="wizard"> <a [routerLink]="['info']" routerLinkActive="active"> Basic Information <span [ngClass]="{'glyphicon glyphicon-exclamation-sign': !isValid('info')}"></span> </a> <a [routerLink]="['tags']" routerLinkActive="active"> Search Tags <span [ngClass]="{'glyphicon glyphicon-exclamation-sign': !isValid('tags')}"></span> </a> </div>
رخدادهای مسیریابی
هر زمانیکه کاربری مسیرهای مختلف برنامه را پیمایش میکند، مسیریاب تعدادی رخداد را نیز تولید خواهد کرد. از این رخدادها جهت تحت نظر قرار دادن، عیبیابی و یا اجرای منطقی میتوان استفاده کرد. این رخدادها شامل موارد ذیل هستند:
- NavigationStart، با آغاز پیمایش یک مسیر رخ میدهد.
- RoutesRecognized، با تشخیص و تطابق یک مسیر، با یکی از المانهای تعریف شدهی در تنظیمات مسیریابی رخ میدهد.
- NavigationEnd، با پایان پیمایش یک مسیر رخ میدهد.
- NavigationCancel، در صورت لغو پیمایش یک مسیریابی توسط محافظهای مسیرها و یا هدایت به یک جهت دیگر رخ میدهد.
- NavigationError، با شکست پیمایش یک مسیر رخ میدهد.
این رخدادها با فعالسازی تنظیم enableTracing تنظیمات مسیریابی به true فعال میشوند. برای این منظور فایل src\app\app-routing.module.ts را گشوده و به نحو ذیل تغییر دهید:
@NgModule({ imports: [RouterModule.forRoot(routes/*, { useHash: true }*/, { enableTracing: true })],
در اینجا ترتیب اجرای رخدادهای متفاوت پیمایش مسیر نمایش لیست محصولات را مشاهده میکنید.
- Router به هر مسیر، یک id خود افزایش یابنده را به صورت خودکار نسبت میدهد. برای نمونه، این مسیر خاص، id:2 را یافتهاست. از این id میتوان برای دسترسی به مجموعهای از رخدادها استفاده کرد.
- در این خروجی، url همان آدرس اصلی مسیر است و urlAfterRedirects به معنای مسیری است که پس از تنظیم redirect در تنظیمات مسیریابی (در صورت وجود) حاصل شدهاست.
- یکی از روشهایی که برای دیباگ مسیریابیها میتوان استفاده کرد، همین فعالسازی enableTracing است.
کار با رخدادهای مسیریابی با کدنویسی
به رخدادهایی که در کنسول developer tools مرورگر مشاهده کردید، با کدنویسی نیز میتوان دسترسی یافت. برای مثال میتوان یک تصویر چرخنده یا لطفا منتظر بمانید را در آغاز پیمایش یک مسیریابی نمایش داد و سپس در پایان پیمایش این مسیریابی، آنرا مخفی کرد. این events نیز از نوع Observable بوده و برای کار با آنها باید مشترکشان شد:
this.router.events.subscribe((routerEvent: Event) => { if (routerEvent instanceof NavigationStart) { //... } });
در مثال جاری این سری، در «مسیریابی در Angular - قسمت چهارم - پیش واکشی اطلاعات»، سبب شدیم تا کل اطلاعات مورد نیاز یک مسیر، پیش از نمایش آن از سرور دریافت شوند تا به این صورت ابتدا یک قاب خالی نمایش داده نشده و پس از مدتی تکمیل شود. هرچند تجربهی کاربری این روش بهتر از روش قبلی است، اما هنوز هم کاربر تاخیری را در ابتدا حس خواهد کرد (به اندازهی زمان delay تنظیم شده)، بدون اینکه راهنمایی به او ارائه شود. در این حالت بهتر است در ابتدای کار، یک تصویر چرخنده نمایش داده شود تا کاربر متوجه شود، نیاز است اندکی منتظر بماند.
در اینجا میخواهیم این تصویر چرخنده برای تمام مسیرهای برنامه فعال شود. به همین جهت گوش فرادادن به رخدادها را در نقطهی آغازین برنامه و یا همان src\app\app.component.ts انجام میدهیم:
import { Router, Event, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router'; import { AuthService } from './user/auth.service'; import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { pageTitle: string = 'Routing Lab'; loading: boolean = true; constructor(private authService: AuthService, private router: Router) { router.events.subscribe((routerEvent: Event) => { this.checkRouterEvent(routerEvent); }); } checkRouterEvent(routerEvent: Event): void { if (routerEvent instanceof NavigationStart) { this.loading = true; } if (routerEvent instanceof NavigationEnd || routerEvent instanceof NavigationCancel || routerEvent instanceof NavigationError) { this.loading = false; } } logOut(): void { this.authService.logout(); this.router.navigateByUrl('/welcome'); } }
- ابتدا وابستگیهای لازم آن import شدهاند.
- سپس میخواهیم خاصیت عمومی loading را در شروع به پیمایش یک مسیر، به true تنظیم کنیم و اگر این پیمایش به هر نحوی خاتمه یافت، آنرا false خواهیم کرد.
اکنون برای استفادهی از این خاصیت عمومی و نمایش تصویر چرخنده، نیاز است قالب src\app\app.component.html را ویرایش کنیم:
<span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="loading"></span>
/* Spinner */ .spinner { font-size:300%; position:absolute; top: 50%; left: 50%; z-index:10 } .glyphicon-spin { -webkit-animation: spin 1000ms infinite linear; animation: spin 1000ms infinite linear; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } }
اکنون مسیرهایی که دارای route resolver هستند (مانند نمایش جزئیات/ویرایش یک محصول)، به همراه یک spinner نمایش داده خواهند شد و سایر مسیرها ابتدا نمایش داده خواهند شد و سپس اطلاعات آنها از سرور دریافت میشود (مانند مسیر نمایش لیست محصولات که دارای route resolver نیست).
البته میتوان این true/false کردن loading را به ابتدا و انتهای کار یک Observable، مانند حالت نمایش لیست محصولات نیز منتقل کرد. اما در این حالت باید span مرتبط را نیز به قالب همان کامپوننت انتقال داد و دیگر سراسری نخواهد بود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-06.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.