مطالب
کار با Razor در ASP.NET Core 2.0
پیش نویس: این مقاله ترجمه شده فصل 5 کتاب Pro Asp.Net Core MVC2 می‌باشد.


ایجاد یک پروژه با استفاده Razor

در ادامه با هم یک مثال را با استفاده از Razor ایجاد می‌کنیم. یک پروژه جدید را با قالب Empty و با نام Razor ایجاد می‌کنیم.

مراحل:

1- ابتدا در کلاس startup قابلیت MVC را فعال می‌کنیم؛ با قرار دادن کد زیر در متد ConfigureServices:
 services.AddMvc();
و بعد کد زیر را که مربوط به اجرای پروژه‌ی hello Word است ، از متد Configure حذف می‌کنیم:
app.Run(async (context) =>
{
   await context.Response.WriteAsync("Hello World!");
});
در نهایت محتویات  فایل StartUp به صورت زیر می‌باشد:

namespace Razor
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //app.Run(async (context) =>
            //{
            //    await context.Response.WriteAsync("Hello World!");
            //});
        }
    }
}


ایجاد یک Model
 یک پوشه جدید را به نام Models ایجاد و بعد در این پوشه یک کلاس را به نام Product ایجاد می‌کنیم و کدهای زیر را در آن قرار میدهیم:
namespace Razor.Models
{
    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }
}

ایجاد Controller
تنظیمات پیشفرض را در فایل Startup انجام داده‌ایم. درخواست‌هایی را که توسط کاربر ارسال میشوند، به controller پیشفرضی که نامش در اینجا Home است، ارسال می‌کند. حالا ما یک پوشه جدید را به نام Controllers ایجاد می‌کنیم و در آن یک کنترلر جدید را به نام HomeController ایجاد می‌کنیم و کدهای زیر را در آن قرار میدهیم:
namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        // GET: /<controller>/
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            return View(myProduct);
        }
    }
}
در این کلاس یک Action Method را به نام index ایجاد می‌کنیم. سپس در آن یک شیء را از مدل ایجاد و مقدار دهی و آن‌را به View ارسال می‌کنیم تا در زمان بارگذاری View از این شیء استفاده نماییم. نیاز نیست نام View را مشخص کنید. به صورت پیشفرض نام View با نام اکشن متد یکسان می‌باشد.

 
ایجاد View
 برای ایجاد یک View پیشفرض برای Action Method فوق در پوشه Views/Home یک MVC View Page (Razor View Page) را به نام Index.schtml ایجاد می‌کنیم.
- نکته1: پوشه View و داخل آن Home را ایجاد کنید.
- نکته2: معادل MVC View Page در نسخه جدید، Razor View می‌باشد. اگر در لیست این آیتم را انتخاب کنید، در توضیحات پنل سمت راست میتوانید این مطلب را مشاهده کنید.
- نکته3: دقت نمایید برای اینکه پروژه net Core2. باشد و تمام مشخصات موردنظر را داشته باشد، باید نگارش ویژوال استودیو VS 2017.15.6.6 و یا بیشتر باشد.
 
@model Razor.Models.Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
    Content will go here
</body>
</html>

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


استفاده از Model در یک View
برای استفاده از شیء مدل در View، باید در View به آن شیء و مشخصات آن دسترسی داشته باشیم که این دسترسی را Razor با استفاده از کاراکتر @ برای ما ایجاد می‌کند. برای اتصال به Model از عبارت model@ (حتما باید حروف کوچک باشد) استفاده می‌کنیم و برای دسترسی به مشخصات مدل از عبارت Model@ (حتما باید حرف اول آن بزرگ باشد) استفاده می‌کنیم. به کد زیر دقت کنید:

@model Razor.Models.Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
  @Model.Name
</body>
</html>
خط اولی که در View تعریف شده است، با استفاده از عبارت model@ مانند تعریف نوع مدل می‌باشد و کار اتصال مدل به View را انجام میدهد و همین خط باعث میشود زمانی که شما در تگ body عبارت Model@ وبعد دات (.) را میزنید، لیست خصوصیات آن مدل ظاهر میشوند. لیست شدن خصوصیات بعد از دات(.) یکی از کارهای پیشفرض ویژوال استودیو می‌باشد؛ برای اینکه از خطاهای احتمالی کاربر جلوگیری کند.

نتیجه خروجی بالا مانند زیر می‌باشد:

 



معرفی View Imports

زمانیکه بخواهیم به یک کلاس در View دسترسی داشته باشیم، باید فضای نام آن کلاس را مانند کد زیر در بالای View اضافه کنیم. حالا اگر بخواهیم به چند کلاس دسترسی داشته باشیم، باید این کار را به ازای هر کلاس در هر View انجام دهیم که سبب ایجاد کدهای اضافی در View‌ها میشود. برای بهبود این وضعیت می‌توانید یک کلاس View Import را در پوشه‌ی Views ایجاد کنید و تمام فضاهای نام را در آن قرار دهید. با اینکار تمام فضاهای نامی که در این کلاس View Import قرار گرفته‌اند، در تمام Viewهای موجود در پوشه Views قابل دسترسی خواهند بود.

در پوشه View راست کلیک کرده و گزینه Add و بعد New Item را انتخاب می‌کنیم و در کادر باز شده، آیتم MVC View Import Page (در نسخه جدید نام آن  Razor View Imports است) انتخاب می‌کنیم. ویژوال استودیو به صورت پیش فرض نام ViewImports.cshtml_ را برای آن قرار میدهد.


نکته: استاندارد نام گذاری این View این می‌باشد که ابتدای آن کاراکتر (_) حتما وجود داشته باشد.
 
در کلاس تعریف شده با استفاده از عبارت using@ فضای نام‌های خود را قرار میدهیم؛ مانند زیر:
 @using Razor.Models
در این کلاس شما فقط میتوانید فضاهای نام را مانند بالا قرار دهید. پس از آم قسمت فضاهای نام اضافی در Viewها قابل حذف میشوند و در این حالت فقط نام کلاس مدل را در بالای فرم قرار میدهیم مانند زیر:
@model Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
  @Model.Name
</body>
</html>


Layout ها

یکی دیگر از عبارت‌های مهم Razor که در فایل Index وجود دارد، عبارت زیر است:
@{
    Layout = null;
}
شما می‌توانید در بین {} کدهای سی شارپ را قرار دهید. حالا مقدار Layout را مساوی نال قرار داده‌ایم که بگوییم View مستقلی است و از قالب مشخصی استفاده نمی‌کند.

از Layout برای طراحی الگوی Viewها استفاده می‌کنیم. اگر بخواهیم برای View ها یک قالب طراحی کنیم و این الگو بین تمام یا چندتای از آن‌ها مشترک باشد، کدهای مربوط به الگو را با استفاده از Layout ایجاد می‌کنیم و از آن در View ها استفاده می‌کنیم. اینکار برای جلوگیری از درج کدهای تکراری قالب در برنامه انجام میشود. با اینکار اگر بخواهیم در الگو تغییری را انجام دهیم، این تغییر را در یک قسمت انجام میدهم و سپس به تمام Viewها اعمال میشود.
 
Layout
طرحبندی  Viewهای برنامه بطور معمول بین چند View مشترک است و طبق استاندارد ویژوال استودیو در پوشه‌ی Views/Shared قرار میگیرد. برای ایجاد Layout، روی پوشه Views/shared راست کلیک کرده و بعد گزینه Add وبعد NewItem و سپس گزینه MVC View Layout Page (نام آن در نسخه جدید Razor Layout است) را انتخاب می‌کنیم و ابتدای نام آن را به صورت پیشفرض کاراکتر (_) قرار میدهیم.
 


هنگام ایجاد این فایل توسط ویژوال استودیو، کدهای زیر به صورت پیش فرض در فایل ایجاد شده وجود دارند: 
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>
طرحبندی‌ها فرم خاصی از View هستند و دو عبارت @ در کدهای آن وجود دارد. در اینجا فراخوانی RenderBody@ سبب درج محتویات View مشخص شده توسط Action Method در این مکان می‌شود. عبارت دیگری که در اینجا وجود دارد، ViewBag است که برای مشخص کردن عنوان در اینجا استفاده شده‌است.
ViewBag ویژگی مفیدی است که اجازه می‌دهد تا مقادیر و داده‌ها در برنامه گردش داشته باشند و در این مورد بین یک View و Layout منتقل شوند. در ادامه خواهید دید وقتی Layout را به یک نمایه اعمال می‌کنیم، این مورد چگونه کار می‌کند.

عناصر HTML در یک Layout به هر View که از آن استفاده می‌کند، اعمال و توسط آن یک الگو برای تعریف محتوای معمولی ارائه می‌شود؛ مانند کدهای زیر. من برخی از نشانه گذاری‌های ساده را به Layout اضافه کردم تا اثر قالب آن آشکارتر شود:
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <style>
        #mainDiv {
            padding: 20px;
            border: solid medium black;
            font-size: 20pt
        }
    </style>
</head>
<body>
    <h1>Product Information</h1>
    <div id="mainDiv">
        @RenderBody()
    </div>
</body>
</html>
در اینجا یک عنصر عنوان و همچنین بعضی از CSS‌ها را به عنصر div که حاوی عبارت RenderBody@ است، اضافه کرده‌ام؛ فقط برای اینکه مشخص شود، چه محتوایی از طرحبندی سایت می‌آید و چه چیزی از View.
 

اعمال Layout

برای اعمال کردن Layout به یک View، نیاز است مشخصه Layout آن‌را مقدار دهی و سپس Htmlهای اضافی موجود در آن‌را مانند المنت‌های head و Body حذف کنید؛ همانند کدهای زیر:
@model Product
@{
    Layout = "_BasicLayout";
    ViewBag.Title = "Product";
}
در خاصیت Layout، مقدار را برابر نام فایل Layout، بدون پسوند cshtml آن قرار میدهیم. Razor در مسیر پوشه Views/shared و پوشه Views/Home فایل Layout را جستجو می‌کند.
در اینجا عبارت ViewBag.Title را نیز مقدار دهی می‌کنیم. زمانیکه فایل فراخوانی میشود، عنوان آن صفحه با این مقدار، جایگزین خواهد شد.
تغییرات این View بسیار چشمگیر است؛ حتی برای چنین برنامه ساده‌ای. طرحبندی شامل تمام ساختار مورد نیاز برای هر پاسخ HTML است که View را به صورت یک محتوای پویا ارائه می‌دهد و داده‌ها را به کاربر منتقل می‌کند. هنگامیکه MVC فایل Index.cshtmal را پردازش می‌کند، این طرحبندی برای ایجاد پاسخ HTML نهایی یکپارچه می‌شود؛ مانند عکس زیر:
 


 
View Start

بعضی موارد هنوز در برنامه وجود دارند که می‌توان کنترل بیشتری بر روی آن‌ها داشته باشید. مثلا اگر بخواهیم نام یک فایل layout را تغییر دهیم، مجبور هستیم تمام Viewهایی را که از آن Layout استفاده می‌کنند، پیدا کنید و نام Layout استفاده شده در آن‌ها را تغییر دهیم. اینکار احتمال خطای بالایی دارد و امکان دارد بعضی View ها از قلم بیفتند و برنامه دچار خطا شود. بنابراین با استفاده از View Start می‌توانیم این مشکل را برطرف کنیم. وقتی نام Layout تغییر کرد، تنها کافی است نام آن‌را در View Start تغییر دهیم. اکنون زمانیکه برنامه را اجرا می‌کنیم، MVC به دنبال فایل View Start می‌گردد و اگر اطلاعاتی داشته باشد، آن را اجرا می‌کند و الویت این فایل از تمام فایل‌های دیگر بیشتر است و ابتدا تمام آنها اجرا میشوند.

برای ایجاد یک فایل شروع مشاهده، روی پوشه‌ی Views کلیک راست کرده و گزینه add->New Items را انتخاب می‌کنیم و از پنجره باز شده گزینه ( Razor View Start ) Mvc View Start Page را انتخاب می‌کنیم؛ مانند تصویر زیر:


ویژوال استودیو به صورت پیش فرض نام ViewStart.cshtml_ را به عنوان نام آن قرار میدهد؛ شما گزینه‌ی Create را در این حالت انتخاب کنید. محتویات فایل ایجاد شده به صورت زیر می‌باشد:
@{
    Layout = "_Layout";
}
برای اعمال Layout جدید به تمام Viewها، مقدار Layout را معادل طرحبندی خود تغییر میدهیم؛ مانند کد زیر: 
@{
    Layout = "_BasicLayout";
}
از آنجا که فایل View Start دارای مقداری برای Layout می‌باشد، می‌توانیم عبارت‌های مربوطه را در Index.cshtml‌ها حذف کنیم:
@model Product
@{
    ViewBag.Title = "Product";
}
در اینجا لازم نیست مشخص کنیم که من می‌خواهم از فایل View Start استفاده کنم. MVC این فایل را پیدا خواهد کرد و از محتویات آن به طور خودکار استفاده می‌کند. البته باید دقت داشت که مقادیر تعریف شده‌ی در فایل View اولویت دارند و باعث میشوند با معادل‌های فایل View Start جایگزین شوند.

شما همچنین می‌توانید چندین فایل View Start را برای تنظیم مقادیر پیش فرض قسمت‌های مختلف برنامه، استفاده کنید. یک فایل Razor همواره توسط نزدیک‌ترین فایل View start، پردازش می‌شود. به این معنا که شما می‌توانید تنظیمات پیش فرض را با افزودن یک فایل View Start به پوشه Views / Home و یا Views / Shared لغو کنید.

نکته: درک تفاوت میان حذف محتویات فایل View Start یا مساوی Null قرار دادن آن مهم است. اگر View شما مستقل است و شما نمی‌خواهید از آن استفاده کنید، بنابراین مقدار Layout آن‌را صریحا برابر Null قرار دهید. اگر مقدار دهی صریح شما مشخصه Layout را نادیده بگیرید، Mvc فرض می‌کند که میخواهید layout را داشته باشید و مقدار آن را از فایل View Start تامین می‌کند.
 

استفاده از عبارت‌های شرطی در Razor
 
حالا که من اصول و مبانی View و Layout را به شما نشان دادم، قصد دارم به انواع مختلفی از اصطلاحات که Razor آن‌ها را پشتیبانی می‌کند و نحوه استفاده‌ی از آنها را برای ایجاد محتوای نمایشی، ارائه دهم. در یک برنامه MVC، بین نقش‌هایی که توسط View و Action متدها انجام می‌شود، جدایی روشنی وجود دارد. در اینجا قوانین ساده‌ای وجود دارند که در جدول زیر مشخص شده‌اند:

کامپوننت 
انجام میشود 
انجام نمیشود 
  Action Method    یک شیء ViewModel را به View ارسال می‌کند.
  یک فرمت داده را به View ارسال می‌کند.
  View    از شیء ViewModel برای ارائه محتوا به کاربر استفاده می‌کند.
  هر جنبه‌ای از شیء View Model مشخصات را تغییر می‌دهد.
 
برای به دست آوردن بهترین نتیجه از MVC، نیاز به تفکیک و جداسازی بین قسمت‌های مختلف برنامه را دارید. همانطور که می‌بینید، می‌توانید کاملا با Razor کار کنید و این نوع فایل‌ها شامل دستورالعمل‌های سی شارپ نیز هستند. اما شما نباید از Razor برای انجام منطق کسب و کار استفاده کنید و یا هر گونه اشیاء Domain Model خود را دستکاری کنید. کد زیر نشان میدهد که یک عبارت جدید به View اضافه میشود:
*@
@model Product
@{

    ViewBag.Title = "Product";
}
<p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p>
می‌توان برای خصوصیت price، در اکشن متد فرمتی را تعریف و بعد آن را به View ارسال کنیم. این روش کار می‌کند، اما استفاده از این رویکرد منافع الگوی MVC را تضعیف می‌کند و توانایی من برای پاسخ دادن به تغییرات در آینده را کاهش می‌دهد. باید به یاد داشته باشید که در ASP NET Core MVC، استفاده مناسب از الگوی MVC اجتناب ناپذیر است و شما باید از تاثیر تصمیمات طراحی و کدگذاری که انجام می‌دهید مطلع باشید.
 

پردازش داده‌ها در مقابل فرمت

تفاوت بین پردازش داده و قالب بندی داده مهم است.
- نمایش فرمت داده‌ها: به همین دلیل در آموزش قبل من یک نمونه از شیء کلاس Product را برای View ارسال کرده‌ام و نه فرمت خاص یک شیء را به صورت یک رشته نمایشی.
- پردازش داده: انتخاب اشیاء داده‌‌ای برای نمایش، مسئولیت کنترلر است و در این حالت مدلی را برای دریافت و تغییر داده مورد نیاز، فراخوانی می‌کند.
گاهی سخت است که متوجه شویم کدی جهت پردازش داده است و یا فرمت آن.


اضافه نمودن مقدار داده ای

ساده‌ترین کاری را که می‌توانید با یک عبارت Razor انجام دهید این است که یک مقدار داده را در نمایش دهید. رایج‌ترین کار برای انجام آن، استفاده از عبارت Model@ است. ویوو Index یک مثال از این مورد است؛ شبیه به این مورد:
 <p>Product Name: @Model.Name</p>
شما همچنین می‌توانید یک مقدار را با استفاده قابلیت ViewBag نیز به View ارسال نمایید که از این قابلیت در Layout برای تنظیم کردن محتوای عنوان استفاده کردیم. اما در حالت زیر یک مدل نوع دار را به سمت View ارسال کرده‌ایم:
using Microsoft.AspNetCore.Mvc;
using Razor.Models;


namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        // GET: /<controller>/
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            return View(myProduct);
        }
    }
}

خصوصیت ViewBag یک شیء پویا را باز می‌گرداند که می‌تواند برای تعیین خواص دلخواهی مورد استفاده قرار گیرد. از آنجا که ویژگی ViewBag پویا است، لازم نیست که نام خصوصیات را پیش از آن اعلام کنم. اما این بدان معنا است که ویژوال استودیو قادر به ارائه پیشنهادهای تکمیل کننده برای ViewBag نیست.
در مثال زیر از یک مدل نوع دار و مزایای به همراه آن استفاده شده‌است: 
 <p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p> <p>Stock Level: @ViewBag.StockLevel</p>
نتیجه آن‌را در زیر می‌توانید مشاهده کنید:



تنظیم مقادیر مشخص

شما همچنین می‌توانید از عبارات Razor برای تعیین مقدار عناصر، استفاده کنید:
@model Product
@{

    ViewBag.Title = "Product";
}
p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p> 
<p>Stock Level: @ViewBag.StockLevel</p>
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">    
<p>Product Name: @Model.Name</p>    
<p>Product Price: @($"{Model.Price:C2}")</p>   
 <p>Stock Level: @ViewBag.StockLevel</p> 
</div>
در اینجا از عبارات Razor، برای تعیین مقدار برای برخی از ویژگی‌های داده در عنصر div استفاده کرده‌ام.

نکته: ویژگی‌های داده‌ها که نام آنها *-data است، روشی برای ایجاد ویژگی‌های سفارشی برای سال‌ها بوده است و بعنوان بخشی از استاندارد HTML5 است. عموما کدهای جاوا اسکریپت از آن‌ها برای یافتن اطلاعات استفاده می‌کنند.

اگر برنامه را اجرا کنید و به منبع HTML که به مرورگر فرستاده شده نگاهی بیندازید، خواهید دید که Razor مقادیر صفات را تعیین کرده است؛ مانند این:
<div data-productid="1" data-stocklevel="2">    <p>Product Name: Kayak</p>    <p>Product Price: £275.00</p>    <p>Stock Level: 2</p> </div>


استفاده از عبارت‌های شرطی

Razor قادر به پردازش عبارات شرطی است. در ادامه کدهای Index View را که در آن دستورات شرطی اضافه شده‌اند می‌بینید:

@model Product
@{ ViewBag.Title = "Product Name"; }
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">  
  <p>Product Name: @Model.Name</p>   
 <p>Product Price: @($"{Model.Price:C2}")</p> 
   <p>Stock Level:       
 @switch (ViewBag.StockLevel)
{
    case 0:@:Out of Stock                break;           
    case 1:          
    case 2:        
    case 3:            
    <b>Low Stock (@ViewBag.StockLevel)</b>         
       break;      
    default:            
    @: @ViewBag.StockLevel in Stock          
      break;      
  }    
</p>
</div>


برای شروع یک عبارت شرطی، یک علامت @ را در مقابل کلمه کلیدی if یا swicth سی شارپ قرار دهید. سپس بخش کد را داخل } قرار می‌دهیم. درون قطعه کد Razor، می‌توانید عناصر HTML و مقادیر داده را در خروجی نمایش دهید؛ مانند:
 <b>Low Stock (@ViewBag.StockLevel)</b>
در اینجا لازم نیست عناصر یا عبارات را در نقل قول قرار دهیم و یا آنها را به روش خاصی تعریف کنیم. موتور Razor این را به عنوان خروجی برای پردازش تفسیر خواهد کرد.
با این حال، اگر می‌خواهید متن واقعی را در نظر بگیرید و دستورات Razor را لغو کنید،‌می‌توانید از :@ استفاده کنید تا عین آن عبارت درج شود.
مطالب
استفاده از SQL-CE به کمک NHibernate

خلاصه‌ای را در مورد SQL Server CE قبلا در این سایت مطالعه‌ کرده‌اید. در ادامه خلاصه‌ای کاربردی را از تنظیمات و نکات مرتبط به کار با SQL-CE به کمک NHibernate ملاحظه خواهید نمود:

1) دریافت SQL-CE 4.0


همین مقدار برای استفاده از SQL-CE 4.0 به کمک NHibernate کفایت می‌کند و حتی نیازی به نصب سرویس پک یک VS 2010 هم نیست.

2) ابزار سازی جهت ایجاد یک بانک اطلاعاتی خالی SQL-CE

using System;
using System.IO;

namespace NHibernate.Helper.DbSpecific
{
public class SqlCEDbHelper
{
const string engineTypeName = "System.Data.SqlServerCe.SqlCeEngine, System.Data.SqlServerCe";

/// <summary>
/// note: this method will delete existing db and then creates a new one.
/// </summary>
/// <param name="filename"></param>
/// <param name="password"></param>
public static void CreateEmptyDatabaseFile(string filename, string password = "")
{
if (File.Exists(filename))
File.Delete(filename);

var type = System.Type.GetType(engineTypeName);
var localConnectionString = type.GetProperty("LocalConnectionString");
var createDatabase = type.GetMethod("CreateDatabase");

var engine = Activator.CreateInstance(type);

string connectionStr = string.Format("Data Source='{0}';Password={1};Encrypt Database=True", filename, password);
if (string.IsNullOrWhiteSpace(password))
connectionStr = string.Format("Data Source='{0}'", filename);

localConnectionString.SetValue(
obj: engine,
value: connectionStr,
index: null);
createDatabase.Invoke(engine, new object[0]);
}

/// <summary>
/// use this method to compact or encrypt existing db or decrypt it to a new db with all records
/// </summary>
/// <param name="sourceConnection"></param>
/// <param name="destConnection"></param>
public static void CompactDatabase(string sourceConnection, string destConnection)
{
var type = System.Type.GetType(engineTypeName);
var engine = Activator.CreateInstance(type);

var localConnectionString = type.GetProperty("LocalConnectionString");
localConnectionString.SetValue(
obj: engine,
value: sourceConnection,
index: null);

var compactDatabase = type.GetMethod("Compact");
compactDatabase.Invoke(engine, new object[] { destConnection });
}
}
}

کلاس فوق، یک کلاس عمومی است و مرتبط به NHibernate نیست و در همه جا قابل استفاده است.
متد CreateEmptyDatabaseFile یک فایل بانک اطلاعاتی خالی با فرمت مخصوص SQL-CE را برای شما تولید خواهد کرد. به این ترتیب می‌توان بدون نیاز به ابزار خاصی، سریعا یک بانک خالی را تولید و شروع به کار کرد. در این متد اگر کلمه عبوری را وارد نکنید، بانک اطلاعاتی رمزنگاری شده نخواهد بود و اگر کلمه عبور را وارد کنید، دیتابیس اولیه به همراه کلیه اعمال انجام شده بر روی آن در طول زمان، با کمک الگوریتم AES به صورت خودکار رمزنگاری خواهند شد. کل کاری را هم که باید انجام دهید ذکر این کلمه عبور در کانکشن استرینگ است.
متد CompactDatabase، یک متد چند منظوره است. اگر بانک اطلاعاتی SQL-CE رمزنگاری نشده‌ای دارید و می‌خواهید کل آن‌را به همراه تمام اطلاعات درون آن رمزنگاری کنید، می‌توانید جهت سهولت کار از این متد استفاده نمائید. آرگومان اول آن به کانکشن استرینگ بانکی موجود و آرگومان دوم به کانکشن استرینگ بانک جدیدی که تولید خواهد شد، اشاره می‌کند.
همچنین اگر یک بانک اطلاعاتی SQL-CE رمزنگاری شده دارید و می‌خواهید آن‌را به صورت یک بانک اطلاعاتی جدید به همراه تمام رکوردهای آن رمزگشایی کنید، باز هم می‌توان از این متد استفاده کرد. البته بدیهی است که کلمه عبور را باید داشته باشید و این کلمه عبور جایی درون فایل بانک اطلاعاتی ذخیره نمی‌شود. در این حالت در کانکشن استرینگ اول باید کلمه عبور ذکر شود و کانکشن استرینگ دوم نیازی به کلمه عبور نخواهد داشت.

فرمت کلی کانکشن استرینگ SQL-CE هم به شکل زیر است:

Data Source=c:\path\db.sdf;Password=1234;Encrypt Database=True

البته این برای حالتی است که قصد داشته باشید بانک اطلاعاتی مورد استفاده را رمزنگاری کنید یا از یک بانک اطلاعاتی رمزنگاری شده استفاده نمائید. اگر بانک اطلاعاتی شما کلمه عبوری ندارد، ذکر Data Source=c:\path\db.sdf کفایت می‌کند.

این کلاس هم از این جهت مطرح شد که NHibernate می‌تواند ساختار بانک اطلاعاتی را بر اساس تعاریف نگاشت‌ها به صورت خودکار تولید و اعمال کند، «اما» بر روی یک بانک اطلاعاتی خالی SQL-CE از قبل تهیه شده (در غیراینصورت خطای The database file cannot be found. Check the path to the database را دریافت خواهید کرد).

نکته:
اگر دقت کرده باشید در این کلاس engineTypeName به صورت رشته ذکر شده است. چرا؟
علت این است که با ذکر engineTypeName به صورت رشته، می‌توان از این کلاس در یک کتابخانه عمومی هم استفاده کرد، بدون اینکه مصرف کننده نیازی داشته باشد تا ارجاع مستقیمی را به اسمبلی SQL-CE به برنامه خود اضافه کند. اگر این ارجاع وجود داشت، متدهای یاد شده کار می‌کنند، در غیراینصورت در گوشه‌ای ساکت و بدون دردسر و بدون نیاز به اسمبلی خاصی برای روز مبادا قرار خواهند گرفت.


3) ابزار مرور اطلاعات بانک اطلاعاتی SQL-CE

با استفاده از management studio خود SQL Server هم می‌شود با بانک‌های اطلاعاتی SQL-CE کار کرد، اما ... اینبار برخلاف نگارش کامل اس کیوال سرور، با یک نسخه‌ی بسیار بدوی، که حتی امکان rename فیلدها را هم ندارد مواجه خواهید شد. به همین جهت به شخصه برنامه SqlCe40Toolbox را ترجیح می‌دهم و اطمینان داشته باشید که امکانات آن برای کار با SQL-CE از امکانات ارائه شده توسط management studio مایکروسافت، بیشتر و پیشرفته‌تر است!



4) تنظیمات NHibernate جهت کار با SQL-CE

الف) پس از نصب SQL-CE ، فایل‌های آن‌را در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0 می‌توان یافت. درایور ADO.NET آن هم در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop قرار دارد. بنابراین در ابتدا نیاز است تا ارجاعی را به اسمبلی System.Data.SqlServerCe.dll به برنامه خود اضافه کنید (نام پوشه desktop آن هم غلط انداز است. از این جهت که نگارش 4 آن، به راحتی در برنامه‌های ذاتا چند ریسمانی ASP.Net بدون مشکل قابل استفاده است).
نکته مهم: در این حالت NHibernate قادر به یافتن فایل درایور یاد شده نخواهد بود و پیغام خطای «Could not create the driver from NHibernate.Driver.SqlServerCeDriver» را دریافت خواهید کرد. برای رفع آن، اسمبلی System.Data.SqlServerCe.dll را در لیست ارجاعات برنامه یافته و در برگه خواص آن، خاصیت «Copy Local» را true کنید. به این معنا که NHibernate این اسمبلی را در کنار فایل اجرایی برنامه شما جستجو خواهد کرد.

ب) مطلب بعد، تنظیمات ابتدایی NHibernate‌ است جهت شناساندن SQL-CE . مابقی مسایل (نکات mapping، کوئری‌ها و غیره) هیچ تفاوتی با سایر بانک‌های اطلاعاتی نخواهد داشت و یکی است. به این معنا که اگر برنامه شما از ویژگی‌های خاص بانک‌های اطلاعاتی استفاده نکند (مثلا اگر از رویه‌های ذخیره شده اس کیوال سرور استفاده نکرده باشد)، فقط با تغییر کانکشن استرینگ و معرفی dialect و driver جدید، به سادگی می‌تواند به یک بانک اطلاعاتی دیگر سوئیچ کند؛ بدون اینکه حتی بخواهید یک سطر از کدهای اصلی برنامه خود را تغییر دهید.



تنها نکته جدید آن این متد است:

private Configuration getConfig()
{
var configure = new Configuration();
configure.SessionFactoryName("BuildIt");

configure.DataBaseIntegration(db =>
{
db.ConnectionProvider<DriverConnectionProvider>();
db.Dialect<MsSqlCe40Dialect>();
db.Driver<SqlServerCeDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = ConnectionString;
db.Timeout = 10;

//for testing ...
db.LogFormattedSql = true;
db.LogSqlInConsole = true;
});

return configure;
}

که در آن نحوه تعریف MsSqlCe40Dialect و SqlServerCeDriver مشخص شده است.

نکته حاشیه‌ای!
در این مثال primary key از نوع identity تعریف شده و بدون مشکل کار کرد. همین را اگر با EF تست کنید، این خطا را دریافت می‌کنید: «Server-generated keys and server-generated values are not supported by SQL Server Compact». بله، EF نمی‌تواند با primary key از نوع identity حین کار با SQL-CE کار کند. برای رفع آن توصیه شده است که از Guid استفاده کنید!

نکته تکمیلی:
استفاده از Dialect سفارشی در NHibernate


نکته پایانی!
و در پایان باید اشاره کرد که SQL-CE یک بانک اطلاعاتی نوشته شده با دات نت نیست (با CPP نوشته شده است و نصب آن هم نیاز به ران تایم به روز VC را دارد). به این معنا که جهت سیستم‌های 64 بیتی و 32 بیتی باید نسخه مناسب آن‌را توزیع کنید. یا اینکه Target platform پروژه جاری دات نت خود را بر روی X86 قرار دهید (نه بر روی Any CPU پیش فرض) و در این حالت تنها یک نسخه X86 بانک اطلاعاتی SQL-CE و همچنین برنامه خود را برای تمام سیستم‌ها توزیع کنید.

مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت نهم - مسیریابی
یک برنامه، از صفحات و Viewهای مختلفی تشکیل می‌شود و Routing یا مسیریابی، امکان ناوبری بین این Viewها را میسر می‌کند. یک برنامه‌ی AngularJS 2.0، یک برنامه‌ی تک صفحه‌ای وب است. به این معنا که تمام Viewهای برنامه، در یک صفحه نمایش داده می‌شوند؛ که معمولا همان index.html سایت است. هدف از سیستم مسیریابی، مدیریت نحوه‌ی نمایش و قرارگیری این Viewها، درون تک صفحه‌ی برنامه است.


برپایی تنظیمات اولیه‌ی سیستم مسیریابی در AngularJS 2.0

برای کار با سیستم مسیریابی AngularJS 2.0، ابتدا باید اسکریپت‌های آن به صفحه اضافه شوند. در ادامه المان پایه‌ای تعریف شده و سپس باید سرویس پروایدر مسیریابی را رجیستر کرد. جزئیات این موارد را در ادامه بررسی می‌کنیم:

الف) سرویس مسیریابی، جزئی از angular2/core نیست. به همین جهت مدخل اسکریپت متناظر با آن باید به صفحه‌ی اصلی سایت اضافه شود که این مورد، در قسمت اول بررسی پیشنیازهای نصب AngularJS 2.0 صورت گرفته‌است:
 <!-- Required for routing -->
<script src="~/node_modules/angular2/bundles/router.dev.js"></script>
این تعریف در فایل Views\Shared\_Layout.cshtml (و یا index.html) پروژه‌ی جاری موجود است.

ب) افزودن المان base به ابتدای صفحه:
 <!DOCTYPE html>
<html>
<head>
    <base href="/">
بلافاصله پس از تگ head، المان base اضافه می‌شود. این المان به سیستم مسیریابی اعلام می‌کند که چگونه آدرس‌های خود را تشکیل دهد. به صورت پیش فرض، AngularJS 2.0 از آدرس‌هایی با فرمت HTML5 استفاده می‌کند. در این حالت دیگر نیازی به ذکر # برای مشخص سازی مسیریابی‌های محلی نیست.
از آنجائیکه فایل index.html در ریشه‌ی سایت قرار گرفته‌است، مقدار آغازین href آن به / تنظیم شده‌است.

ج) شبیه به حالت ثبت پروایدر HTTP در قسمت قبل، برای ثبت پروایدر مسیریابی نیز به فایل App\app.component.ts مراجعه می‌کنیم:
//same as before ...
import { ROUTER_PROVIDERS } from 'angular2/router';
 
//same as before ... 

@Component({
//same as before …
    providers: [
        ProductService,
        HTTP_PROVIDERS,
        ROUTER_PROVIDERS
    ]
})
//same as before ...
در اینجا سرویس ROUTER_PROVIDERS به خاصیت providers اضافه شده‌است و همچنین import متناظر با آن نیز به ابتدای صفحه اضافه گردیده‌است.
علت ختم شدن نام این سرویس‌ها به PROVIDERS این است که این تعاریف، امکان استفاده‌ی از چندین سرویس زیر مجموعه‌ی آن‌ها را فراهم می‌کنند و صرفا یک سرویس نیستند.


ساخت کامپوننت نمایش جزئیات محصولات

در ادامه می‌خواهیم جزئیات هر محصول را با کلیک بر روی نام آن در لیست محصولات، در آدرسی دیگر به صورتی مجزا مشاهده کنیم. به همین منظور به پوشه‌ی products برنامه مراجعه کرده و دو فایل جدید product-detail.component.ts و product-detail.component.html را ایجاد کنید؛ با این محتوا:
الف) محتوای فایل product-detail.component.html
<div class='panel panel-primary'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
</div>
ب) محتوای فایل product-detail.component.ts
import { Component } from 'angular2/core';

@Component({
    templateUrl: 'app/products/product-detail.component.html'
})
export class ProductDetailComponent  {
    pageTitle: string = 'Product Detail'; 
}
در اینجا یک کامپوننت جدید را ایجاد کرده‌ایم که در قالب آن، مقدار خاصیت pageTitle با روش interpolation در آن درج شده‌است. کلاس ProductDetailComponent، قالب خود را از طریق مقدار دهی آدرس آن در خاصیت templateUrl متادیتای خود، پیدا می‌کند.
اگر دقت کنید، این کامپوننت ویژه دارای خاصیت selector نیست. ذکر selector تنها زمانی اجباری است که بخواهیم این کامپوننت را داخل کامپوننتی دیگر قرار دهیم. در اینجا قصد داریم این کامپوننت را به صورت یک View جدید، توسط سیستم مسیریابی نمایش دهیم و نه به صورت جزئی از یک کامپوننت دیگر.


افزودن تنظیمات مسیریابی به برنامه

مسیریابی در AngularJS 2.0، مبتنی بر کامپوننت‌ها است. به همین جهت، ابتدای کار مسیریابی، مشخص سازی تعدادی از کامپوننت‌ها هستند که قرار است به عنوان مقصد سیستم راهبری (navigation) مورد استفاده قرار گیرند و به ازای هر کدام، یک مسیریابی و Route جدید را تعریف می‌کنیم. تعریف هر Route جدید شامل انتساب نامی به آن، تعیین مسیر مدنظر و مشخص سازی کامپوننت مرتبط است:
 { path: '/products', name: 'Products', component: ProductListComponent },
برای مثال جهت تعریف Route کامپوننت لیست محصولات، نام آن‌را Products، مسیر آن‌را products/ و در آخر کامپوننت آن‌را به نام کلاس متناظر با آن، تنظیم می‌کنیم.
این تنظیمات به عنوان یک متادیتای جدید دیگر به کلاس AppComponent، در فایل app.component.ts اضافه می‌شوند:
//same as before …
import { ROUTER_PROVIDERS, RouteConfig } from 'angular2/router';
 
//same as before …
 
@Component({
    //same as before …
})
@RouteConfig([
    { path: '/welcome', name: 'Welcome', component: WelcomeComponent, useAsDefault: true },
    { path: '/products', name: 'Products', component: ProductListComponent }
])
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
در اینجا decorator جدیدی به نام RouteConfig به کلاس AppComponent اضافه شده‌است و همچنین importهای متناظری نیز در ابتدای این فایل تعریف شده‌اند.
همانطور که ملاحظه می‌کنید، یک کلاس می‌تواند بیش از یک decorator داشته باشد.
()RouteConfig@ را به کامپوننتی الصاق می‌کنیم که قصد میزبانی مسیریابی را دارد (Host component). این مزین کننده، آرایه‌ای از اشیاء را قبول می‌کند و هر شیء آن دارای خواصی مانند مسیر، نام و کامپوننت است. باید دقت داشت که نام هر مسیریابی تعریف شده باید pascal case باشد. در غیراینصورت مسیریاب ممکن است این نام را با path اشتباه کند.  
همچنین امکان تعریف خاصیت دیگری به نام useAsDefault نیز در اینجا میسر است. از آن جهت تعریف مسیریابی پیش فرض سیستم، در اولین بار نمایش آن، استفاده می‌شود.

در اینجا نام کامپوننت، رشته‌ای ذکر نمی‌شود و دقیقا اشاره دارد به نام کلاس متناظر. بنابراین هر نام کلاسی که در اینجا اضافه می‌شود، باید به همراه import ماژول آن نیز در ابتدای فایل جاری باشد. به همین جهت اگر تنظیمات فوق را اضافه کنید، ذیل کلمه‌ی WelcomeComponent یک خط قرمز مبتنی بر عدم تعریف آن کشیده می‌شود. برای تعریف آن، پوشه‌ی جدیدی را به ریشه‌ی سایت به نام home اضافه کنید و به آن دو فایل ذیل را اضافه نمائید:
الف) محتوای فایل welcome.component.ts
import { Component } from 'angular2/core';
 
@Component({
    templateUrl: 'app/home/welcome.component.html'
})
export class WelcomeComponent {
    public pageTitle: string = "Welcome";
}
ب) محتوای فایل welcome.component.html
<div class="panel panel-primary">
    <div class="panel-heading">
        {{pageTitle}}
    </div>
    <div class="panel-body">
        <h3 class="text-center">Default page</h3>
    </div>
</div>
کار این کامپوننت، نمایش صفحه‌ی آغازین برنامه است؛ بر اساس تنظیم useAsDefault: true مسیریابی‌های تعریف شده‌.
پس از تعریف این کامپوننت، اکنون باید import ماژول آن‌را به ابتدای فایل app.component.ts اضافه کنیم، تا مشکل عدم شناسایی نام کلاس WelcomeComponent برطرف شود:
 import { WelcomeComponent } from './home/welcome.component';


فعال سازی مسیریابی‌های تعریف شده

روش‌های مختلفی برای دسترسی به اجزای یک برنامه وجود دارند؛ برای مثال کلیک بر روی یک لینک، دکمه و یا تصویر و سپس فعال سازی مسیریابی متناظر با آن. همچنین کاربر می‌تواند آدرس صفحه‌ای را مستقیما در نوار آدرس‌های مرورگر وارد کند. به علاوه امکان کلیک بر روی دکمه‌های back و forward مرورگر نیز همواره وجود دارند. تنظیمات مسیریابی‌های انجام شده، دو مورد آخر را به صورت خودکار مدیریت می‌کنند. در اینجا تنها باید مدیریت اولین حالت ذکر شده را با اتصال مسیریابی‌ها به اعمال کاربران، انجام داد.
به همین جهت منویی را به بالای صفحه‌ی برنامه اضافه می‌کنیم. برای این منظور، فایل app.component.ts را گشوده و خاصیت template کامپوننت AppComponent را به نحو ذیل تغییر می‌دهیم:
@Component({
    //same as before …
    template: `
     <div>
        <nav class='navbar navbar-default'>
            <div class='container-fluid'>
                <a class='navbar-brand'>{{pageTitle}}</a>
                <ul class='nav navbar-nav'>
                    <li><a [routerLink]="['Welcome']">Home</a></li>
                    <li><a [routerLink]="['Products']">Product List</a></li>
                </ul>
            </div>
        </nav>
        <div class='container'>
            <router-outlet></router-outlet>
        </div>
     </div>
    `,
    //same as before …
})
در اینجا یک navigation bar بوت استرپ 3، جهت تعریف منوی بالای صفحه اضافه شده‌است.
سپس جهت تعریف لینک‌های هر آیتم، از یک دایرکتیو توکار AngularJS 2.0 به نام routerLink استفاده می‌کنیم. هر routerLink به یکی از آیتم‌های تنظیم شده‌ی در RouteConfig بایند می‌شود. بنابراین نام‌هایی که در اینجا قید شده‌اند، دقیقا نام‌هایی هستند که در خاصیت name هر کدام از اشیاء تشکیل دهنده‌ی RouteConfig، تعریف و مقدار دهی گردید‌ه‌اند.
اکنون اگر کاربر بر روی یکی از لینک‌های Home و یا Product List کلیک کند، مسیریابی متناظر با آن فعال می‌شود (بر اساس این نام، در لیست عناصر RouteConfig جستجویی صورت گرفته و عنصر معادلی بازگشت داده می‌شود) و سپس View آن کامپوننت نمایش داده خواهد شد.
تا اینجا دایرکتیو جدید routerLink به قالب کامپوننت اضافه شد‌ه‌است؛ اما AngularJS 2.0 نمی‌داند که باید آن‌را از کجا دریافت کند. به همین جهت ابتدا import آن‌را (ROUTER_DIRECTIVES) به ابتدای ماژول جاری اضافه خواهیم کرد:
 import { ROUTER_PROVIDERS, RouteConfig, ROUTER_DIRECTIVES } from 'angular2/router';
و سپس خاصیت دایرکتیوهای کامپوننت ریشه‌ی سایت را نیز با آن مقدار دهی خواهیم کرد:
 directives: [ROUTER_DIRECTIVES],
علت جمع بود نام این دایرکتیو این است که routerLink استفاده شده، یکی از اعضای مجموعه‌ی دایرکتیوهای مسیریابی توکار AngularJS 2.0 است.

تا اینجا اگر دقت کرده باشید، کامپوننت نمایش لیست محصولات را از کامپوننت ریشه‌ی سایت حذف کرده‌ایم و بجای آن منوی بالای سایت را نمایش می‌دهیم که توسط آن می‌توان به صفحه‌ی آغازین و یا صفحه‌ی نمایش لیست محصولات، رسید. به همین جهت خاصیت directives دیگر شامل ذکر کلاس کامپوننت لیست محصولات نیست.

در انتهای قالب کامپوننت ریشه‌ی سایت، یک دایرکتیو جدید به نام router-outlet نیز تعریف شده‌است. وقتی یک کامپوننت فعال می‌شود، نیاز است View مرتبط با آن نیز نمایش داده شود. دایرکتیو router-outlet محل نمایش این View را مشخص می‌کند.

اکنون اگر برنامه را اجرا کنیم، به این شکل خواهیم رسید:


اگر دقت کنید، آدرس بالای صفحه، در اولین بار نمایش آن به http://localhost:2222/welcome تنظیم شده و این مقدار دهی بر اساس خاصیت useAsDefault تنظیمات مسیریابی سایت انجام شده‌است (نمایش welcome به عنوان صفحه‌ی اصلی و پیش فرض).
همچنین با کلیک بر روی لینک لیست محصولات، کامپوننت آن فعال شده و نمایش داده می‌شود. محل قرارگیری این کامپوننت‌ها، دقیقا در محل قرارگیری دایرکتیو router-outlet است.


ارسال پارامترها به سیستم مسیریابی

در ابتدا بحث، مقدمات کامپوننت نمایش جزئیات یک محصول انتخابی را تهیه کردیم. برای فعال سازی این کامپوننت و مسیریابی آن، نیاز است بتوان پارامتری را به سیستم مسیریابی ارسال کرد. برای مثال با انتخاب آدرس product/5، جزئیات محصول با ID مساوی 5 نمایش داده شود.
برای این منظور:
الف) اولین قدم، تعریف مسیریابی آن است. به همین جهت به فایل app.component.ts مراجعه و دو تغییر ذیل را به آن اعمال کنید:
//same as before …

import { ProductDetailComponent } from './products/product-detail.component';
 
@Component({
    //same as before …
})
@RouteConfig([
    //same as before …
    { path: '/product/:id', name: 'ProductDetail', component: ProductDetailComponent }
])
//same as before …
ابتدا مسیریابی جدیدی به نام ProductDetail اضافه شده‌است و سپس ماژول دربرگیرنده‌ی کلاس کامپوننت آن نیز import شده‌است.
تفاوت این مسیریابی با نمونه‌های قبلی در تعریف id:/ است. پس از ذکر :/، نام یک متغیر عنوان می‌شود و اگر نیاز به چندین متغیر بود، همین الگو را تکرار خواهیم کرد.

ب) سپس نحوه‌ی فعال سازی این مسیریابی را توسط تعریف لینکی جدید، معرفی می‌کنیم. بنابراین فایل قالب product-list.component.html را گشوده و سپس بجای نمایش عنوان محصول:
 <td>{{ product.productName }}</td>
لینک به جزئیات آن‌را نمایش می‌دهیم:
<td>
    <a [routerLink]="['ProductDetail', {id: product.productId}]">
        {{product.productName}}
    </a>
</td>
نحوه‌ی تعریف این لینک، با لینک‌هایی که در منوی بالای سایت اضافه کردیم، یکی است؛ با این تفاوت که اکنون پارامتر دومی را به قسمت یافتن نام این Route، جهت مشخص سازی روش مقدار دهی متغیر id، تعریف کرده‌ایم. در اینجا id هر لینک از productId بایند شده تامین می‌شود.
اکنون که از دایرکتیو جدید routerLink در این قالب استفاده شده‌است، نیاز است تعریف دایرکتیو آن‌را به متادیتای کلاس کامپوننت لیست محصولات نیز اضافه کنیم تا AngularJS 2.0 بداند آن‌را از کجا باید تامین کند:
import { Component, OnInit } from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';
//same as before …
 
@Component({
    //same as before …
    directives: [StarComponent, ROUTER_DIRECTIVES]
})

در ادامه اگر برنامه را اجرا کنید، عنوان‌های محصولات، به آدرس نمایش جزئیات آن‌ها لینک شده‌اند:


ج) در آخر زمانیکه View نمایش جزئیات محصول فعال می‌شود، نیاز است این id را از url جاری دریافت کند. به همین جهت فایل product-detail.component.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
import { Component } from 'angular2/core';
import { RouteParams } from 'angular2/router';
 
@Component({
    templateUrl: 'app/products/product-detail.component.html'
})
export class ProductDetailComponent {
    pageTitle: string = 'Product Detail';
 
    constructor(private _routeParams: RouteParams) {
        let id = +this._routeParams.get('id');
        this.pageTitle += `: ${id}`;
    } 
}
با نحوه‌ی تزریق وابستگی‌ها در قسمت هفتم آشنا شدیم. در اینجا سرویس توکار RouteParams به سازنده‌ی کلاس تزریق شده‌‌است. با استفاده از آن می‌توان به id ارسالی از طریق url دسترسی یافت. در اینجا پارامتری که به متد get ارسال می‌شود، باید با نام پارامتری که در تنظیمات آغازین مسیریابی سیستم تعریف گردید، تطابق داشته باشد.
در این حالت، id دریافتی، به متغیر pageTitle اضافه شده و در قالب مربوطه به صورت خودکار نمایش داده می‌شود.

تا اینجا اگر برنامه را اجرا کنید، صفحه‌ی نمایش جزئیات یک محصول، با کلیک بر روی عناوین آن‌ها به صورت زیر نمایش داده می‌شود:



افزودن دکمه‌ی back با کدنویسی

اکنون برای بازگشت مجدد به لیست محصولات، می‌توان از دکمه‌ی back مرورگر استفاده کرد، اما امکان طراحی این دکمه در قالب‌ها نیز پیش بینی شده‌است.
برای این منظور قالب product-detail.component.html را به نحو ذیل بازنویسی می‌کنیم:
<div class='panel panel-primary'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
    <div class='panel-footer'>
        <a class='btn btn-default' (click)='onBack()' style='width:80px'>
            <i class='glyphicon glyphicon-chevron-left'></i> Back
        </a>
    </div>    
</div>
در اینجا دکمه‌ی بازگشت به صفحه‌ی قبلی اضافه شده‌است که به متد onBack در کلاس مرتبط با این قالب، متصل است.

سپس کدهای product-detail.component.ts را به صورت ذیل تکمیل خواهیم کرد:
import { Component } from 'angular2/core';
import { RouteParams, Router } from 'angular2/router';
 
@Component({
    templateUrl: 'app/products/product-detail.component.html'
})
export class ProductDetailComponent {
    pageTitle: string = 'Product Detail';
 
    constructor(private _routeParams: RouteParams, private _router: Router) {
        let id = +this._routeParams.get('id');
        this.pageTitle += `: ${id}`;
    }
 
    onBack(): void {
        this._router.navigate(['Products']);
    }
}
در اینجا سرویس جدیدی به نام Router در سازنده‌ی کلاس تزریق شده‌است. این سرویس امکان فراخوانی متدهایی مانند navigate را جهت حرکت به مسیریابی مشخصی، میسر می‌کند. پارامتری که به این متد ارسال می‌شود، دقیقا معادل همان پارامتری است که به دایرکتیو routerLink ارسال می‌گردد و در اینجا صرفا نام یک مسیریابی مشخص شده‌است؛ بدون ذکر پارامتری.


رفع تداخل مسیریابی‌های ASP.NET MVC با مسیریابی‌های AngularJS 2.0

در طی بحث جاری عنوان شد که اگر کاربر مسیر http://localhost:2222/product/2 را جایی ثبت کرده یا bookmark کند، پس از فراخوانی مستقیم آن در نوار آدرس‌های مرورگر، بلافاصله به این آدرس هدایت خواهد شد. این مورد صحیح است اگر از index.html بجای بکارگیری ASP.NET MVC، جهت هاست برنامه استفاده شود. اگر چنین آدرسی را در یک برنامه‌ی ASP.NET MVC فراخوانی کنیم، ابتدا به دنبال کنترلری به نام product می‌گردد (ابتدا وارد موتور ASP.NET MVC می‌شود) و چون این کنترلر در سمت سرور تعریف نشده‌است، پیام 404 و یا یافت نشد را مشاهده خواهید کرد و فرصت به اجرای برنامه‌ی AngularJS نخواهد رسید.
برای حل این مشکل نیاز است یک route جدید را به نام catch all، در انتهای مسیریابی‌های فعلی اضافه کنید؛ تا سایر درخواست‌های رسیده را به صفحه‌ی نمایش برنامه‌ی AngularJS هدایت کند:
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            constraints: new { controller = "Home" } // for catch all to work, Home|About|SomeName
        );
 
        // Route override to work with Angularjs and HTML5 routing
        routes.MapRoute(
            name: "NotFound",
            url: "{*catchall}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part9.zip


خلاصه‌ی بحث

حین ایجاد کامپوننت‌ها باید به نحوه‌ی نمایش آن‌ها نیز فکر کرد. اگر کامپوننتی قرار است داخل یک کامپوننت دیگر نمایش یابد، باید دارای selector باشد. یک چنین کامپوننتی نیاز به تعریف مسیریابی ندارد. برای کامپوننت‌هایی که به عنوان یک View مستقل طراحی می‌شوند و قرار است در یک صفحه‌ی مجزا نمایش داده شوند، نیازی به تعریف selector نیست؛ اما باید برای آن‌ها مسیریابی‌های ویژه‌ای را تعریف کرد. همچنین نیاز است مدیریت اعمال کاربران را جهت فعال سازی آن‌ها نیز مدنظر داشت. برای استفاده از امکانات مسیریابی توکار AngularJS 2.0 نیاز است اسکریپت آن‌را به صفحه‌ی اصلی اضافه کرد. سپس باید المان base را جهت نحوه‌ی تشکیل آدرس‌های مسیریابی، به صفحه اضافه کرد. در ادامه کار ثبت ROUTER_PROVIDERS در بالاترین سطح سلسه مراتب کامپوننت‌های سایت انجام می‌شود. با استفاده از RouteConfig کار تنظیمات ابتدایی مسیریابی صورت خواهد گرفت. این decorator به کامپوننتی که قرار است کار میزبانی مسیریابی را انجام دهد، متصل می‌شود. پس از تعریف مسیریابی‌ها با ذکر یک نام منحصربفرد، مسیری که باید توسط کاربر وارد شود و نام کامپوننت مدنظر، با استفاده از دایرکتیو routerLink کار تعریف این آدرس‌ها، در رابط کاربری برنامه انجام می‌شود. این دایرکتیو جدید، جزئی از مجموعه‌ی ROUTER_DIRECTIVES است که باید به لیست دایرکتیوهای کامپوننت ریشه‌های سایت و هر کامپوننتی که از routeLink استفاده می‌کند، اضافه شود. برای بایند این دایرکتیو به مسیریابی‌های تعریف شده، سمت راست این اتصال باید به آرایه‌ای از مقادیر مقدار دهی شود که اولین عنصر آن، نام یکی از عناصر مسیریابی تعریف شده‌ی در RouteConfig است. از دومین عنصر آن برای مقدار دهی پارامترهای ارسالی به سیستم مسیریابی استفاده می‌شود. کار دایرکتیو router-outlet، مشخص سازی محل نمایش یک View است که عموما در قالب میزبان مسیریابی قرار می‌گیرد. برای تعیین پارامترهای مسیریابی، از الگوی paramName:/ استفاده می‌شود. برای دسترسی به این مقادیر در یک کامپوننت، می‌توان از سرویس RouteParams استفاده کرد. برای فعال سازی یک مسیریابی با کدنویسی، از سرویس Router و متد navigate آن کمک می‌گیریم.
مطالب
لیست اکران‌های نوروزی MIX09

MIX09 | Web Design and Development Conference