مطالب
بررسی روش ارتقاء به NET Core 1.1.
نگارش پایدار NET Core 1.1. روز قبل منتشر شد. در ادامه نحوه‌ی ارتقاء پروژه‌‌های نگارش 1.0 RTM را به این نگارش بررسی خواهیم کرد.


دریافت نصاب NET Core 1.1.

برای این منظور به آدرس https://www.microsoft.com/net/download/core مراجعه کرده و فایل NET Core 1.1 SDK - Installer. را دریافت و نصب کنید. برای ظاهر شدن این گزینه باید حالت Current را بجای LTS (Long Term Support) انتخاب کرد:


همچنین در اینجا بسته NET Core 1.1 runtime - Installer. را هم جداگانه می‌توان دریافت و نصب کرد.


به روز رسانی فایل‌های global.json پروژه‌ها

اولین کاری را که باید پس از نصب نگارش‌های جدید NET Core. انجام داد، به روز رسانی شماره نگارش SDK درج شده‌ی در فایل‌های global.json تمام پروژه‌های موجود است. در غیراینصورت NuGet بسته‌های جدید مرتبط با آن‌ها را دریافت نخواهد کرد و آن‌ها را در لیست به روز شده‌ها نخواهید یافت.
برای این منظور خط فرمان را گشوده و دستور ذیل را صادر کنید:
 C:\>dotnet --version
1.0.0-preview2-1-003177
خروجی آن عبارتی است که باید قسمت نگارش SDK درج شود:
{
  "projects": [ "src", "test" ],
  "sdk": {
    "version": "1.0.0-preview2-1-003177"
  }
}


اصلاح فایل project.json پس از به روز رسانی فایل global.json

در ادامه باید فایل project.json نیز اندکی ویرایش شود تا شماره platform جدید را نیز درج کند. همچنین محل قرارگیری یکسری از بسته‌ها نیز باید تغییر کنند. در غیر اینصورت با اولین کامپایل Solution چنین خطاهایی را دریافت خواهید کرد:
 Can not find runtime target for framework '.NETCoreApp,Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'.
The project does not list one of 'win10-x64, win81-x64, win8-x64, win7-x64' in the 'runtimes' section.
برای رفع این مشکل، عبارت netcoreapp را در Solution جاری جستجو کرده و آن‌ها را به نحو ذیل تغییر دهید:
"frameworks": {
    "netcoreapp1.1": {
        "dependencies": {
            "Microsoft.NETCore.App": {
                "type": "platform",
                "version": "1.1.0"
            }
        },
        "imports": [
            "dnxcore50",
            "portable-net45+win8"
        ]
    }
},

یک نکته: اگر هنوز Microsoft.NETCore.App را در لیست dependencies ابتدای فایل project.json دارید، آن‌را حذف کنید؛ چون در قسمت frameworks فوق درج شده‌است. در غیراینصورت پیام تکراری بودن این کلید را دریافت خواهید کرد.

پس از طی دو مرحله‌ی فوق، یکبار پروژه را بسته و مجددا باز کنید.


به روز رسانی بسته‌های نیوگت پایدار

قبل از هر کاری مطمئن شوید که آخرین بسته‌ی خود NuGet را نیز نصب کرده‌اید (مهم). به روز رسانی‌های اخیر آن بیشتر در جهت سازگاری با پروژه‌های NET Core. است.
https://dist.nuget.org/index.html

در ادامه برای به روز رسانی بسته‌های نیوگت، می‌توان بر روی گره References کلیک راست کرد و سپس انتخاب گزینه‌ی Manage NuGet Packages و در آخر انتخاب برگه‌ی Updates و انتخاب کتابخانه‌های به روز شده. این روش برای حالت داشتن چندین پروژه در یک Solution اندکی کند است.


روش سریعتر که تمام پروژه‌ها را نیز به صورت خودکار بررسی و به روز می‌کند، مراجعه به کنسول پاورشل نیوگت و سپس صدور دستور ذیل است:
 PM> Update-Package
اگر در میان کار خطایی را دریافت کردید، این دستور را مجددا اجرا کنید (جهت اطمینان حداقل دوبار این دستور را صادر کنید).
به علاوه پس از پایان کار، یکبار به طور کامل ویژوال استودیو را بسته و مجددا باز کنید. سپس این دستور را یکبار دیگر هم صادر کنید.


به روز رسانی بسته‌های نیوگت آزمایشی

یکسری از بسته‌ها مانند Microsoft.AspNetCore.Razor.Tools تنها با انتخاب حالت include prereleases ظاهر می‌شوند که آن‌ها را نیز باید به روز کرد:



تغییر مهم ابزارهای EF Core

در کل Solution عبارت Microsoft.EntityFrameworkCore.Tools را جستجو کرده و با نام جدید Microsoft.EntityFrameworkCore.Tools.DotNet جایگزین کنید.


در آخر یک نمونه فایل project.json به روز شده‌ی یک برنامه‌ی ASP.NET Core 1.1 را در ذیل مشاهده می‌کنید:
{
    "dependencies": {
        "Microsoft.AspNetCore.Diagnostics": "1.1.0",
        "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.1.0",
        "Microsoft.AspNetCore.Http.Extensions": "1.1.0",
        "Microsoft.AspNetCore.Mvc": "1.1.0",
        "Microsoft.AspNetCore.Mvc.Core": "1.1.0",
        "Microsoft.AspNetCore.Mvc.TagHelpers": "1.1.0",
        "Microsoft.AspNetCore.Razor.Runtime": "1.1.0",
        "Microsoft.AspNetCore.Razor.Tools": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
        "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
        "Microsoft.AspNetCore.Session": "1.1.0",
        "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000019",
        "Microsoft.AspNetCore.StaticFiles": "1.1.0",
        "Microsoft.EntityFrameworkCore": "1.1.0",
        "Microsoft.EntityFrameworkCore.InMemory": "1.1.0",
        "Microsoft.EntityFrameworkCore.Tools.DotNet": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.Extensions.Configuration.Binder": "1.1.0",
        "Microsoft.Extensions.Configuration.Json": "1.1.0",
        "Microsoft.Extensions.Logging.Console": "1.1.0",
        "Microsoft.Extensions.Logging.Debug": "1.1.0",
        "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        }
    },
 
    "tools": {
        "BundlerMinifier.Core": "2.2.301",
        "Microsoft.AspNetCore.Razor.Tools": "1.1.0-preview4-final",
        "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
            "version": "1.1.0-preview4-final",
            "imports": [
                "portable-net45+win8"
            ]
        },
        "Microsoft.EntityFrameworkCore.Tools.DotNet": {
            "version": "1.1.0-preview4-final",
            "imports": [
                "portable-net45+win8"
            ]
        },
        "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.1.0-preview4-final"
    },
 
    "frameworks": {
        "netcoreapp1.1": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.1.0"
                }
            },
            "imports": [
                "dnxcore50",
                "portable-net45+win8"
            ]
        }
    },
 
    "buildOptions": {
        "emitEntryPoint": true,
        "preserveCompilationContext": true
    },
 
    "runtimeOptions": {
        "configProperties": {
            "System.GC.Server": true
        }
    },
 
    "publishOptions": {
        "include": [
            "wwwroot",
            "Features",
            "appsettings.json",
            "web.config"
        ]
    },
 
    "configurations": {
        "Release": {
            "buildOptions": {
                "optimize": true,
                "platform": "anycpu"
            }
        }
    },
 
    "scripts": {
        "precompile": [
            "dotnet bundle"
        ],
        "prepublish": [
            //"bower install"
        ],
        "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
    }
}


به روز رسانی پروژه‌ی Test

اگر از MSTest برای انجام آزمون‌های واحد استفاده می‌کنید، تغییرات فایل project.json آن نیز شامل تغییر شماره نگارش NETStandard.Library به 1.6.1 است و همچنین خود بسته‌های mstest نیز به روز شده‌اند. به علاوه قسمت frameworks آن نیز باید همانند مطالبی که عنوان شد، به روز شود:
{
    "version": "1.0.0-*",
 
    "testRunner": "mstest",
    "dependencies": {
        "Microsoft.EntityFrameworkCore": "1.1.0",
        "Microsoft.EntityFrameworkCore.InMemory": "1.1.0",
        "NETStandard.Library": "1.6.1",
        "dotnet-test-mstest": "1.1.2-preview",
        "MSTest.TestFramework": "1.0.6-preview"
    },
 
    "frameworks": {
        "netcoreapp1.1": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.1.0"
                }
            },
            "imports": [
                "dnxcore50",
                "portable-net45+win8"
            ]
        }
    }
}
مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
زمانیکه صحبت از توزیع برنامه‌های Blazor بر روی IIS است، عموما تنظیمات مرتبط با برنامه‌های Blazor Server و یا Hosted Blazor Apps که همان ترکیب WASM+Web API هستند، مطرح است؛ در غیراینصورت اگر برنامه‌ای صرفا از فایل‌های Blazor WASM تشکیل شده باشد، توزیع آن حتی بر روی صفحات static مربوط به GitHub هم میسر است و وابستگی خاصی به سروری ندارند. بنابراین در اینجا بیشتر هدف تنظیمات IIS مرتبط با قسمت ASP.NET Core این برنامه‌ها است و این مورد را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS» بررسی کرده‌ایم و نکته‌ی اضافه‌تری را به همراه ندارد.


مروری بر نحوه‌ی توزیع برنامه‌های Blazor بر روی IIS

1- پیش از هر کاری باید مطابق نگارش ASP.NET Core در حال استفاده (که به عنوان هاست Blazor Server و یا ارائه دهنده‌ی قسمت Web API برنامه‌ی سمت کلاینت WASM مطرح است)، بسته‌ی NET Core hosting bundle. را نصب کرد که عموما تحت عنوان «Hosting Bundle Installer» قابل دریافت است.
نکته‌ی مهم: همانند تمام نگارش‌های دات نت، در اینجا نیز باید Hosting Bundle را پس از نصب IIS، بر روی سیستم نصب کرد. اگر این ترتیب تغییر کند، یکبار دیگر نصاب آن‌را اجرا کرده و گزینه‌ی ترمیم نصب را انتخاب کنید تا یکپارچگی آن با IIS صورت گیرد.

2- نیاز است برنامه‌ی خود را اصطلاحا publish کرد تا به همراه فایل‌های نهایی قابل کپی باشد که در پوشه‌ای توسط IIS هاست خواهند شد. برای اینکار اگر از نگارش کامل ویژوال استودیو استفاده می‌کنید، فقط کافی است بر روی پروژه‌ی مدنظر کلیک راست کرده و از منوی باز شده، گزینه‌ی publish را انتخاب کنید و مراحل آن‌را طی نمائید و یا این مراحل را می‌توان توسط دستور خط فرمان زیر نیز خلاصه کرد که وابستگی خاصی، به IDE ویژه‌ای ندارد و چند سکویی است:
dotnet publish -o "c:\dir1\dir2" -c Release
این دستور، کار Build و توزیع پروژه را در پوشه‌ای که مشخص شده انجام می‌دهد. همچنین برای حالت build، نوع release را انتخاب می‌کند که به همراه بهینه سازی‌های بسیاری به همراه کاهش حجم نهایی فایل‌ها نیز هست.
و یا اگر فقط دستور dotnet publish -c Release را در ریشه‌ی پروژه اجرا کنیم، خروجی نهایی را در پوشه‌ی bin\Release\net5.0\publish می‌توان مشاهده کرد که به همراه یک web.config مخصوص برنامه‌های blazor هم هست و در آن mime typeهای متناظری، به همراه URL rewriting مناسب برنامه‌های تک صفحه‌ای وب از پیش تنظیم شده‌است. بنابراین در اینجا نصب ماژول URL rewrite بر روی IIS نیز الزامی است.

3- در اینجا نیز همانند تنظیمات برنامه‌های ASP.NET Core، باید application pool منتسب به برنامه را ویرایش کرده و NET Clr Version. آن‌را بر روی No Managed Code قرار داد.


روش فعالسازی توزیع مبتنی بر فشرده سازی Brotli در IIS

در حین عملیات publish استاندارد، به صورت پیش‌فرض از تمام فایل‌ها، سه نسخه‌ی اصلی، gz شده (gzip) و یا br شده (فشرده سازی Brotli که فایل‌های کم حجم‌تری را نسبت به gz ارائه می‌دهد) نیز تهیه می‌شوند که بسته به نوع مرورگر و پشتیبانی آن از روش‌های مختلف فشرده سازی، یکی از آن‌ها در اختیار کلاینت قرار خواهد گرفت که به این صورت کاربران، تجربه‌ی دریافت کم حجم‌تر و سریعتری را خواهند داشت.


باید دقت داشت Web.config ای که به همراه دستور dotnet publish ایجاد می‌شود، روش توزیع پیش‌فرض فایل‌های br. تولیدی را ندارد. برای اینکار نیاز است تنظیمات این فایل web.config توصیه شده‌ی توسط تیم Blazor را به web.config خود اضافه کرد تا در نهایت حجم دریافتی از سرور به شدت کاهش یابد.

یک نکته: اگر می‌خواهید فایل web.config سفارشی خودتان را داشته باشید، نمونه‌ای از آن‌را در ریشه‌ی پروژه قرار داده و سپس فایل csproj را به نحو زیر ویرایش کنید تا از آن در حین publish استفاده کند:
<PropertyGroup>
    <PublishIISAssets>true</PublishIISAssets>
</PropertyGroup>


در حین publish برنامه‌های Blazor WASM کار IL trimming نیز انجام می‌شود

برای کاهش حجم نهایی برنامه‌های Blazor WASM، در حین publish در حالت release، کار IL Trimming نیز به صورت خودکار انجام می‌شود تا کدهای IL ای که در برنامه نقش نداشته‌اند و مستقیما در جائی استفاده نشده‌اند، به صورت خودکار حذف شوند و به این ترتیب حجم ارائه‌ی نهایی به شدت کاهش یابد.
فقط باید دقت داشت که در این حالت اگر عملیات پویایی مانند reflection در کدهای شما صورت می‌گیرد، به علت نداشتن ارجاع استاتیکی به منابع مورد استفاده، در زمان اجرا با مشکل مواجه خواهد شد. اگر می‌خواهید اخطارهایی را در این زمینه مشاهده کنید، گزینه‌ی زیر را به فایل csproj اضافه نمائید:
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>


حذف مباحث بومی سازی در صورت عدم نیاز


اگر در برنامه‌ی خود از مباحث time-zones استفاده نمی‌کنید، می‌توانید با غیرفعال کردن آن در فایل csproj، حداقل 100 کیلوبایت از حجم برنامه‌ی نهایی را کاهش دهید:
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
و یا اگر نمی‌خواهید خروجی‌های تاریخ و یا مقادیر، بر اساس فرهنگ بومی کاربر تطابق پیدا کنند، می‌توانید invariant culture را فعال کنید:
<InvariantGlobalization>true</InvariantGlobalization>
مطالب
پیاده سازی عملیات صفحه بندی (paging) در sql server

در خیلی مواقع ملاحظه میشود که برای نمایش تعدادی از رکوردهای یک جدول در پایگاه داده، کل مقادیر موجود درآن توسط یک دستور select به دست می‌آید و صفحه‌بندی خروجی، به کنترلهای موجود سپرده میشود. اگر پایگاه داده ما دارای تعداد زیادی رکورد باشد، آن موقع است که دچار مشکل می‌شویم. فرض کنید به طور همزمان ۵ نفر (که تعداد زیادی نیستند) از برنامه ما که شامل ۱۰۰۰۰۰ سطر داده میباشد استفاده کنند و در هر صفحه، ۱۰ رکورد نمایش داده شود و صفحه‌بندی ما از نوع معقولی نباشد. در این صورت به جای اینکه با ۵×۱۰ رکورد داده را بارگزاری کنیم، ۵×۱۰۰۰۰۰ رکورد یعنی ۵۰۰۰۰۰ رکورد را برای به دست آوردن ۵۰ رکورد بارگزاری میکنیم. در زیر روشی شرح داده میشود که توسط آن، این سربار اضافه از روی برنامه و سرورهای مربوطه حذف شود. به stored procedure و توضیحات مربوط به آن توجه فرمایید :

CREATE PROCEDURE sp_PagedItems
(
 @Page int,
 @RecsPerPage int
)
AS

-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON


--Create a temporary table
CREATE TABLE #TempItems
(
ID int IDENTITY,
Name varchar(50),
Price currency
)


-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (Name, Price)
SELECT Name,Price FROM tblItem ORDER BY Price

-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)

-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
SELECT *,
MoreRecords =
(
 SELECT COUNT(*)
 FROM #TempItems TI
 WHERE TI.ID >= @LastRec
)
FROM #TempItems
WHERE ID > @FirstRec AND ID < @LastRec


-- Turn NOCOUNT back OFF
SET NOCOUNT OFF
در این کد دو پارامتر از نوع integer تعریف میکنیم. اول پارامتر @Page که مربوط به شماره صفحه‌ای می‌باشد که قصد دارید آن‌را بارگزاری نمایید. دومین پارامتر با نام @RecsPerPage تعداد رکوردهایی است که هر بار میخواهید بارگزاری شوند. مثلا اگر میخواهید هر بار ۱۵ عدد از رکوردها را نمایش دهید، این مقدار را باید برابر ۱۵ قرار دهیم. در مرحله بعد یک جدول موقت با نام #TempItems ساخته شده است که به طور موقت مقادیری را در حافظه نگه میدارد. نکته کلیدی که جلوتر از آن استفاده شده، ستون با نام ID است که از نوع auto-increment بوده و روی جدول موقت تعریف شده است. این ستون شناسه هر سطر را در خود نگه میدارد که به صورت اتوماتیک بالا میرود و جزء لاینفکی از این نوع paging میباشد. پس از آن جدول موقت را توسط رکوردهای جدول واقعی با نام tblItem توسط دستور select پر میکنیم.

در مرحله بعد شماره اولین و آخرین سطر مورد نظر را بر اساس پارامترهای ورودی محاسبه کرده و در متغیرهای @FirstRec و @LastRec می‌ریزیم.
برای استفاده از این کد فقط کافیست که پارامترهای ورودی را مقداردهی نمایید. مثلا اگر میخواهید در یک کنترل Grid از آن استفاده کنید باید ابتدا یک کوئری داشته باشید که تعداد کل سطرها را به شما بدهد و بر اساس این مقدار تعداد صفحات مورد نظر را به دست آورید. پس از آن با کلیک روی هر کدام از شماره صفحات آن را به عنوان مقدار به پارامتر مورد نظر بفرستید و از آن لذت ببرید. 

نظرات مطالب
توزیع پروژه‌های ASP.NET MVC بدون ارائه فایل‌های View آن

سلام آقای نصیری.من نسخه 2.1.2 Razor Generator رو نصب کردم.اما دستور enable-razorgenerator بدون دادن هیچ خطایی انجام می‌شود اما فایلهای  Cs مربوط به View‌ها تولید نمیشه و وقتی روشون Run Custom Tool رو می‌زنم این پیغام میاد.ممنون اگه راهنماییم کنید.

مطالب
بازسازی کامل پوشه packages بسته‌های NuGet به صورت خودکار
اگر قصد انتشار عمومی پروژه خود را دارید، نیازی به ارائه پوشه packages آن نیست. استفاده کننده نهایی به روشی که در ادامه توضیح داده خواهد شد، می‌تواند ارجاعات کل Solution را به یکباره به روز نماید؛ البته اگر تنها فایل یا فایل‌های packages.config پروژه‌های موجود پیوست شده و موجود باشند.


یک آزمایش

یک پروژه کنسول جدید را آغاز می‌کنیم. سپس به Solution آن یک Class library آزمایشی را نیز اضافه خواهیم کرد.
اکنون در خط فرمان پاورشل نیوگت (Tools > Library Package Manager > Package Manager Console) به هر یک از این دو پروژه، ارجاعی را به بسته structuremap از طریق دستور زیر اضافه می‌کنیم:
 Install-Package structuremap
اکنون یکبار پروژه را کامپایل کرده و سپس VS.NET را خاتمه می‌دهیم.
در ادامه پوشه‌های packages و همچنین bin و obj را کلا حذف می‌کنیم؛ اما فایل‌‌های متنی packages.config پروژه‌ها را نگه خواهیم داشت.
مجددا به VS.NET مراجعه خواهیم کرد. اینبار بر روی Solution کلیک راست کرده و گزینه «Enable NuGet Package Restore» را انتخاب می‌کنیم:


یک پوشه حاوی NuGet.exe به Solution جاری اضافه خواهد شد:



اکنون اگر پروژه را Build کنیم، تمام ارجاعات را به صورت خودکار از اینترنت (و یا کش موجود بر روی سیستم) دریافت و به Solution اضافه می‌کند.
به علاوه پوشه Packages نیز مجددا بازسازی خواهد شد.

پس از اینکار نهایتا برای اطمینان خاطر می‌توان دستور ذیل را در خط فرمان پاورشل نیوگت صادر کرد:
 Update-Package -Safe
این دستور به یکباره کلیه ارجاعات موجود packages را بررسی کرده و به روز خواهد کرد. پارامتر safe آن اختیاری است. اگر عنوان شود، سعی می‌کند همان شماره نگارشی را که در فایل‌های packages.config موجود است، دریافت و به روز نماید. در غیر اینصورت، آخرین فایل و آخرین نگارش موجود را دریافت و به روز رسانی خواهد کرد. به این ترتیب می‌توان به صرفه جویی زمانی قابل توجهی در یک پروژه با ارجاعات زیاد، رسید.
برای نمونه دستور update-package را بر روی یک پروژه MVC4 اجرا کنید تا این صرفه جویی زمانی را بهتر بتوانید حس کنید!


نتیجه گیری
لطفا حین ارائه عمومی پروژه خود، پوشه‌های bin، obj و همچنین packages آن‌را حذف کنید. استفاده کننده صرفا با داشتن فایل‌‌های packages.config به کمک روشی که عنوان شد می‌تواند ارجاعات کل Solution را بازیابی کند.
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها
AngularJS یک فریم ورک جاوا اسکریپتی، برای ساخت برنامه‌های تک صفحه‌ای سمت کاربر، توسط HTML ،CSS و جاوا اسکریپت است. این فریم ورک، حاوی اجزایی برای data binding، طراحی ماژولار، کار با سرویس‌های سمت سرور وب و امثال آن است. Angular 2 بازنویسی کامل نگارش 1 آن است و اهداف زیر را دنبال می‌کند:
- سرعت بالاتر بارگذاری و اجرای کدها
- استفاده از آخرین فناوری‌های وب مانند ES 6 و وب کامپوننت‌ها با پشتیبانی تا IE 9.
- سطح API آن با طراحی جدید آن، به شدت کاهش یافته و خلاصه شده‌است. همین امر یادگیری آن‌را نیز ساده‌تر می‌کند.

برای یادگیری Angular 2 نیازی به فراگیری Angular 1 نیست. در اینجا با آشنایی با TypeScript، به این نتیجه خواهید رسید که برنامه‌های Angular 2 چیزی بیشتر از یک مثال عملی TypeScript نیستند. زبان TypeScript، زبان اول و توصیه شده‌ی کار با Angular 2 است و مزیت آن دسترسی به تمام قابلیت‌های ES 6 است؛ با این تفاوت که کامپایلر TypeScript قادر است آن‌ها را به ES 5 یا نگارش فعلی جاوا اسکریپت که توسط تمام مرورگرها پشتیبانی می‌شود، ترجمه و تبدیل کند. به این نحو به یک طراحی شیءگرا، مدرن و با قابلیت نگهداری بالا خواهید رسید که با تمام مرورگرهای جدید نیز سازگار است.
بنابراین پیشنیاز‌های اصلی کار با Angular 2، فراگیری ES 6 و TypeScript هستند که دو سری ویژه و مختص به آن‌ها در سایت جاری تهیه شده‌اند:
«مبانی ES 6»
«مبانی TypeScript»

در این قسمت قصد داریم پیشنیازهای دریافت و نصب اجزاء و وابستگی‌های AngularJS 2 را به همراه TypeScript، در ویژوال استودیو 2015 بررسی کنیم. البته استفاده از ویژوال استودیو در اینجا ضروری نیست و اساسا AngularJS وابستگی به ویژوال استودیو ندارد. اگر به دنبال پشتیبانی بهتری از TypeScript هستید، VSCode رایگان، سورس باز و چند سکویی، گزینه‌ی بسیار بهتری است نسبت به ویژوال استودیوی کامل. البته این مورد تعجبی هم ندارد؛ از این جهت که VSCode با TypeScript نوشته شده‌است.


اهمیت آشنایی با npm

اگر در طی سال‌های اخیر با ویژوال استودیو کار کرده باشید، به طور قطع با NuGet نیز آشنا هستید. NuGet به عنوان یک package manager دات نتی شناخته می‌شود و کار آن دریافت وابستگی‌های یک پروژه، از مخزنی مشخص و نصب و به روز رسانی خودکار آن‌ها است. اما این روزها خارج از محیط توسعه‌ی مایکروسافت، مدیرهای بسته‌های دیگری مانند Bower نیز برای نصب و به روز رسانی بسته‌های front end مانند کتابخانه‌های CSS ایی و جاوا اسکریپتی، بسیار رواج پیدا کرده‌اند. Bower یکی از ابزارهای NodeJS است که توسط NPM یا NodeJS Package Manager نصب می‌شود. به این معنا که استفاده از Bower به معنای استفاده از NPM است. پیش از این از NPM صرفا جهت نصب بسته‌های مرتبط با NodeJS استفاده می‌شد، اما در طی یکسال اخیر، استفاده از NPM نیز برای مدیریت بسته‌های front end رواج پیدا کرده‌است و در صدر مدیر بسته‌های نصب کتابخانه‌های front end قرار دارد. همچنین در این حالت شما تنها نیاز به یک ابزار و یک فایل تنظیمات آن خواهید داشت، بجای استفاده از چندین ابزار و چندین فایل تنظیمات جداگانه. به علاوه این روزها انجام کارهای جدی جاوا اسکریپتی بدون دانش NPM دیگر میسر نیست. بهترین ابزارها، کامپایلرها، فشرده کننده‌های front end و امثال آن‌ها، تحت NodeJS اجرا می‌شوند. برای کار با AngularJS 2.0 و دریافت وابستگی‌های آن نیز استفاده از npm، روش پیش فرض و توصیه شده‌است.


کار با npm جهت دریافت وابستگی‌های کتابخانه‌های front end

برای کار با npm یا می‌توان از دستورات خط فرمان آن به صورت مستقیم استفاده کرد و یا از امکانات یکپارچگی آن با ویژوال استودیو نیز بهره گرفت که ما در ادامه از این روش استفاده خواهیم کرد. البته توانمندی‌های خط فرمان npm فراتر است از امکانات توکار VS.NET، اما در اینجا نیازی به آن‌ها نیست و هدف صرفا دریافت و به روز رسانی وابستگی‌های مرتبط است.
ویژوال استودیوی 2015 به همراه ابزارهای NodeJS ارائه می‌شود. اما مشکل اینجا است که این ابزارها هم اکنون قدیمی شده‌اند و تطابقی با آخرین نگارش ابزارهای NodeJS ندارند. برای نمونه حین نصب وابستگی‌‌های AngularJS 2.0 که یکی از آن‌ها RxJS است، یک چنین خروجی را در پنجره‌ی output ویژوال استودیو مشاهده می‌کنید:
 npm WARN engine rxjs@5.0.0-beta.6: wanted: {"npm":">=2.0.0"} (current: {"node":"v0.10.31","npm":"1.4.9"})
به این معنا که نگارش‌های اخیر RxJS نیاز به npm با نگارشی بیشتر از 2 را دارند؛ اما نگارش npm پیش فرض ویژوال استودیو 1.4.9 است (البته باید دقت داشت که من به روز رسانی دوم VS 2015 را هم نصب کرده‌ام). برای رهایی از این مشکل، تنها کافی است تا به ویژوال استودیو اعلام کنیم از نسخه‌ی اصلی خط فرمان npm، بجای نسخه‌ی پیش فرض خودش استفاده کند:


همانطور که در تصویر نیز ملاحظه می‌کنید، به مسیر Tools->Options->Projects and Solutions->External Web Tools مراجعه کرده و متغیر محیطی PATH ویندوز را به ابتدای لیست منتقل کنید. به این صورت ابزارهای به روز شده‌ی خط فرمان، مقدم خواهند شد بر ابزارهای قدیمی به همراه نگارش فعلی ویژوال استودیو.

البته فرض بر این است که آخرین نگارش nodejs را از آدرس https://nodejs.org/en دریافت و نصب کرده‌اید. با نصب آن، برنامه npm، در خط فرمان ویندوز به صورت مستقیم قابل استفاده خواهد بود؛ از این جهت که مسیر آن به PATH ویندوز اضافه شده‌است:



افزودن فایل project.json به پروژه

تا اینجا فرض بر این است که nodejs را از سایت آن دریافت و نصب کرده‌اید. همچنین متغیر PATH را در External Web Tools ویژوال استودیو به ابتدای لیست آن منتقل کرده‌اید تا همواره از آخرین نگارش npm نصب شده‌ی در سیستم، استفاده شود.
پس از آن همانند نیوگت که از فایل xml ایی به نام packages.config برای ثبت آخرین تغییرات خودش استفاده می‌کند، npm نیز از فایلی به نام package.json برای ذکر وابستگی‌های مورد نیاز پروژه بهره می‌برد. برای افزودن آن، از منوی Project گزینه‌ی Add new item را انتخاب کرده و سپس npm را در آن جستجو کنید:


در اینجا نام پیش فرض package.json را پذیرفته و این فایل را به پروژه و ریشه‌ی آن اضافه کنید.
اگر گزینه‌ی فوق را در لیست Add new item مشاهده نمی‌کنید، مهم نیست. یک فایل متنی را به نام package.json به ریشه‌ی پروژه‌ی جاری اضافه کنید.
در ادامه محتوای این فایل را به نحو ذیل تغییر دهید:
{
    "name": "asp-net-mvc5x-angular2x",
    "version": "1.0.0",
    "author": "DNT",
    "description": "",
    "scripts": {},
    "license": "Apache-2.0",
    "dependencies": {
        "jquery": "2.2.3",
        "angular2": "2.0.0-beta.15",
        "systemjs": "^0.19.26",
        "es6-promise": "^3.1.2",
        "es6-shim": "^0.35.0",
        "reflect-metadata": "0.1.2",
        "rxjs": "5.0.0-beta.2",
        "zone.js": "^0.6.12",
        "bootstrap": "^3.3.6"
    },
    "devDependencies": {
        "typescript": "^1.8.9",
        "typings": "^0.8.1"
    },
    "repository": {
    }
}
این فایل تنظیمات، سبب بارگذاری خودکار وابستگی‌های مورد نیاز AngularJS 2.0 در ویژوال استودیو می‌شود.

چند نکته:
- هر زمانیکه این فایل را ذخیره کنید، بلافاصله کار دریافت این بسته‌ها از اینترنت آغاز خواهد شد. جزئیات آن‌را می‌توان در پنجره‌ی output ویژوال استودیو مشاهده کرد (و حتما این‌کار را جهت دیباگ کار انجام دهید. بسیاری از مشکلات و خطاها، در اینجا لاگ می‌شوند). بنابراین اگر قصد دارید کار همگام سازی تغییرات را انجام دهید، فقط کافی است دکمه‌های ctrl+s یا save را بر روی این فایل اعمال کنید.
- کاری که دکمه‌ی ctrl+s در این فایل انجام می‌دهد، اعمال خودکار دستور npm install بر روی پوشه‌ای است که package.json در آن قرار دارد. بنابراین برای دریافت این بسته‌ها، روش خط فرمان آن، ابتدا وارد شدن به ریشه‌ی پروژه‌ی جاری و سپس صدور دستور npm install است (که نیازی به آن نیست و ویژوال استودیو این‌کار را به صورت خودکار انجام می‌دهد).
- بسته‌های دریافت شده، در پوشه‌ای به نام node_modules در ریشه‌ی پروژه ذخیره می‌شوند:


برای مشاهده‌ی آن‌ها می‌توان بر روی دکمه‌ی show all files در solution explorer کلیک نمائید.

- درون فایل package.json، پشتیبانی کاملی از intellisense وجود دارد. برای مثال اگر علاقمند بودید تا آخرین نگارش AngularJS را مشاهده کنید، پس از خاصیت angular2 در تنظیمات فوق، " را تایپ کنید تا منوی تکمیل کننده‌ای ظاهر شود:


بدیهی است این منو جهت دریافت آخرین اطلاعات، نیاز به اتصال به اینترنت را دارد.

البته همیشه آخرین شماره نگارش AngularJS 2.0 را در این آدرس نیز می‌توانید مشاهده کنید: CHANGELOG.md

- در این فایل شماره نگارش آغاز شده‌ای با ^ مانند "3.1.2^" به این معنا است که اجازه‌ی نصب نگارش‌های جدیدتری از 3.1.2 نیز داده شده‌است.


به روز رسانی کامپایلر TypeScript

نیاز است یکبار مطلب «مبانی TypeScript؛ تنظیمات TypeScript در ویژوال استودیو» را جهت آشنایی با نحوه‌ی به روز رسانی اجزای مرتبط با TypeScript مطالعه کنید.


افزودن فایل tsconfig.json به پروژه

مرحله‌ی بعد شروع به کار با AngularJS و TypeScript، انجام تنظیمات کامپایلر TypeScript است. برای این منظور از منوی پروژه، گزینه‌ی Add new item، عبارت typescript را جستجو کرده و در لیست ظاهر شده، گزینه‌ی TypeScript JSON Configuration File را با نام پیش فرض آن انتخاب کنید:


اگر این گزینه‌ی ویژه را در لیست add new items ندارید، مهم نیست. یک فایل متنی را به نام tsconfig.json به ریشه‌ی پروژه‌ی جاری اضافه کنید.
پس از افزودن فایل tsconfig.json به ریشه‌ی سایت، تنظیمات آن‌را به نحو ذیل تغییر دهید:
{
    "compileOnSave": true,
    "compilerOptions": {
        "target": "es5",
        "module": "system",
        "moduleResolution": "node",
        "noImplicitAny": false,
        "noEmitOnError": true,
        "removeComments": false,
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true
    },
    "exclude": [
        "node_modules",
        "wwwroot",
        "typings/main",
        "typings/main.d.ts"
    ]
}
در مورد جزئیات این تنظیمات در سری «مبانی TypeScript» و «تنظیمات کامپایلر TypeScript» بیشتر بحث شده‌است. فایل ویژه‌ی tsconfig.json تنظیمات پیش فرض ویژوال استودیو را جهت کار با TypeScript بازنویسی می‌کند. بنابراین زمانیکه از این فایل استفاده می‌شود، دیگر نیازی نیست به گزینه‌ی مختلف پروژه، جهت تنظیم TypeScript مراجعه کرد.
برای نمونه خاصیت compileOnSave سبب خواهد شد تا پس از ذخیره سازی یک فایل ts، بلافاصله فایل js معادل آْن تولید شود. نوع ماژول‌ها در اینجا به system.js تنظیم شده‌است و خروجی کدهای js آن از نوع ES 5 درنظر گرفته شده‌است. همچنین فعال سازی decorators نیز در اینجا صورت گرفته‌است. از این جهت که AngularJS2 استفاده‌ی گسترده‌ای را از این مفهوم، انجام می‌دهد.
در قسمت excludes به کامپایلر TypeScript اعلام شده‌است تا از یک سری از مسیرها مانند پوشه‌ی node_modules که پیشتر آن‌را تنظیم و دریافت کردیم، صرفنظر کند.


خلاصه‌ی بحث

تا اینجا با نحوه‌ی نصب پیشنیازهای AngularJS 2 و همچنین TypeScript آشنا شدیم. به صورت خلاصه:
- nmp اصلی را از سایت nodejs دریافت و نصب کنید.
- متغیر PATH را در ویژوال استودیو، به ابتدای لیست ابزارهای خارجی وب آن منتقل کنید تا از npm اصلی استفاده کند.
- فایل project.json را با محتوای ذکر شده، به ریشه‌ی پروژه اضافه کنید. سپس یکبار آن‌را save کنید تا وابستگی‌ها را از اینترنت دریافت کند.
- اطمینان حاصل کنید که آخرین کامپایلر TypeScript را نصب کرده‌اید.
- فایل tsconfig.json را با محتوای ذکر شده، به ریشه‌ی پروژه اضافه کنید.
مطالب
مبانی TypeScript؛ جنریک‌ها
بخش عمده‌ای از مهندسی نرم افزار، مربوط به ساخت کامپوننت‌هایی است که نه تنها به خوبی و مستحکم توسعه داده شده‌اند، بلکه قابلیت استفاده دوباره را نیز دارند.
کامپوننت‌هایی که قادر هستند بر روی داده‌های فعلی و همچنین داده‌های آینده، کار کنند، قابلیت‌های انعطاف پذیری را برای ساخت سیستم‌های نرم افزاری بزرگ در اختیار شما قرار خواهند داد.
در زبان هایی نظیر جاوا و سی شارپ، یکی از ابزارهای اصلی برای ساخت کامپوننت‌هایی با قابلیت استفاده مجدد، "جنریک‌ها" میباشد که امکان ساخت کامپوننت‌هایی را می‌دهند که با انواع داده‌های متنوعی به جای یک نوع داده، کار میکنند.
برای شروع به تابع زیر توجه کنید:
function identity(arg: number): number {
    return arg;
}
تابع identity هر آنچه را که به عنوان پارامتر به آرگومان آن ارسال کنیم، بازگشت خواهد داد. میتوانید آن را به مانند دستور "echo" در نظر بگیرید.
بدون استفاده از جنریک ها، باید برای هر نوع داده، یک تابع جدید و یا تابعی را به صورت کلی زیر در نظر بگیریم:
function identity(arg: any): any {
    return arg;
}
در تابع بالا از نوع any استفاده شده است. با استفاده از any، قطعا تابع بالا به صورت عمومی خواهد بود و تمام نوع داده‌ها را به عنوان آرگومان خواهد پذیرفت. ولی در واقع ما اطلاعات مربوط به اینکه نوع داده بازگشتی توسط تابع چه چیزی است را از دست خواهیم داد.
برای مثال اگر یک عدد را به آن ارسال کنیم، تنها متوجه خواهیم شد که نوع آن any میباشد؛ بنابراین به روشی نیاز داریم تا بتوانیم نوع داده آرگومان‌های تابع مورد نظر را کنترل کنیم.
در پیاده سازی زیر، ما از یک type variable خاصی استفاده خواهیم کرد که به جای مقادیر برای انوع داده‌ها مورد استفاده قرار می‌گیرد.
function identity<T>(arg: T): T {
    return arg;
}
در تابع بالا با از T به عنوان یک type variable استفاده کرده‌ایم که امکان گرفتن انواع داده‌هایی را (برای مثال number) که توسط کاربر مهیا میشود، به ما خواهد داد.
این پیاده سازی از تابع identity، تحت عنوان تابع جنریک مطرح می‌شود که برای دامنه‌ی عظیمی از انواع داده‌ها می‌تواند مورد استفاده قرار گیرد و بر خلاف پیاده سازی قبل که از any استفاده کرده‌ایم، در این حالت دیگر اطلاعات نوع داده را از دست نخواهیم داد.
برای استفاده از تابع فوق ما دو روش را پیش رو خواهیم داشت:
  • ارسال تمام آرگومان‌ها که شامل آرگومان نوع داده هم میباشد
let output = identity<string>("myString");  // type of output will be 'string'
در کد بالا ما به صراحت T را با نوع داده string با استفاده از < > مقدار دهی کرده‌ایم.
  • روش دوم که شاید استفاده رایج از توابع جنریک هم هست، استفاده از امکان type argument inference میباشد.
let output = identity("myString");  // type of output will be 'string'
در کد بالا اینبار به صورت صریح نوع T را مشخص نکرده‌ایم و کامپایلر باتوجه به "myString"، نوع T را تعیین خواهد کرد. درحالیکه استفاده از امکان type argument inference خیلی مفید میباشد و کد را خیلی کم حجم و خوانا در اختیار ما قرار میدهد، ولی در مثال‌های پیچیده، امکان این وجود دارد که کامپایلر در تشخیص نوع داده، با خطا مواجه شود. در این صورت استفاده از روش اول مفید خواهد بود.
در ادامه اگر قصد لاگ کردن Length مربوط به آرگومان arg را در هر بار فراخوانی تابع داشته باشیم، می‌بایستی به شکل زیر عمل کنیم:
function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}
همانطور که انتظار داشتیم، کامپایلر خطایی مبنی بر نداشتن عضوی تحت عنوان length برای آرگومان arg را نمایش خواهد داد. همانطور که قبلا نیز اشاره کردیم، T جانشینی برای تمام نوع داده‌ها خواهد بود؛ بنابراین در اینجا میتوانیم یک داده‌ی از نوع number را که عضوی بنام length ندارد، هم به این تابع  پاس دهیم.
حال بیایید بگوییم که ما قصد داریم این تابع، با آرایه ای از T کار کند. در این صورت اگر با آرایه‌ها کار کنیم، عضوی به نام length را خواهیم داشت. به پیاده سازی زیر توجه کنید:
function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}
کد بالا را میتوانیم به این شکل تفسیر کنیم: تابع جنریک loggingIdentity یک type parameter را تحت عنوان T و یک آرگومان را تحت عنوان arg که آرایه ای از T هست، گرفته و آرایه‌ای از T را بازگشت خواهد داد. اگر ما آرایه‌ای از number را به آن پاس دهیم، آرایه‌ای از number‌ها را بازگشت خواهد داد.
در این حالت استفاده از T به عنوان type variable که بخشی از نوع داده‌هایی است که ما با آنها کار میکنیم، به جای پشتیبانی از تمام نوع داده‌ها، انعطاف پذیری بالایی را به ما خواهد داد.
حتی میتوانیم این مثال را به شکل زیر نیز پیاده سازی کنیم:
function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}
پیاده سازی بالا خیلی شبیه به پیاده سازی در سایر زبان‌ها هم میباشد.

Generic Types
در این قسمت ما به دنبال یافتن نوع خود توابع بوده و سعی خواهیم کرد اینترفیس‌های جنریک را هم پیاده سازی کنیم. نوع توابع جنریک هم بمانند توابع غیر جنریک میباشند؛ به طوری که می‌توان لیستی از type parameters هایی را که در حالت function declarations موجود هستند، در ابتدا بنویسیم.
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;
حتی می‌توانیم نام متفاوتی را هم برای type parameter در نظر بگیرم:
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <U>(arg: U) => U = identity;
یا حتی می‌توانیم به مانند امضای یک object literal هم کد بالا را بازنویسی کنیم:
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: {<T>(arg: T): T} = identity;
حال میتوانیم این object literal را به یک اینترفیس منتقل کنیم:
interface GenericIdentityFn {
    <T>(arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn = identity;
کد بالا خوانایی بالاتری را نسبت به حالت قبل دارد و با تعریف یک اینترفیس به نام GenericIdentityFn و انتقال object literal به داخل آن، میتوانیم از نام اینترفیس به جای استفاده مستقیم از object literal، بهره ببریم.
حتی میتوانیم type parameter تابع جنریک خود را هم به اینترفیس منتقل کنیم. 
interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;
باید توجه داشت که پیاده سازی ما کمی متفاوت‌تر از قبل شده است.الان type parameter ما برای کل اعضای اینترفیس قابل رویت میباشد.فهم این مورد که چه زمانی type parameter را در امضای نامیدن داخل اینترفیس یا بر روی خود اینترفیس استفاده کنیم، خود میتوانید برای شرح اینکه کدام وجه‌های یک نوع داده جنریک هستند، مفید باشد.
نکته : امکان تعریف enum‌ها و namespace‌های جنریک وجود ندارد.
 
Generic Classes
تعریف کلاس‌های جنریک هم به مانند اینترفیس‌های جنریک میباشد. به مثال زیر توجه کنید:
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
در کد بالا، استفاده‌ای واقعی از کلاس GenericNumber قابل مشاهده است. شاید متوجه شده باشید که هیچ محدودیتی برای استفاده‌ی نوع‌ها برای مثال تنها از نوع number در آن نیست و میتوانید از نوع string هم به شکل زیر استفاده کنید:
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
نکته : برای اعضای استاتیک کلاس نمیتوانید از type parameter کلاس استفاده کنید.
 
Generic Constraints
اگر مثال اخیر را به یاد داشته باشید، شاید بعضی اوقات لازم باشد که یک تابع جنریک را تعریف کنیم تا تنها با مجموعه‌ای از نوع داده‌ها کار کند که اتفاقا از امکانات این مجموعه، آگاهی داریم. در همان مثال loggingIdentity، ما نیاز داشتیم تا به خصوصیت length آرگومان arg دسترسی داشته باشیم و کامپایلر در همان ابتدا، به دلیل اینکه همه نوع داده‌ها از این خصوصیت برخوردار نیستند، خطایی را به ما نشان میدهد.
در ادامه تابعی را پیاده سازی میکنیم که جوابگوی تمام نوع داده‌ها بوده، به شرطی که حداقل خصوصیت length را داشته باشند. لذا باید نیاز خود را در قالب یک محدودیت بر آنچه که T میتواند انجام دهد، فهرست کنیم.
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}
در کد بالا برای توصیف محدودیت خود از یک اینترفیس به نام Lengthwise استفاده کرده‌ایم که فقط یه خصوصیت length را دارد و با استفاده از آن و کلمه‌ی کلیدی extends، محدودیت خود را اعمال کرده ایم.
استفاده از تابع بالا:
loggingIdentity(3);  // Error, number doesn't have a .length property
چون تابع جنریک ما الان محدود میباشد و با تمام نوع داده‌ها کار نخواهد کرد، با خطای بالا روبرو خواهیم شد.
loggingIdentity({length: 10, value: 3});
در عوض مثال بالا، محدودیت ما را به همراه دارد (داشتن خصوصیت length) و بدون هیچ خطایی جواب خواهیم گرفت.

استفاده از Type Parameter‌ها در تعریف محدودیت
در برخی از سناریو‌ها شاید نیاز باشد که یکی از type parameter‌ها توسط دیگری محدود شده باشد. به مثال زیر توجه کنید:
function find<T, U extends Findable<T>>(n: T, s: U) {   // errors because type parameter used in constraint
  // ...
}
find (giraffe, myAnimals);
همانطور که مشخص است، کامپایلر ما را با نشان دادن خطایی متوقف خواهد کرد. چون اجازه‌ی استفاده از type parameter را در اعمال محدودیت، نداریم. در عوض میشود به شکل زیر عمل کرد:
function find<T>(n: T, s: Findable<T>) {
  // ...
}
find(giraffe, myAnimals);
این بار آرگومان s ما باید از نوع <Findable<T باشد که باز هم توانسته‌ایم محدودیت خود را توسط یک type parameter بر آن یکی اعمال کنیم.
نکته : دو پیاده سازی بالا اصلا یکسان نیستند؛ نوع بازگشی در تابع اول میبایستی از نوع U می‌بود، ولی در پیاده سازی دوم اینگونه نیست.(در صورت نبودن خطا)
 
استفاده از کلاس‌ها در جنریک‌ها
زمانی که قصد دارید با استفاده از جنریک‌ها، factory‌ها را پیاده سازی کنید، باید با استفاده از سازنده‌ی کلاس‌ها، به آنها اشاره کنید. به مثال زیر توجه کنید:
function create<T>(c: {new(): T; }): T {
    return new c();
}
تابع بالا به عنوان یک object factory می‌تواند مورد استفاده قرار بگیرد و نکته آن در تعریف نوع آرگومان c میباشد که باز هم به صورت object literal معرفی شده است. اگر در قسمت‌های بالا به یاد داشته باشید، می‌توان این مورد را هم داخل یک اینترفیس گنجاند.
به عنوان یک مثال پیشرفته‌تر هم میتوان به استفاده از prototype property برای استنتاج type parameter‌ها و تحمیل کردن ارتباط بین تابع سازنده و وهله کلاس‌ها، اشاره کرد. به مثال زیر توجه کنید:
class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function findKeeper<A extends Animal, K> (a: {new(): A;
    prototype: {keeper: K}}): K {

    return a.prototype.keeper;
}
در کد بالا از دو کلاس BeeKeeper و ZooKeeper برای نوع بازگشتی متد‌های موجود در کلاس‌های Bee و Lion استفاده شده‌است. کلاس Animal به عنوان کلاس پایه دو کلاس Bee و Lion که یک خصوصیت numLegs دارد، تعریف شده‌است. از تابع جنریک findKeeper برای مشخص کردن نگهبان مرتبط با Animal ای که به عنوان type parameter توسط A مشخص میشود، استفاده می‌گردد. محدودیتی که بر روی A اعمال شده است نشان دهنده‌ی این است که نوع داده‌ی مورد نظر باید حتما یک Animal باشد و همچنین با اعمال محدودیتی که در قالب object literal مشخص است، تعیین شده است که نوع مورد نظر باید یک کلاس باشد و در نهایت با استفاده از prototype مشخص کرده‌ایم که متدی به نام Keeper آن کلاس، باید نوع برگشتی از نوع K را که به عنوان type parameter مطرح شده‌ی در امضای تابع است، دارا باشد. K نشان دهنده نوع داده بازگشتی این تابع جنریک نیز میباشد.
استفاده از تابع بالا:
findKeeper(Lion).nametag;  // typechecks!
بله همانطور که مشخص است، type parameter‌های مورد نظر به اصطلاح infer شده‌اند و خصوصیت nametag نشان از این دارد که ZooKeeper به صورت خودکار به عنوان نوع داده K تشخیص داده شده است.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 14 - فعال سازی اعتبارسنجی ورودی‌های کاربران
یک نکته‌ی تکمیلی: جایگزینی bower با npm

bower یک فناوری منسوخ شده‌است و اگر بخواهیم bower.json ذکر شده‌ی در این مطلب را با package.json مربوط به npm جایگزین کنیم، به یک چنین محتوایی خواهیم رسید:
{
  "name": "testwebapp",
  "version": "1.0.0",
  "description": "",
  "scripts": {},
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bootstrap": "^3.3.7",
    "jquery": "^2.2.4",
    "jquery-ajax-unobtrusive": "^3.2.4",
    "jquery-validation": "^1.17.0",
    "jquery-validation-unobtrusive": "^3.2.8"
  }
}
پس از ایجاد فایل package.json فوق، کافی است دستور npm install را در ریشه‌ی پروژه وارد کنید تا این بسته‌ها دریافت و نصب شوند.
سپس بر اساس مسیرهای پوشه‌ی node_modules جدید، فایل bundleconfig.json چنین محتوایی را پیدا می‌کند:
[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "node_modules/bootstrap/dist/css/bootstrap.min.css",
      "wwwroot/css/site.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "node_modules/jquery/dist/jquery.min.js",
      "node_modules/jquery-validation/dist/jquery.validate.min.js",
      "node_modules/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js",
      "node_modules/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js",
      "node_modules/bootstrap/dist/js/bootstrap.min.js",
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]
و در آخر فایل Layout.cshtml_ برنامه به این صورت ساده خواهد شد (فقط خروجی‌های نهایی css و js حاصل از BundlerMinifier در اینجا قید می‌شوند؛ که حاصل یکی کردن و فشرده سازی تمام آن‌ها است):
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - DNTCommon.Web.Core.TestWebApp</title>
    <link href="~/css/site.min.css" rel="stylesheet" asp-append-version="true" />
</head>

<body>
    <div>
        @RenderBody()
    </div>

    <script src="~/js/site.min.js" type="text/javascript" asp-append-version="true"></script> 
    @RenderSection("Scripts", required: false)
</body>
</html>
البته با این شرط که تنظیمات ذیل در فایل csproj برنامه موجود باشند:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <Target Name="PrecompileScript" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet bundle" />
  </Target>
  <ItemGroup>
    <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.6.362" />
  </ItemGroup>
</Project>
مطالب
Blazor 5x - قسمت سوم - مبانی Razor
پیش از شروع به کار توسعه‌ی برنامه‌های مبتنی بر Blazor، باید با مبانی Razor آشنایی داشت. Razor امکان ترکیب کدهای #C و HTML را در یک فایل میسر می‌کند. دستور زبان آن از @ برای سوئیچ بین کدهای #C و HTML استفاده می‌کند. کدهای Razor را می‌توان در فایل‌های cshtml. نوشت که عموما مخصوص صفحات و Viewها هستند و یا در فایل‌های razor. که برای توسعه‌ی کامپوننت‌های Balzor بکار گرفته می‌شوند. در اینجا مهم نیست که پسوند فایل مورد استفاده چیست؛ چون اصول razor بکار گرفته شده در آن‌ها یکی است. البته در اینجا تاکید ما بیشتر بر روی فایل‌های razor. است که در برنامه‌های مبتنی بر Blazor بکار گرفته می‌شوند.


ایجاد یک پروژه‌ی جدید Blazor WASM

برای پیاده سازی و اجرای مثال‌های این قسمت، نیاز به یک پروژه‌ی جدید Blazor WASM را داریم که می‌توان آن‌را با اجرای دستور dotnet new blazorwasm --hosted در یک پوشه‌ی خالی، ایجاد کرد.

یک نکته: دستور فوق به همراه یک سری پارامتر اختیاری مانند hosted-- نیز هست. برای مشاهده‌ی لیست آن‌ها دستور dotnet new blazorwasm --help را صادر کنید. برای مثال ذکر پارامتر hosted-- سبب می‌شود تا یک ASP.NET Core host نیز برای Blazor WebAssembly app ایجاد شده تولید شود.

حالت hosted-- آن یک چنین ساختاری را دارد که از سه پروژه و پوشه‌ی Client ،Server و Shared تشکیل می‌شود:


در اینجا یک پروژه‌ی خالی WASM ایجاد شده که برخلاف حالت معمولی dotnet new blazorwasm که در قسمت قبل آن‌را بررسی کردیم، دیگر از فایل استاتیک wwwroot\sample-data\weather.json در آن خبری نیست. بجای آن، یک پروژه‌ی استاندارد ASP.NET Core Web API را در پوشه‌ی جدید Server ایجاد کرده که کار ارائه‌ی اطلاعات این سرویس آب و هوا را انجام می‌دهد و برنامه‌ی WASM ایجاد شده، این اطلاعات را توسط HTTP Client خود، از سرور Web API دریافت می‌کند.

بنابراین اگر مدل برنامه‌ای که قصد دارید تهیه کنید، ترکیبی از یک Web API و WASM است، روش hosted--، آغاز آن‌را بسیار ساده می‌کند.

نکته: روش اجرای این نوع برنامه‌ها با اجرای دستور dotnet run در داخل پوشه‌ی Server پروژه، انجام می‌شود. با اینکار هم سرور ASP.NET Core آغاز می‌شود و هم برنامه‌ی WASM توسط آن ارائه می‌گردد. در این حالت اگر آدرس https://localhost:5001 را در مرورگر باز کنیم، هم قسمت‌های بدون نیاز به سرور پروژه‌ی WASM قابل دسترسی است (مانند کار با شمارشگر آن) و هم قسمت دریافت اطلاعات از سرور آن، در منوی Fetch Data.


شروع به کار با Razor

پس از ایجاد یک پروژه‌ی جدید WASM، به فایل Client\Pages\Index.razor آن مراجعه کرده و محتوای پیش‌فرض آن‌را بجز سطر اول زیر، حذف می‌کنیم:
@page "/"
این سطر، بیانگر مسیریابی منتهی به کامپوننت جاری است. یعنی با گشودن برنامه‌ی WASM در مرورگر و مراجعه به ریشه‌ی سایت، محتوای این کامپوننت را مشاهده خواهیم کرد.
در فایل‌های razor. می‌توان ترکیبی از کدهای #C و HTML را نوشت. برای مثال:
@page "/"

<p>Hello, @name</p>

@code
{
    string name = "Vahid N.";
}
در اینجا قصد داریم مقدار یک متغیر را در یک پاراگراف درج کنیم. به همین جهت برای تعریف آن و شروع به کدنویسی می‌توان با تعریف یک قطعه کد که در فایل‌های razor با code@ شروع می‌شود، اینکار را انجام داد. در این قطعه کد، نوشتن هر نوع کد #C ای مجاز است که نمونه‌ای از آن‌را در اینجا با تعریف یک متغیر مشاهده می‌کنید. اکنون برای درج مقدار این متغیر در بین کدهای HTML از حرف @ استفاده می‌کنیم؛ مانند name@ در اینجا. نمونه‌ای از خروجی تغییرات فوق را در تصویر زیر مشاهده می‌کنید:


یک نکته: با توجه به اینکه تغییرات زیادی را در فایل جاری اعمال خواهیم کرد، بهتر است برنامه را با دستور dotnet watch run اجرا کرد، تا این تغییرات را تحت نظر قرار داده و آن‌ها را به صورت خودکار کامپایل کند. به این صورت دیگر نیازی نخواهد بود به ازای هر تغییر، یکبار دستور dotnet run اجرا شود.

در زمان درج متغیرهای #C در بین کدهای HTML توسط razor، استفاده از تمام متدهای الحاقی زبان #C نیز مجاز هستند؛ مانند:
 <p>Hello, @name.ToUpper()</p>
بنابراین درج حرف @ در بین کدهای HTML به این معنا است که به کامپایلر razor اعلام می‌کنیم، پس از این حرف، هر عبارتی که قرار می‌گیرد، یک عبارت معتبر #C است.

یا حتی می‌توان یک متد جدید را مانند CustomToUpper در قطعه کد razor، تعریف کرد و از آن به صورت زیر استفاده نمود:
@page "/"

<p>Hello, @name.ToUpper()</p>
<p>Hello, @CustomToUpper(name)</p>

@code
{
    string name = "Vahid N.";

    string CustomToUpper(string value) => value.ToUpper();
}
در این مثال‌ها، ابتدای عبارت #C تعریف شده با حرف @ شروع می‌شود و انتهای آن‌را خود کامپایلر razor بر اساس بسته شدن تگ p تعریف شده، تشخیص می‌دهد. اما اگر قصد داشته باشیم برای مثال جمع دو عدد را در اینجا محاسبه کنیم چطور؟
<p>Let's add 2 + 2 : @2 + 2 </p>
در این حالت امکان تشخیص ابتدا و انتهای عبارت #C توسط کامپایلر میسر نیست. برای رفع این مشکل می‌توان از پرانتزها استفاده کرد:
<p>Let's add 2 + 2 : @(2 + 2) </p>
نمونه‌ی دیگر نیاز به تعریف ابتدا و انتهای یک قطعه کد، در حین تعریف مدیریت کنندگان رویدادها است:
<button @onclick="@(()=>Console.WriteLine("Test"))">Click me</button>
در اینجا onclick@ مشخص می‌کند که با کلیک بر روی این دکمه قرار است قطعه کد #C ای اجرا شود. سپس با استفاده از ()@ محدوده‌ی این قطعه کد، مشخص می‌شود و اکنون در داخل آن می‌توان یک anonymous function را تعریف کرد که خروجی آن را در قسمت console ابزارهای توسعه دهندگان مرورگر می‌توان مشاهده کرد:


در اینجا اگر از Console.WriteLine("Test")@ استفاده می‌شد، به معنای انتساب یک رشته‌ی محاسبه شده به رویداد onclick بود که مجاز نیست.
روش دیگر انجام اینکار به صورت زیر است:
@page "/"

<button @onclick="@WriteLog">Click me 2</button>

@code
{
    void WriteLog()
    {
        Console.WriteLine("Test");
    }
}
می‌توان یک متد void را تعریف کرد و سپس فقط نام آن‌را توسط @ به onlick انتساب داد. ذکر این نام، اشاره‌گری خواهد بود به متد اجرا نشده‌ی WriteLog. در این حالت اگر نیاز به ارسال پارامتری به متد WriteLog بود، چطور؟
@page "/"

<button @onclick="@(()=>WriteLogWithParam("Test 3"))">Click me 3</button>

@code
{
    void WriteLogWithParam(string value)
    {
        Console.WriteLine(value);
    }
}
در این حالت نیز می‌توان از روش بکارگیری anonymous function‌ها برای تعریف پارامتر استفاده کرد.

یک نکته: اگر به اشتباه بجای WriteLogWithParam، همان WriteLog قبلی را بنویسیم، کامپایلر (در حال اجرای توسط دستور dotnet watch run) خطای زیر را نمایش می‌دهد؛ پیش از اینکه برنامه در مرورگر اجرا شود:
BlazorRazorSample\Client\Pages\Index.razor(12,25): error CS1501: No overload for method 'WriteLog' takes 1 arguments


امکان تعریف کلاس‌ها در فایل‌های razor.

در فایل‌های razor.، محدود به تعریف یک سری متدها و متغیرهای ساده نیستیم. در اینجا امکان تعریف کلاس‌ها نیز وجود دارد و همچنین می‌توان از کلاس‌های خارجی (کلاس‌هایی که خارج از فایل razor جاری تعریف شده‌اند) نیز استفاده کرد.
@page "/"

<p>Hello, @StringUtils.MyCustomToUpper(name)</p>

@code
{
    public class StringUtils
    {
        public static string MyCustomToUpper(string value) => value.ToUpper();
    }
}
برای نمونه در اینجا یک کلاس کمکی را جهت تعریف متد MyCustomToUpper، اضافه کرده‌ایم. در ادامه نحوه‌ی استفاده از این متد را در پاراگراف تعریف شده، مشاهده می‌کنید که همانند کار با کلاس و متدهای متداول #C است.
البته این کلاس را تنها می‌توان داخل همین کامپوننت استفاده کرد. برای اینکه بتوان از امکانات این کلاس، در سایر کامپوننت‌ها نیز استفاده کرد، می‌توان آن‌را در پروژه‌ی Shared قرار داد. اگر به تصویر ابتدای مطلب جاری دقت کنید، سه پروژه ایجاد شده‌است:
الف) پروژه‌ی کلاینت: که همان WASM است.
ب) پروژه‌ی سرور: که یک پروژه‌ی ASP.NET Core Web API ارائه کننده‌ی سرویس و API آب و هوا است و همچنین هاست کننده‌ی WASM ما.
ج) پروژه‌ی Shared: کدهای این پروژه، بین هر دو پروژه به اشتراک گذاشته می‌شوند و برای مثال محل مناسبی است برای تعریف DTO ها. برای نمونه WeatherForecast.cs قرار گرفته‌ی در آن، DTO یا data transfer object سرویس API برنامه است که قرار است به کلاینت بازگشت داده شود. به این ترتیب دیگر نیازی نخواهد بود تا این تعاریف را در پروژه‌های سرور و کلاینت تکرار کنیم و می‌توان کدهای اینگونه را به اشتراک گذاشت.
کاربرد دیگر آن تعریف کلاس‌های کمکی است؛ مانند StringUtils فوق. به همین به پروژه‌ی Shared مراجعه کرده و کلاس StringUtils را به صورت زیر در آن تعریف می‌کنیم (و یا حتی می‌توان این قطعه کد را داخل یک پوشه‌ی جدید، در همان پروژه‌ی WASM نیز قرار داد):
namespace BlazorRazorSample.Shared
{
    public class StringUtils
    {
        public static string MyNewCustomToUpper(string value) => value.ToUpper();
    }
}
اگر به فایل‌های csproj دو پروژه‌ی سرور و کلاینت جاری مراجعه کنیم، از پیش، مدخلی را به فایل Shared\BlazorRazorSample.Shared.csproj دارند. بنابراین جهت معرفی این اسمبلی به آن‌ها، نیاز به کار خاصی نیست و از پیش، ارجاعی به آن تعریف شده‌است.

پس از آن روش استفاده‌ی از این کلاس کمکی خارجی اشتراکی به صورت زیر است:
@page "/"

@using BlazorRazorSample.Shared

<p>Hello, @StringUtils.MyNewCustomToUpper(name)</p>
ابتدا فضای نام این کلاس را با استفاده از using@ مشخص می‌کنیم و سپس امکان دسترسی به امکانات آن میسر می‌شود.

یک نکته: می‌توان به فایل Client\_Imports.razor مراجعه و مدخل زیر را به انتهای آن اضافه کرد:
@using BlazorRazorSample.Shared
به این ترتیب دیگر نیازی به ذکر این using@ تکراری، در هیچکدام از فایل‌های razor. پروژه‌ی کلاینت نخواهد بود؛ چون تعاریف درج شده‌ی در فایل Client\_Imports.razor سراسری هستند.


کار با حلقه‌ها در فایل‌های razor.

همانطور که عنوان شد، یکی از کاربردهای پروژه‌ی Shared، امکان به اشتراک گذاشتن مدل‌ها، در برنامه‌های کلاینت و سرور است. برای مثال یک پوشه‌ی جدید Models را در این پروژه ایجاد کرده و کلاس MovieDto را به صورت زیر در آن تعریف می‌کنیم:
using System;

namespace BlazorRazorSample.Shared.Models
{
    public class MovieDto
    {
        public string Title { set; get; }

        public DateTime ReleaseDate { set; get; }
    }
}
سپس به فایل Client\_Imports.razor مراجعه کرده و فضای نام این پوشه را اضافه می‌کنیم؛ تا دیگر نیازی به تکرار آن در تمام فایل‌های razor. برنامه‌ی کلاینت نباشد:
@using BlazorRazorSample.Shared.Models
اکنون می‌خواهیم لیستی از فیلم‌ها را در فایل Client\Pages\Index.razor نمایش دهیم:
@page "/"

<div>
    <h3>Movies</h3>
    @foreach(var movie in movies)
    {
        <p>Title: <b>@movie.Title</b></p>
        <p>ReleaseDate: @movie.ReleaseDate.ToString("dd MMM yyyy")</p>
    }
</div>

@code
{
    List<MovieDto> movies = new List<MovieDto>
    {
        new MovieDto
        {
            Title = "Movie 1",
            ReleaseDate = DateTime.Now.AddYears(-1)
        },
        new MovieDto
        {
            Title = "Movie 2",
            ReleaseDate = DateTime.Now.AddYears(-2)
        },
        new MovieDto
        {
            Title = "Movie 3",
            ReleaseDate = DateTime.Now.AddYears(-3)
        }
    };
}
در اینجا در ابتدا لیستی از MovieDto‌ها در قسمت code@ تعریف شده و سپس روش استفاده‌ی از یک حلقه‌ی foreach سی‌شارپ را در کدهای razor نوشته شده، مشاهده می‌کنید که این خروجی را ایجاد می‌کند:


یک نکته: در حین تعریف فیلدهای code@، امکان استفاده‌ی از var وجود ندارد؛ مگر اینکه از آن بخواهیم در داخل بدنه‌ی یک متد استفاده کنیم.

و یا نمونه‌ی دیگری از حلقه‌های #‍C مانند for را می‌توان به صورت زیر تعریف کرد:
    @for(var i = 0; i < movies.Count; i++)
    {
        <div style="background-color: @(i % 2 == 0 ? "blue" : "red")">
            <p>Title: <b>@movies[i].Title</b></p>
            <p>ReleaseDate: @movies[i].ReleaseDate.ToString("dd MMM yyyy")</p>
        </div>
    }
در اینجا روش تغییر پویای background-color هر ردیف را نیز به کمک کدهای razor، مشاهده می‌کنید. اگر شماره‌ی ردیفی زوج بود، با آبی نمایش داده می‌شود؛ در غیراینصورت با قرمز. در اینجا نیز از ()@ برای تعیین محدوده‌ی کدهای #C نوشته شده، کمک گرفته‌ایم.


نمایش شرطی عبارات در فایل‌های razor.

اگر به مثال توکار Client\Pages\FetchData.razor مراجعه کنیم (مربوط به حالت host-- که در ابتدای مطلب عنوان شد)، کدهای زیر قابل مشاهده هستند:
@page "/fetchdata"
@using BlazorRazorSample.Shared
@inject HttpClient Http

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
    }

}
در این مثال، روش کار با یک سرویس تزریق شده‌ی async که قرار است از Web API اطلاعاتی را دریافت کند، مشاهده می‌کنید. در اینجا برخلاف مثال قبلی ما، از روال رویدادگردان OnInitializedAsync برای مقدار دهی لیست یا آرایه‌ای از اطلاعات وضعیت هوا استفاده شده‌است (و نه به صورت مستقیم در یک فیلد قسمت code@). این مورد جزو life-cycle‌های کامپوننت‌های razor است که در قسمت‌های بعد بیشتر بررسی خواهد شد. متد OnInitializedAsync برای بارگذاری اطلاعات یک سرویس از راه دور استفاده می‌شود و در اولین بار اجرای کامپوننت فراخوانی خواهد شد. نکته‌ی مهمی که در اینجا وجود دارد، نال بودن فیلد forecasts در زمان رندر اولیه‌ی کامپوننت جاری است؛ از این جهت که کار دریافت اطلاعات از سرور زمان‌بر است ولی رندر کامپوننت، به صورت آنی صورت می‌گیرد. در این حالت زمانیکه نوبت به اجرای foreach (var forecast in forecasts)@ می‌رسد، برنامه با یک استثنای نال بودن forecasts، متوقف خواهد شد؛ چون هنوز کار OnInitializedAsync به پایان نرسیده‌است:


 برای رفع این مشکل، ابتدا یک if@ مشاهده می‌شود، تا نال بودن forecasts را بررسی کند:
@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
و همچنین عبارت در حال بارگذاری را نمایش می‌دهد. سپس در قسمت else آن، نمایش اطلاعات دریافت شده را توسط یک حلقه‌ی foreach مشاهده می‌کنید. با مقدار دهی forecasts در متد OnInitializedAsync، مجددا کار رندر جدول انجام خواهد شد.


روش نمایش عبارات HTML در فایل‌های razor.

فرض کنید عنوان اول فیلم مثال جاری، به همراه یک تگ HTML هم هست:
new MovieDto
{
   Title = "<i>Movie 1</i>",
   ReleaseDate = DateTime.Now.AddYears(-1)
},
در این حالت اگر برنامه را اجرا کنیم، خروجی آن دقیقا به صورت <Title: <i>Movie 1</i خواهد بود. این مورد به دلایل امنیتی انجام شده‌است. اگر پیشتر تگ‌های HTML را تمیز کرده‌اید و مطمئن هستید که خطری را ایجاد نمی‌کنند، می‌توانید با استفاده از روش زیر، آن‌ها را رندر کرد:
<p>Title: <b>@((MarkupString)movie.Title)</b></p>


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-03.zip
برای اجرای آن وارد پوشه‌ی Server شده و دستور dotnet run را اجرا کنید.