نظرات مطالب
بررسی خطای cycles or multiple cascade paths و یا cyclical reference در EF Code first
در متن عنوان شد «راه دیگر، استفاده از تنظیمات Fluent و تنظیم WillCascadeOnDelete به false است که به صورت پیش فرض در حالات ذکر شده (روابط چند به چند و یا کلید خارجی غیرنال پذیر)، true است.» « از این لحاظ که SQL Server در این حالت (true بودن حذف آبشاری) نمی‌تواند در مورد نحوه حذف خودکار رکوردهای وابسته درست تصمیم‌گیری و عمل کند »
نظرات مطالب
EF Code First #14

ممنون

این کد شما زمانی هست که من رکورد رو ذخیره کرده باشم و لی من حالتی رو می‌خوام که یک شی از جدول رو در برنامه ایحاد کردم و بعدش می‌خوام تست کنم که کاربر در ستون هاش مقداری وارد کرده یا نه که اگه حتی یک مقدار وارد کرده بود پیغام << آیا می‌خواهید شخص مورد نظر اضافه شود >> را بدم که اگه تایید کرد من اونو ضافه کنم .

من وقتی با کد : contex.entry(table1).state وضعیت شی table1 رو که ایجاد کردم رو قبل از هر کاری چک می‌کنم این کد مقدار deteced رو می‌ده و وقتی که ستونهای شی table1 رومقدار دهی می‌کنم مقدار deteced  رو باز میده و وقتی این شی رو با استفاده از متد savecheng در دیتابیس ذخیره می‌کنم بعد state رو چک می‌کنم مقدار unchenged رو بهم می‌ده

لطفا در این خصوص کمک کنید

مطالب
بررسی تغییرات Blazor 8x - قسمت چهارم - معرفی فرم‌های جدید تعاملی
در قسمت قبل مشاهده کردیم که چگونه می‌توان کل برنامه را به صورت سراسری، تعاملی کرد تا بتوان توسط آن، Blazor Server سنتی را شبیه سازی نمود؛ اما ... آیا واقعا نیاز است چنین کاری را انجام دهیم؟! چون در این صورت از قابلیت‌‌های جدید SSR به همراه Blazor 8x محروم می‌شویم. اگر کل قابلیت‌های تعاملی مورد نیاز ما در حد یک فرم و ارسال اطلاعات آن به سمت سرور است، می‌توان در Blazor 8x هنوز هم در همان حالت SSR قرار گرفت و از فرم‌های جدید تعاملی آن استفاده کرد تا برای پردازش چنین مواردی، نیازی به برقراری اتصال دائم SignalR نباشد. جزئیات نحوه‌ی کار با اینگونه فرم‌ها را در ادامه بررسی می‌کنیم.


امکان تعریف HTML Forms استاندارد در Blazor 8x

فرم‌های استاندارد HTML، پیش از ظهور جاوااسکریپت و SPAها وجود داشتند (دقیقا همان زمانیکه که فقط مفهوم SSR وجود خارجی داشت) و هنوز هم جزء مهمی از اغلب برنامه‌های وب را تشکیل می‌دهند. با ارائه‌ی دات نت 8 و قابلیت server side rendering آن، کامپوننت‌های برنامه، فقط یکبار در سمت سرور رندر شده و HTML ساده‌ی آن‌ها به سمت مرورگر کاربر بازگشت داده می‌شود. در این حالت، فرم‌های استاندارد HTML، امکان دریافت ورودی‌های کاربر و ارسال داده‌های آن‌ها را به سمت سرور میسر می‌کنند (چون دیگر خبری از اتصال دائم SignalR نیست و باید اطلاعات را به همان نحو استاندارد پروتکل HTTP، به سمت سرور Post کرد). در دات نت 8، دو راه‌حل برای کار با فرم‌ها در برنامه‌های Blazor وجود دارد: استفاده از EditForm خود Blazor و یا استفاده از HTML forms استاندارد و ساده، به همان نحوی که بوده و هست.


روش کار با EditForm در برنامه‌های Blazor SSR

البته ما قصد استفاده از فرم‌های ساده‌ی HTML را در اینجا نداریم و ترجیح می‌دهیم که از همان EditForm استفاده کنیم. EditForms در Blazor بسیار مفید بوده و امکان بایند خواص یک مدل را به اجزای مختلف ورودی‌های تعریف شده‌ی در آن میسر می‌کند و همچنین قابلیت‌هایی مانند اعتبارسنجی و امثال آن‌را نیز به همراه دارد (اطلاعات بیشتر). اما چگونه می‌توان از این امکان در برنا‌مه‌های Blazor SSR نیز استفاده کرد؟
برای این منظور، ابتدا مثالی را به صورت زیر تکمیل می‌کنیم (که بر اساس قالب dotnet new blazor --interactivity Server تهیه شده) و سپس توضیحات آن ارائه خواهد شد:

الف) تهیه یک مدل برای تعریف محل‌های مرتبط با یک سفارش در فایل Models/OrderPlace.cs

using System.ComponentModel.DataAnnotations;

namespace Models;

public record OrderPlace
{
    public Address BillingAddress { get; set; } = new();
    public Address ShippingAddress { get; set; } = new();
}

public class Address
{
    [Required] public string Name { get; set; } = default!;
    public string? AddressLine1 { get; set; }
    public string? AddressLine2 { get; set; }
    public string? City { get; set; }
    [Required] public string PostCode { get; set; } = default!;
}

ب) تهیه‌ی یک کامپوننت Editor برای دریافت اطلاعات آدرس فوق در فایل Components\Pages\Chekout\AddressEntry.razor

@inherits Editor<Models.Address>

<div>
    <label>Name</label>
    <InputText @bind-Value="Value.Name"/>
</div>
<div>
    <label>Address 1</label>
    <InputText @bind-Value="Value.AddressLine1"/>
</div>
<div>
    <label>Address 2</label>
    <InputText @bind-Value="Value.AddressLine2"/>
</div>
<div>
    <label>City</label>
    <InputText @bind-Value="Value.City"/>
</div>
<div>
    <label>Post Code</label>
    <InputText @bind-Value="Value.PostCode"/>
</div>

ج) استفاده از مدل و ادیتور فوق در یک EditForm تغییر یافته برای کار با برنامه‌های Blazor SSR در فایل Components\Pages\Chekout\Checkout.razor

@page "/checkout"

@using Models
@if (!_submitted && PlaceModel != null)
{
    <EditForm Model="PlaceModel" method="post" OnValidSubmit="SubmitOrder" FormName="checkout">
        <DataAnnotationsValidator/>

        <h4>Bill To:</h4>
        <AddressEntry @bind-Value="PlaceModel.BillingAddress"/>

        <h4>Ship To:</h4>
        <AddressEntry @bind-Value="PlaceModel.ShippingAddress"/>

        <button type="submit">Submit</button>
        <ValidationSummary/>
    </EditForm>
}

@if (_submitted && PlaceModel != null)
{
    <div>
        <h2>Order Summary</h2>

        <h3>Shipping To:</h3>
        <dl>
            <dt>Name</dt>
            <dd>@PlaceModel.BillingAddress.Name</dd>
            <dt>Address 1</dt>
            <dd>@PlaceModel.BillingAddress.AddressLine1</dd>
            <dt>Address 2</dt>
            <dd>@PlaceModel.BillingAddress.AddressLine2</dd>
            <dt>City</dt>
            <dd>@PlaceModel.BillingAddress.City</dd>
            <dt>Post Code</dt>
            <dd>@PlaceModel.BillingAddress.PostCode</dd>
        </dl>
    </div>
}

@code {
    bool _submitted;

    [SupplyParameterFromForm]
    public OrderPlace? PlaceModel { get; set; }

    protected override void OnInitialized()
    {
        PlaceModel ??= GetOrderPlace();
    }

    private void SubmitOrder()
    {
        _submitted = true;
    }

    private static OrderPlace GetOrderPlace() =>
        new()
        {
            BillingAddress = new Address
                             {
                                 PostCode = "12345",
                                 Name = "Test 1",
                             },
            ShippingAddress = new Address
                              {
                                  PostCode = "67890",
                                  Name = "Test 2",
                              },
        };

}
توضیحات:
باید بخاطر داشت که این فرم بر اساس حالت Server Side Rendering در اختیار مرورگر کاربر قرار می‌گیرد. یعنی برای بار اول، یک HTML خالص، در سمت سرور بر اساس اطلاعات آن تهیه شده و بازگشت داده می‌شود و زمانیکه به کاربر نمایش داده شد، دیگر برخلاف Blazor Server پیشین، اتصال SignalR ای وجود ندارد تا قابلیت‌های تعاملی آن‌را مدیریت کند. در این حالت اگر به view source صفحه‌ی جاری رجوع کنیم، چنین خروجی قابل مشاهده‌است:
<form method="post">
   <input type="hidden" name="_handler" value="checkout" />
   <input type="hidden" name="__RequestVerificationToken" value="CfDxxx" />
.
.
.
   <button type="submit">Submit</button>
</form>
یعنی زمانیکه این فرم به سمت سرور ارسال می‌شود، همان HTTP POST استاندارد رخ می‌دهد و برای اینکار، نیازی به اتصال وب‌سوکت SignalR ندارد.
این EditForm تعریف شده، دو قسمت اضافه‌تر را نسبت به EditFormهای نگارش‌های قبلی Blazor دارد:
<EditForm Model="PlaceModel" method="post" OnValidSubmit="SubmitOrder" FormName="checkout">
در اینجا نوع HTTP Method ارسال فرم، مشخص شده و همچنین یک FormName نیز تعریف شده‌است. علت اینجا است که Blazor باید بتواند اطلاعات POST شده و دریافتی در سمت سرور را به کامپوننت متناظری نگاشت کند؛ به همین جهت این نامگذاری، ضروری است.
همانطور که در نحوه‌ی تعریف فرم HTML ای فوق مشخص است، فیلد مخفی handler_، کار متمایز ساختن این فرم را به عهده داشته و از مقدار آن در سمت سرور جهت یافتن کامپوننت متناظر، استفاده خواهد شد.

همچنین برای دریافت و پردازش این اطلاعات در سمت سرور، تنها کافی است خاصیت مرتبط با آن‌را با ویژگی SupplyParameterFromForm مزین کنیم:
[SupplyParameterFromForm]
public OrderPlace? PlaceModel { get; set; }

جریان کاری این فرم به صورت خلاصه به نحو زیر است (که در آن متد OnInitialized دوبار فراخوانی می‌شود و باید به آن دقت داشت):
- در بار اول نمایش این صفحه (با فراخوانی مسیر /checkout در مرورگر)، متد OnInitialized فراخوانی شده و در آن، مقدار شیء PlaceModel نال است.
- بنابراین به متد GetOrderPlace مراجعه کرده و اطلاعاتی را دریافت می‌کند؛ برای مثال، این اطلاعات را از سرویسی می‌خواند.
- پس از پایان هر روال رخدادگردانی در Blazor، در پشت صحنه به صورت خودکار، متد تغییر حالت جاری کامپوننت (متد StateHasChanged) هم فراخوانی می‌شود. این فراخوانی خودکار، باعث رندر مجدد UI آن بر اساس اطلاعات جدید خواهد شد. یعنی قسمت‌های نمایش فرم و نمایش اطلاعات ارسالی، یکبار ارزیابی شده و در صورت برقراری شرط‌ها، نمایش داده می‌شوند.
- در ادامه، کاربر فرم را پر کرده و به سمت سرور POST می‌کند.
- پیش از هر رخ‌دادی، خواص شیء PlaceModel به علت مزین بودن به ویژگی SupplyParameterFromForm، بر اساس اطلاعات ارسالی به سرور، مقدار دهی می‌شوند.
- سپس متد OnInitialized فراخوانی شده و چون اینبار مقدار PlaceModel نال نیست، به متد GetOrderPlace جهت دریافت مقادیر ابتدایی خود مراجعه نمی‌کند. سطر تعریف شده‌ی در متد OnInitialized فقط زمانی سبب مقدار دهی شیء PlaceModel می‌شود که مقدار این شیء، نال باشد (یعنی فقط در اولین بار نمایش صفحه)؛ اما اگر این مقدار توسط پارامتر مزین شده‌ی به SupplyParameterFromForm به علت ارسال داده‌های فرم به سرور، مقدار دهی شده باشد، دیگر به منبع داده‌ی ابتدایی رجوع نمی‌کند.
- چون متد رخ‌دادگردان OnInitialized فراخوانی شده، پس از پایان آن (و فراخوانی خودکار متد StateHasChanged در انتهای آن)، یکبار دیگر کار رندر UI فرم جاری بر اساس اطلاعات جدید، انجام خواهد شد.
- اکنون است که پس از طی این رخ‌دادها، متد رویدادگردان SubmitOrder فراخوانی می‌شود. یعنی زمانیکه این متد فراخوانی می‌شود، شیء PlaceModel بر اساس اطلاعات رسیده‌ی از طرف کاربر، مقدار دهی شده و آماده‌ی استفاده است (برای مثال آماده‌ی ذخیره سازی در بانک اطلاعاتی؛ با فراخوانی سرویسی در اینجا).
- پس از پایان فراخوانی متد رویدادگردان SubmitOrder، به علت تغییر حالت کامپوننت (و فراخوانی خودکار متد StateHasChanged در انتهای آن)، یکبار دیگر نیز کار رندر UI فرم جاری بر اساس اطلاعات جدید انجام خواهد شد. یعنی اینبار قسمت Order Summary نمایش داده می‌شود.


مدیریت تداخل نام‌های HTML Forms در Blazor 8x SSR

تمام فرم‌هایی که به این صورت در برنامه‌های Blazor SSR مدیریت می‌شوند، باید دارای نام منحصربفردی که توسط خاصیت FormName مشخص می‌شود، باشند. برای جلوگیری از این تداخل نام‌ها، کامپوننت جدیدی به نام FormMappingScope معرفی شده‌است که نمونه‌ای از آن‌را در فایل فرضی Components\Pages\Chekout\CheckoutForm.razor تعریف شده‌ی به صورت زیر مشاهده می‌کنید:

@page "/checkout"

<FormMappingScope Name="store-checkout">
    <CheckoutForm />
</FormMappingScope>
در اینجا ابتدا ویژگی page@ کامپوننت CheckoutForm را حذف کرده و آن‌را تبدیل به یک کامپوننت معمولی بدون قابلیت مسیریابی کرده‌ایم. سپس آن‌را توسط کامپوننت FormMappingScope در صفحه‌ای دیگر معرفی و محصور می‌کنیم.
اکنون اگر برنامه را اجرا کرده و خروجی HTML آن‌را بررسی کنیم، به فرم زیر خواهیم رسید:
<form method="post">
   <input type="hidden" name="_handler" value="[store-checkout]checkout" />
   <input type="hidden" name="__RequestVerificationToken" value="CfDxxxxx" />
.
.
.
   <button type="submit">Submit</button>
</form>
همانطور که ملاحظه می‌کنید، اینبار مقدار فیلد مخفی handler_ که کار متمایز ساختن این فرم را به عهده دارد و از آن در سمت سرور جهت یافتن کامپوننت متناظری استفاده می‌شود، با حالتی‌که از کامپوننت FormMappingScope استفاده نشده بود، متفاوت است و نام FormMappingScope را در ابتدای خود به همراه دارد تا به این نحو، از تداخل احتمالی نام‌های فرم‌ها جلوگیری شود.

یک نکته: اگر به تگ‌های فرم HTML ای فوق دقت کنید، به همراه یک anti-forgery token نیز هست که کار تولید و مدیریت آن، به صورت خودکار صورت می‌گیرد و میان‌افزاری نیز برای آن طراحی شده که در فایل Program.cs برنامه، به صورت app.UseAntiforgery بکارگرفته شده‌است.


یک نکته: در Blazor 8x SSR می‌توان بجای EditForm، از همان HTML form متداول هم استفاده کرد

اگر بخواهیم بجای استفاده از EditForm، از فرم‌های استاندارد HTML هم در حالت SSR استفاده کنیم، این کار میسر بوده و روش کار به صورت زیر است:
<form method="post" @onsubmit="SaveData" @formname="MyFormName">
    <AntiforgeryToken />

    <InputText @bind-Value="Name" />

    <button>Submit</button>
</form>
در اینجا ذکر دایرکتیوهای onsubmit@ و formname@ را (شبیه به خواص و رویدادگردان‌های مشابهی در EditForm) به همراه ذکر صریح کامپوننت AntiforgeryToken، مشاهده می‌کنید. در حین استفاده از EditForm، نیازی به درج این کامپوننت نیست و به صورت خودکار اضافه می‌شود.


پردازش فرم‌های GET در Blazor 8x

در حالتی‌که از فرم‌های استاندارد HTML ای استفاده می‌شود، ممکن است method فرم، بجای post، حالت get باشد که نتایج آن به صورت کوئری استرینگ در نوار آدرس مرورگر ظاهر می‌شوند؛ مانند جستجوی گوگل که اشخاص می‌توانند کوئری استرینگ و لینک نهایی را به اشتراک بگذارند. روش پردازش یک چنین فرم‌هایی به صورت زیر است:
@page "/"

<form method="GET">
    <input type="text" name="q"/>
    <button type="submit">Search</button>
</form>


@code {
    [SupplyParameterFromQuery(Name="q")]
    public string SearchTerm { get; set; }
    
    protected override async Task OnInitializedAsync()
    {
       // do something with the search term
    }
}
در اینجا از ویژگی SupplyParameterFromQuery برای دریافت کوئری استرینگ استفاده شده و چون نام پارامتر تعریف شده با نام input فرم یکی نیست، این نام به صورت صریحی توسط خاصیت Name آن مشخص شده‌است.


یک ابتکار! تعاملی کردن قسمتی از صفحه بدون فعالسازی کامل Blazor Server و یا Blazor WASM کامل

این دکمه‌ی قرار گرفته‌ی در یک صفحه‌ی SSR را ملاحظه کنید:
<button class="nav-link border-0" @onclick="BeginSignOut">Log out</button>
در اینجا می‌خواهیم، اگر کاربری بر روی آن کلیک کرد، روال رویدادگردان منتسب به onclick اجرا شود. اما ... اگر در این حالت برنامه را اجرا کرده و بر روی دکمه‌ی Log out کلیک کنیم، هیچ اتفاقی رخ نمی‌دهد! یعنی روال رویدادگران BeginSignOut اصلا اجرا نمی‌شود. علت اینجا است که صفحات SSR، در نهایت یک static HTML بیشتر نیستند و فاقد قابلیت‌های تعاملی، مانند واکنش نشان دادن به کلیک بر روی یک دکمه هستند. برای رفع این مشکل یا می‌توان این قسمت از صفحه را کاملا تعاملی کرد که روش انجام آن‌را در قسمت‌های بعدی با جزئیات کاملی بررسی می‌کنیم و یا ... می‌توان این دکمه را داخل یک فرم جدید تعاملی به صورت زیر محصور کرد:
<EditForm Context="ctx" FormName="LogoutForm" method="post" Model="@Foo" OnValidSubmit="BeginSignOut">
     <button type="submit" class="nav-link border-0">Log out</button>
</EditForm>

@code{
    [SupplyParameterFromForm(Name = "LogoutForm")]
    public string? Foo {  get; set; }

    protected override void OnInitialized() => Foo = "";

    async Task BeginSignOut()
    {
        // TODO: SignOutAsync();
        // TODO: NavigateTo("/authentication/logout");
    }
}
در این حالت چون این فرم، از نوع فرم‌های جدید تعاملی است، برای پردازش آن نیازی به اتصال دائم SignalR و یا فعالسازی یک وب‌اسمبلی نیست. پردازش آن بر اساس استاندارد HTTP Post و فرم‌های آن، صورت گرفته و به این ترتیب می‌توان عملکرد onclick@ کاملا تعاملی را با یک فرم تعاملی جدید، شبیه سازی کرد.


یک نکته: می‌توان حالت post-back مانند فرم‌های تعاملی Blazor 8x را تغییر داد.

به همراه ویژگی‌های جدید مرتبط با صفحات SSR، ویژگی هدایت بهبودیافته هم وجود دارد که جزئیات بیشتر آن‌را در قسمت‌های بعدی این سری بررسی می‌کنیم. برای نمونه اگر مثال این قسمت را اجرا کنید، فرم آن به همراه یک post-back مانند به سمت سرور است که کاملا قابل احساس است؛ این رفتار هرچند استاندارد است، اما بی‌شباهت به برنامه‌های MVC ، Razor pages و یا وب‌فرم‌ها نیست و با فرم‌های بی‌صدا و سریع نگارش‌های قبلی Blazor متفاوت است. در Blazor8x می‌توان این نوع ارسال اطلاعات را Ajax ای هم کرد که به آن enhanced navigation می‌گویند. برای اینکار فقط کافی است ویژگی Enhance را به تگ EditForm اضافه کرد و یا ویژگی جدید data-enhance را به تگ‌های فرم‌های استاندارد HTML ای افزود. پس از آن اگر برنامه را اجرا کنیم، دیگر یک post-back استاندارد وب‌فرم‌ها مشاهده نمی‌شود و رفتار این صفحه بسیار سریع، نرم و روان خواهد بود.
<EditForm Model="PlaceModel" method="post" OnValidSubmit="SubmitOrder" FormName="checkout" Enhance>
در اینجا تنها تغییری که حاصل شده، اضافه شدن ویژگی Enhance به المان EditForm است. این ویژگی به صورت پیش‌فرض غیرفعال است که جزئیات بیشتر آن‌را در قسمت‌های بعدی بررسی خواهیم کرد.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: Blazor8x-Server-Normal.zip
مطالب
معرفی Selector های CSS - قسمت 5
41- :checked
برای تگ‌های radio و checkbox استفاده می‌شود و آنهایی را انتخاب می‌کند که گزینه‌ی آن‌ها انتخاب شده است یا شامل ویژگی checked می‌باشند.
<style>
    :checked {
        width: 50px;
        height: 50px;
    }
</style>
<input type="checkbox" checked="checked"/>
<input type="checkbox"/>
<input type="radio" name="test"/>
<input type="radio" name="test" checked="checked"/>
<input type="radio" name="test"/>
در مثال فوق تمامی checkbox هایی که گزینه‌ی آن‌ها انتخاب شده‌اند با ابعاد 50 در 50 نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :checked  3

42- :indeterminate
در HTML نمی‌توان وضعیت indeterminate را برای checkbox ایجاد نمود. اما با جاوا اسکریپت می‌توان این وضعیت را ایجاد کرد. هر checkbox می‌تواند دارای سه وضعیت انتخاب شده، انتخاب نشده و نامعلوم (indeterminate) باشد. این Selector تگهای checkbox ی را انتخاب می‌نماید که وضعیت نامعلوم دارند.
<style>
    :indeterminate {
        width: 50px;
        height: 50px;
    }
</style>
<input type="checkbox" id="chk1"/>
<input type="checkbox" id="chk2" checked="checked"/>
<input type="checkbox" id="chk3"/>
<script>
    document.getElementById("chk1").indeterminate=true;
</script>
در مثال فوق chk1 به صورت indeterminate انتخاب شده است که با ابعاد 50 در 50 نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :indeterminate  4

43- :default
برای تگ‌های radio و checkbox، آنهایی را انتخاب می‌کند که در زمان بارگزاری فرم و بصورت پیش فرض انتخاب شده‌اند. برای دکمه‌ی submit ایی نیز استفاده می‌شود که در حال حاضر در فرم فعال می‌باشد. پشتیبانی از این Selector در مرورگرها متفاوت می‌باشد؛ بنابراین در چند مرورگر کارآیی آن را بررسی کنید.
<style>
    :default {
        width: 50px;
        height: 50px;
        background: lime;
    }
</style>
<form>
    <input type="checkbox" checked="checked" />
    <input type="checkbox" />
    <input type="radio" name="radio1" />
    <input type="radio" name="radio1" checked="checked" />
    <input type="radio" name="radio1" />
    <input type="submit" value="Default" />
    <input type="submit" value="Submit 2" />
</form>
در مثال فوق checkbox و radio هایی که در لود اولیه فرم دارای ویژگی checked می‌باشند به همراه دکمه‌ی submit با عنوان Default با ابعاد 50 در 50 و رنگ پس زمینه‌ی لیمویی نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :default  4

44- :root
المنت ریشه‌ی یک صفحه را انتخاب می‌نماید. معمولا تگ html به عنوان المنت ریشه انتخاب می‌شود.
<style>
    :root {
        background: khaki;
    }
</style>
در مثال فوق رنگ پس زمینه به رنگ خاکی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :root  3

45- :after
جهت درج محتوا بعد از تگ استفاده می‌شود.
<style>
    form:after {
        content: "[ * : Required]";
        color: red;            
    }
        input+span:after {
            content: ' * ';
            color: red;
        }
</style>
<form>
    <div><input type="text" /><span></span></div>
    <div><input type="password" /><span></span></div>
    <div><input type="email" /><span></span></div>
</form>
در مثال فوق بعد از تمامی span‌های قرار گرفته در کنار تگ‌های input، یک کاراکتر * را به رنگ قرمز نمایش میدهد. بعد از فرم نیز رشته‌ی [ * : Required] را به رنگ قرمز نمایش می‌دهد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1 7.0  9.0 3.5  4.0 :after 2

46- :before
جهت درج محتوا قبل از تگ استفاده می‌شود.
<style>
    form:before {
        content: "[ * : Required]";
        color: red;
    }

    input + span:before {
        content: ' * ';
        color: red;
    }
</style>
<form>
    <div><input type="text" /><span></span></div>
    <div><input type="password" /><span></span></div>
    <div><input type="email" /><span></span></div>
</form>
در مثال فوق قبل از تمامی span‌های قرار گرفته در کنار تگ‌های input، یک کاراکتر * را به رنگ قرمز نمایش می‌دهد. قبل از فرم نیز رشته‌ی [ * : Required] را به رنگ قرمز نمایش می‌دهد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1 7.0  9.0 3.5  4.0 :before 2

47- ::selection
محتوایی را انتخاب می‌کند که در حال حاضر در صفحه انتخاب یا Select شده‌است.
<style>
    ::selection {
        background: navy;
        color: orange;
    }
</style>
<p>
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 
    Maecenas porttitor congue massa. Fusce posuere, 
    magna sed pulvinar ultricies, purus lectus malesuada libero, 
    sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.
</p>
در مثال فوق با انتخاب متن توسط ماوس، رنگ زمینه‌ی متن انتخاب شده به صورت سرمه ای و رنگ متن آن نارنجی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1  9.6  9.0 2.0
-moz-
 4.0 ::selection  در CSS 3 معرفی و سپس حذف شد ولی امکان بازگشت مجدد وجود دارد

48- :not(S1,S2)
تگ هایی را انتخاب می‌کند که شامل Selector‌های S1 یا S2 یا ... یا Sn نباشند.
<style>
    :not([readonly]) {
        background: yellow;
    }
</style>
<input type="text"/>
<input type="text" readonly="readonly"/>
<input type="text"/>
<input type="text" readonly="readonly" />
در مثال فوق پس زمینه‌ی تگهای input که ویژگی readonly ندارند به رنگ زرد نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :not(S1,S2)  3

49- :matches(S1,S2)
تگ هایی را انتخاب می‌کند که شامل Selector‌های S1 و/یا S2 و/یا ... و/یا Sn باشند. 
<style>
    :matches([readonly]) {
        background: yellow;
    }
</style>
<input type="text" />
<input type="text" readonly="readonly" />
<input type="text" />
<input type="text" readonly="readonly" />
در مثال فوق پس زمینه‌ی تگهای input که ویژگی readonly دارند به رنگ زرد نمایش می‌یابند. 
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :matches(S1,S2)  4

50- :has(S1,S2)
تگ هایی را انتخاب می‌کند که در ارتباط خاصی با Selector های S1 و/یا S2 و/یا ... و/یا Sn می‌باشند. 
<style>
    :has(>span) {
        color: red;
    }
    :has(+div) {
        color: blue;
    }
</style>
<div>
    <p>
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
        Maecenas <span>porttitor</span> congue massa. Fusce posuere,
        magna sed pulvinar ultricies, purus lectus malesuada libero,
        sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.
    </p>
    <h1>Header 1</h1>
    <div>
            
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
        Maecenas porttitor congue massa. Fusce posuere,
        magna sed pulvinar ultricies, purus lectus malesuada libero,
        sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est.            
    </div>
در مثال فوق متن موجود در تگ p که دارای فرزند مستقیم span می‌باشد، با توجه به Selector ی که بصورت :has(>span) تعریف شده است، به رنگ قرمز نمایش می‌یابد. همچنین Header 1 که دارای تگ div هم تراز و دقیقا بعد از h1 است، با توجه به Selector ی که بصورت  :has(+div) تعریف شده است، به رنگ آبی نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No :has(S1,S2)  4 
مطالب
استخراج تمام XPathهای یک محتوای HTMLایی به کمک کتابخانه HtmlAgilityPack
اولین قدم کار کردن با کتابخانه قدرتمند HtmlAgilityPack، داشتن XPath معتبر و متناظر با یک گره خاص می‌باشد. هرچند به ظاهر تعدادی از مرورگرها با کمک افزونه‌های خود امکان استخراج این XPathها را فراهم کرده‌اند اما ... عموما این مقادیر ارائه شده، نادرست هستند و بر روی محتوای HTML اصلی یک سایت قابل اجرا نیستند؛ علت هم به نرمال سازی‌های انجام شده بر روی محتوای یک سایت، توسط موتور مرورگر بر می‌گردد.
خود کتابخانه HtmlAgilityPack به ازای هر HtmlNode ایی که ارائه می‌دهد، خاصیت XPath معتبری را نیز به همراه دارد. در ادامه قصد داریم از این امکان توکار استفاده کرده و کلیه XPath‌های یک محتوای HTML ایی را استخراج کنیم.


پردازش تگ‌های تو در توی یک HTML به کمک کتابخانه HtmlAgilityPack

using System;
using System.Linq;
using System.Net;
using System.Text;
using HtmlAgilityPack;

namespace HapTests
{
    public class HtmlReader
    {
        public Action<string> ParseError { set; get; }

        public Func<HtmlNode, bool> ParserHtmlNode { set; get; }

        public void StartParsingHtml(Uri url)
        {
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                client.Headers.Add("user-agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
                StartParsingHtml(client.DownloadString(url));
            }
        }

        public void StartParsingHtml(string htmlContent)
        {
            if (string.IsNullOrWhiteSpace(htmlContent))
                throw new ArgumentNullException("content");

            var doc = new HtmlDocument
            {
                OptionCheckSyntax = true,
                OptionFixNestedTags = true,
                OptionAutoCloseOnEnd = true,
                OptionDefaultStreamEncoding = Encoding.UTF8
            };
            doc.LoadHtml(htmlContent);

            if (doc.ParseErrors != null && doc.ParseErrors.Any())
            {
                foreach (var error in doc.ParseErrors)
                {
                    if (ParseError != null)
                        ParseError(error.Code + " - " + error.Reason);
                }
            }

            if (!doc.DocumentNode.HasChildNodes)
                return;

            handleChildren(doc.DocumentNode.ChildNodes);
        }

        private void handleChildren(HtmlNodeCollection nodes)
        {
            foreach (var itm in nodes)
            {
                if (itm.Name.ToLower().Equals("html"))
                {
                    if (itm.Element("body") != null)
                        handleChildren(itm.Element("body").ChildNodes);
                }
                else
                    handleHtmlNode(itm);
            }
        }

        private void parserChildNodes(HtmlNode content)
        {
            foreach (var item in content.ChildNodes)
            {
                handleHtmlNode(item);
            }
        }

        private void handleHtmlNode(HtmlNode htmNode)
        {
            switch (htmNode.Name.ToLower())
            {
                case "html":
                case "body":
                    handleChildren(htmNode.ChildNodes);
                    break;

                default:
                    if (ParserHtmlNode == null)
                        throw new ArgumentNullException("ParserHtmlNode");

                    if (ParserHtmlNode(htmNode))
                        parserChildNodes(htmNode);

                    break;
            }
        }
    }
}
در اینجا کدهایی را ملاحظه می‌کنید که علاوه بر ارائه تنظیمات اولیه HtmlAgilityPack (خصوصا با درنظر گرفتن مباحث ورودی یونیکد)، به صورت بازگشتی (با توجه به اینکه الزاما مسیر یا Node خاصی مدنظر نیست)، کلیه گره‌های یک HTML را بررسی و ارائه می‌دهند.
این کد برای نوشتن مبدل‌های HTML به XYZ بسیار مناسب است. برای مثال اگر بخواهید یک مبدل HTML به PDF را تهیه کنید، کدهای ابتدایی آن همین موارد است:
new HtmlReader
{
    ParseError = error => Console.WriteLine(error),
    ParserHtmlNode = htmlNode =>
    {
        //switch(htmlNode.Name) { }
        return true; //it's a nested node.
    }
}.StartParsingHtml(html);
نمونه‌ای از نحوه استفاده از کدهای کلاس HtmlReader را ملاحظه می‌کنید.
در اینجا html، محتوای HTMLایی در حال بررسی است. ParserHtmlNode یک callback است. هر زمانیکه به یک گره HTML برخورد، آن‌را در اختیار شما قرار می‌دهد. در ادامه فرصت خواهید داشت تا برای نمونه یک swicth را تهیه کرده و مثلا به ازای تگ hr یک خط رسم کنید، به ازای تگ br یک سطر جدید را درنظر بگیرید و الی آخر. اگر خروجی این Func را true درنظر بگیرید، فرض بر این خواهد بود که گره جاری تو در تو است (حالت دنیای واقعی)؛ در غیراینصورت، یک سطح این گره، بیشتر بررسی نخواهد شد.
در این کلاس، ParseError نیز یک callback است و اگر کتابخانه HtmlAgilityPack، در حین آنالیز کدهای HTML دریافتی به خطایی برخورد، آن‌را گزارش خواهد داد.
در کلاس فوق، دو حالت برای متد StartParsingHtml در نظر گرفته شده است. در حالت اول، یک Uri یا آدرس اینترنتی دریافت و سپس آنالیز می‌گردد. در حالت دوم، فرض بر این است که محتوای کدهای HTML مدنظر به هر نحوی پیشتر تهیه شده و به صورت string موجود است.


استخراج کلیه XPathها از یک فایل HTML به کمک کتابخانه HtmlAgilityPack

اکنون که یک HTML Parser عمومی را تهیه کرده‌ایم، استخراج XPathها توسط آن کار ساده‌ای خواهد بود. یک مثال کامل را در این زمینه در ادامه ملاحظه می‌کنید:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using HtmlAgilityPack;

namespace HapTests
{
    class Program
    {
        static void Main(string[] args)
        {
            var html = 
                @"<table width='750' border='0' style='font-size: 10pt; width: 736px' class='boxcar2 gerd'>
            <tbody><tr>
            <td height='70' colspan='4' class='boxcart1 gerd'>
            <iframe width='718' scrolling='no'>
            </iframe></td>
            </tr>
            <tr>
            <td height='70' colspan='4' class='boxcart1 gerd'>
    </td>
            </tr>
            <tr>
            <td width='193' height='36' class='boxcart2 gerd'>
            <a target='_self' href='Curr.cbi.2.php'>نرخ ارز مبادله ای بانک مرکزی</a></td>
            <td width='181' height='36' class='boxcart2 gerd'>
            <a target='_self' href='Curr.cbi.php'>نرخ ارز مرجع بانک مرکزی</a></td>
            <td width='149' height='36' class='boxcart2 gerd'>
            <a target='_self' href='curv.htm'>نمودار قیمت طلا</a></td>
            <td width='199' height='36' class='boxcart2 gerd'>
            <a target='_self' href='index.php'>قیمت طلا و سکه در بازار ایران</a></td>
            </tr>
            <tr>
            <td height='48' colspan='4' class='boxcart1 gerd'>
            <p dir='rtl'><span style='font-size: 13pt;'>تابلو آنلاین قیمت جهانی طلا و نقره ( دلار 
            )</span></p></td>
            </tr>
            <tr>
            <td height='57' colspan='2' class='boxcart1 gerd'>قیمت لحظه ای هر انس 
            نقره در بازارهای جهانی<br>
            <span style='font-size: 9pt;'>
            </span></td>
            <td height='57' colspan='2' class='boxcart1 gerd'>قیمت لحظه ای هر انس 
            طلا در بازارهای جهانی<br>
            <span style='font-size: 9pt;'>
            </span></td>
            </tr>
            <tr>
            <td height='48' colspan='4' class='boxcart1 gerd'>
            <p dir='rtl'><span style='font-size: 13pt'>تابلو آنلاین قیمت طلا ، سکه 
            و نقره در بازار ایران ( ریال )</span></p>
            </td>
            </tr>
            <tr>
            <td style='direction: rtl; font-size: 8pt' colspan='4'><div align='center'>
                            <table id='gold_tbl'><tbody><tr><th>قیمت طلا</th><th>قیمت زنده</th><th>تغییر</th>
                            <th>کمترین</th><th>بیشترین</th><th>زمان</th></tr><tr><td>انس طلا <sup>دلار</sup></td>
                            <td class='s0_1'>1,375.90</td><td class='c0_1 neg'>(-0.34%) -4.70</td>
                            <td class='l0_1'>1,374.90</td><td class='h0_1'>1,380.80</td><td class='z0_1 fa'>17:53</td>
                            </tr><tr><td>مثقال طلا</td><td class='s3_2'>5,290,000</td>
                            <td class='c3_2 pos'>(1.63%) 85,000</td><td class='l3_2'>5,200,000</td><td class='h3_2'>5,320,000</td><td class='z3_2 fa'>17:50</td></tr><tr><td>گرم طلای 18</td>
                            <td class='s3_3'>1,221,200</td><td class='c3_3 pos'>(1.63%) 19,600</td><td class='l3_3'>1,200,400</td><td class='h3_3'>1,228,100</td><td class='z3_3 fa'>17:50</td>
                            </tr><tr><td>انس نقره <sup>دلار</sup></td><td class='s0_5'>21.83</td><td class='c0_5'>(0.00%) 0.00</td><td class='l0_5'>21.67</td><td class='h0_5'>21.96</td>
                            <td class='z0_5 fa'>17:53</td></tr></tbody></table><br><table id='coin_tbl'><tbody><tr><th>سکه</th><th>قیمت زنده</th><th>تغییر</th><th>کمترین</th>
                            <th>بیشترین</th><th>ارزش طلا</th><th>زمان</th></tr><tr><td>بهار آزادی</td><td class='s3_10'>12,650,000</td><td class='c3_10 pos'>(2.68%) 330,000</td>
                            <td class='l3_10'>12,320,000</td><td class='h3_10'>12,650,000</td><td class='z4_10'>11,918,400</td><td class='z3_10 fa'>16:07</td></tr><tr><td>امامی</td>
                            <td class='s3_11'>12,960,000</td><td class='c3_11 pos'>(2.61%) 330,000</td><td class='l3_11'>12,630,000</td><td class='h3_11'>13,050,000</td><td class='z4_11'>11,918,400</td>
                            <td class='z3_11 fa'>17:43</td></tr><tr><td>نیم</td><td class='s3_12'>6,880,000</td><td class='c3_12 pos'>(2.69%) 180,000</td><td class='l3_12'>6,700,000</td>
                            <td class='h3_12'>6,900,000</td><td class='z4_12'>5,959,200</td><td class='z3_12 fa'>16:08</td></tr><tr><td>ربع</td><td class='s3_13'>4,250,000</td><td class='c3_13 pos'>(2.41%) 100,000</td>
                            <td class='l3_13'>4,150,000</td><td class='h3_13'>4,300,000</td><td class='z4_13'>2,978,100</td><td class='z3_13 fa'>17:42</td></tr><tr><td>گرمی</td><td class='s3_14'>2,940,000</td>   
                            <td class='c3_14 pos'>(3.16%) 90,000</td><td class='l3_14'>2,850,000</td><td class='h3_14'>2,940,000</td><td class='z4_14'>1,465,400</td><td class='z3_14 fa'>17:40</td></tr></tbody></table></div></td>
            </tr>
            </tbody></table>
                ";

            extractXPath(html);
            test(html);
        }

        /// <summary>
        /// Converts /#comment[1] to /comment()[1] 
        /// or /#text[1] to /text()[1]
        /// </summary>
        private static string GetValidXPath(string xpath)
        {
            var index = xpath.LastIndexOf("/");
            var lastPath = xpath.Substring(index);

            if (lastPath.Contains("#"))
            {
                xpath = xpath.Substring(0, index);
                lastPath = lastPath.Replace("#", "");
                lastPath = lastPath.Replace("[", "()[");
                xpath = xpath + lastPath;
            }

            return xpath;
        }

        private static void extractXPath(string html)
        {
            var sb = new StringBuilder();
            new HtmlReader
            {
                ParseError = error => Console.WriteLine(error),
                ParserHtmlNode = htmlNode =>
                {
                    if (htmlNode is HtmlTextNode)
                    {
                        sb.AppendLine("Text NodeName: " + htmlNode.Name.Trim());
                        sb.AppendLine("InnerText: " + htmlNode.InnerText.Trim());
                    }
                    else
                    {
                        sb.AppendLine("NodeName: " + htmlNode.Name.Trim());
                        var nodeText = new StringBuilder();
                        for (int i = 0; (i < htmlNode.OuterHtml.Length && htmlNode.OuterHtml[i] != '>'); i++)
                            nodeText.Append(htmlNode.OuterHtml[i]);

                        nodeText.Append(">");

                        sb.AppendLine("Node Start: " + nodeText.ToString());
                    }

                    sb.AppendLine("XPath: " + GetValidXPath(htmlNode.XPath.Trim()));
                    sb.AppendLine(Environment.NewLine);

                    return true; //it's a nested node.
                }
            }.StartParsingHtml(html);

            File.WriteAllText("xpath.txt", sb.ToString());
            Process.Start("xpath.txt");
        }

        private static void test(string html)
        {
            var doc = new HtmlDocument
            {
                OptionCheckSyntax = true,
                OptionFixNestedTags = true,
                OptionAutoCloseOnEnd = true,
                OptionDefaultStreamEncoding = Encoding.UTF8
            };
            doc.LoadHtml(html);
            var node = doc.DocumentNode.SelectSingleNode("/table[1]/tbody[1]/tr[7]/td[1]/div[1]/table[2]/tbody[1]/tr[6]/td[7]/text()[1]");
            Console.WriteLine(node.InnerText);
        }
    }
}
در این مثال html مقداری است که از یک سایت عمومی دریافت شده است.
سپس نمونه‌ای دیگر از نحوه استفاده از کلاس HtmlReader قسمت قبل را در ادامه، در متد extractXPath ملاحظه می‌کنید. در اینجا کلاس HtmlReader در یک عملیات بازگشتی، کلیه گره‌های تو در توی HTML مورد نظر را آنالیز کرده و توسط callback ایی به نام ParserHtmlNode در اختیار ما قرار می‌دهد. اکنون که این htmlNode را داریم، خاصیت XPath آن دقیقا مقداری است که به دنبالش هستیم.
در اینجا چند نکته حائز اهمیت هستند:
- با بررسی HtmlTextNode، به نودهایی خواهیم رسید که دارای مقدار متنی هستند. در غیراینصورت این گره، خود ابتدای یک سری گره تو در توی دیگر است.
- XPath بازگشتی توسط کتابخانه HtmlAgilityPack نیاز به کمی تمیز سازی دارد. اینکار در متد GetValidXPath انجام شده است.
- در متد test انتهایی، نمونه‌ای از نحوه استفاده از XPathهای استخراجی را ملاحظه می‌کنید.
Text NodeName: #text
InnerText: 17:40
XPath: /table[1]/tbody[1]/tr[7]/td[1]/div[1]/table[2]/tbody[1]/tr[6]/td[7]/text()[1]
برای نمونه سه سطر فوق، یکی از مداخل فایل نهایی تولیدی مثال جاری است. اکنون که XPath را داریم، استفاده از آن جهت استخراج مقدار InnerText مدنظر، ساده خواهد بود.
نظرات مطالب
طراحی گردش کاری با استفاده از State machines - قسمت اول
jsPlumb یک سری callback function داره که زمان اتصال نودها و یا زمان قطع اتصالات فراخوانی خواهند شد:
jsPlumb.bind("jsPlumbConnection", function(connectionInfo) {
  // update your data model here.
});

jsPlumb.bind("jsPlumbConnectionDetached", function(connectionInfo) { 
  // update your data model here. 
});
در اینجا شما فرصت خواهید داشت اطلاعات مدل مورد نظر را به روز کنید.
connectionInfo دریافتی یک شیء جاوا اسکریپتی است شامل connection, source, sourceEndpoint, sourceId, target, targetEndpoint, targetId 

مطالب
یک نکته درباره Angular routeProvider
بعد از مطالعه پست‌های ^ و ^    نکته ای به ذهنم رسید که بیان آن از بنده و مطالعه آن توسط شما خالی از لطف نیست. اگر مثال‌های پیاده سازی شده در پست‌های ^ و ^ را با AngularJs نسخه 1.2 اجرا نمایید به طور حتم با خطا روبرو می‌شوید و نتیجه مورد نظر حاصل نمی‌شود. در این پست نیز توسط یکی از دوستان اشاره ای به این مطلب شد.
دلیل خطا این است که از نسخه 1.2 به بعد در Angular سیستم مسیر یابی به این شکل امکان پذیر نیست و بخش مسیریابی به یک فایل دیگر به نام angular-route.js منتقل شده است. در نتیجه اگر به سبک نسخه‌های قبلی Angular از سیستم مسیریابی استفاده نمایید با خطا مواجه خواهید شد و خطای مورد نظر هم مربوط به عدم توانایی در تزریق وابستگی routeProvider$ به ماژول مورد نظر است. حال راه حل چیست؟
کافیست در هنگام تعریف ماژول، ngRoute را به عنوان وابستگی ماژول تعیین نمایید. و از طرفی فایل اسکریپتی angular-route.js را بعد از angular.js فراخوانی کنید.
بررسی مثال:
کد‌های زیر مربوط به مثال‌های پست قبلی می‌باشد که شرح کامل آن در این پست است:
var myFirstRoute = angular.module('myFirstRoute', []);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
 
 
myFirstRoute.controller('ShowPage1Controller', function($scope) {
    $scope.message = 'Content of page-one.html';
});
  
  
myFirstRoute.controller('ShowPage2Controller', function($scope) {
    $scope.message = 'Content of page-two.html';
});
برای هماهنگ کردن مثال بالا با نسخه 1.2 باید به روش زیر عمل نمود:
var myFirstRoute = angular.module('myFirstRoute',['ngRoute']);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
تنها تغییر، مشخص کردن ngRoute به عنوان وابستگی ماژول myFirstRoute است(خط اول). نکته دیگر لود فایل angular-route.js قبل از فراخوانی فایل بالا و بعد از فراخوانی فایل angular.js است:
<body ng-app="app">
   
    <div>
        <div>
        <div>
            <ul>
                <li><a href="#pageOne"> Show page one </a></li>
                <li><a href="#pageTwo"> Show page two </a></li>
            </ul>
        </div>
        <div>
            <div ng-view></div>
        </div>
        </div>
    </div>
   
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
     <script src="angular-route.js"></script>
    <script src="app.js"></script>
   
  </body>

مطالب
C# 8.0 - Pattern Matching
در نگارش‌های پیشین #C، بهبودهایی در زمینه‌ی Pattern matching وجود داشتند. در نگارش 8 نیز این بهبودها ادامه پیدا کرده‌اند که نتیجه‌ی آن به‌وجود آمدن روش جدیدی برای نوشتن عبارات switch است.


معرفی روش جدید نوشتن عبارات switch در C#8.0

فرض کنید یک enum که معرف تعدادی رنگ است را تعریف کرده‌ایم:
    public enum Rainbow
    {
        Red,
        Orange,
        Yellow,
        Green,
        Blue,
        Indigo,
        Violet
    }
همچنین کلاسی را نیز جهت تشکیل اشیاء رنگ مبتنی بر RGB تدارک دیده‌ایم:
    class RGBColor
    {
        internal byte Red { get; }
        internal byte Green { get; }
        internal byte Blue { get; }

        internal RGBColor(byte red, byte green, byte blue)
        {
            Red = red;
            Green = green;
            Blue = blue;
        }

        public override string ToString() => $"rgb({Red}, {Green}, {Blue})";
    }
اکنون هدف ما این است که اگر یکی از اعضای این enum را انتخاب کردیم، بتوانیم معادل رنگ RGB آن‌را نیز داشته باشیم. برای این منظور می‌توان switch ساده‌ی زیر را تشکیل داد:
        internal static RGBColor FromRainbow(Rainbow rainbowBolor)
        {
            switch (rainbowBolor)
            {
                case Rainbow.Red:
                    return new RGBColor(0xFF, 0x00, 0x00);
                case Rainbow.Orange:
                    return new RGBColor(0xFF, 0x7F, 0x00);
                case Rainbow.Yellow:
                    return new RGBColor(0xFF, 0xFF, 0x00);
                case Rainbow.Green:
                    return new RGBColor(0x00, 0xFF, 0x00);
                case Rainbow.Blue:
                    return new RGBColor(0x00, 0x00, 0xFF);
                case Rainbow.Indigo:
                    return new RGBColor(0x4B, 0x00, 0x82);
                case Rainbow.Violet:
                    return new RGBColor(0x94, 0x00, 0xD3);
                default:
                    throw new ArgumentException(message: "invalid enum value", paramName: nameof(rainbowBolor));
            };
        }
این کاری است که تا پیش از C# 8.0 به صورت متداولی انجام می‌شود. اکنون در C# 8.0 می‌توان عبارت switch فوق را به صورت زیر خلاصه کرد:
        internal static RGBColor TasteTheRainbow(Rainbow rainbowColor) =>
            rainbowColor switch
        {
            Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
            Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
            Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
            Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),
            Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),
            Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
            Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
            _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(rainbowColor)),
        };
- در این روش جدید، بجای اینکه با ذکر switch و سپس، مقداری/نوعی شروع شود، ابتدا با نوع شروع می‌شود و سپس واژه‌ی کلیدی switch ذکر خواهد شد.
- در ادامه تمام caseها حذف می‌شوند و بجای آن‌ها صرفا مقادیر مدنظر باقی می‌ماند. در اینجا <= به صورت expressed as خوانده می‌شود.
- caseهای مختلف با کاما از هم جدا می‌شوند.
- همچنین در سطر آخر آن نیز از یک discard استفاده شده‌است که معادل همان حالت default یا حالتی است که هیچ تطابقی صورت نگرفته باشد.
- به علاوه اگر دقت کنید، نتیجه‌ی نهایی این switch جدید، به صورت یک مقدار، توسط متد TasteTheRainbow، بازگشت داده شده‌است. بنابراین نوشتن یک چنین عباراتی در C# 8.0، مجاز است:
var operation = "+";
int a = 1, b = 2;
var result = operation switch
{
   "+" => a + b,
   "-" => a - b,
   "/" => a / b,
     _ => throw new NotSupportedException()
};


معرفی Property Patterns در C# 8.0

کلاس زیر را درنظر بگیرید که از تعدادی خاصیت عمومی تشکیل شده‌است:
    class Address
    {
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
        public string CountryRegion { get; set; }
    }
اکنون فرض کنید که می‌خواهیم مالیات فروش را بر اساس آدرس و محل آن، محاسبه کنیم. در C# 8.0 با معرفی قابلیت الگوهای خواص، می‌توان بر روی آدرس، یک switch را تشکیل داد و سپس تک تک خواص آن‌را ارزیابی کرد:
    static class PropertyPatterns
    {
        internal static decimal ComputeSalesTax(
            Address location,
            decimal salePrice) =>
            location switch
        {
            { State: "Fars" } => salePrice * 0.06m,
            { State: "Tehran", City: "Tehran" } => salePrice * 0.056m,

            // Other cases removed for brevity...
            _ => 0M
        };
    }
در اینجا، سمت چپ هر case، داخل یک {} قرار می‌گیرد و در آن می‌توان مقادیر چندین خاصیت شیء location دریافتی را بررسی کرد. برای نمونه در سطر دوم آن، روش ارزیابی بیش از یک خاصیت را نیز مشاهده می‌کنید که روش ذکر آن شبیه به تعریف شیء‌های JSON است. در آخر نیز توسط یک discard، حالت default ذکر شده‌است.


معرفی Tuple Patterns در C# 8.0

در switch‌های C# 8.0، می‌توان از tuples نیز برای تشکیل قسمت case و همچنین مقداری که قرار است switch بر روی آن صورت گیرد، استفاده کرد:
    static class TuplePatterns
    {
        internal static string RockPaperScissors(
            string first,
            string second)
            => (first, second) switch
        {
            ("rock", "paper") => "Rock is covered by Paper. Paper wins!",
            ("rock", "scissors") => "Rock breaks Scissors. Rock wins!",
            ("paper", "rock") => "Paper covers Rock. Paper wins!",
            ("paper", "scissors") => "Paper is cut by Scissors. Scissors wins!",
            ("scissors", "rock") => "Scissors is broken by Rock. Rock wins!",
            ("scissors", "paper") => "Scissors cuts Paper. Scissors wins!",
            (_, _) => "tie"
        };
    }
در اینجا بر روی tuple ای که به صورت (first, second) تعریف شده، یک switch تعریف می‌شود. سپس برای نمونه 6 حالت مختلف برای آن پیش‌بینی شده و یک حالت default که آن نیز توسط discards معرفی می‌شود.


بهبودهای Pattern Matching بر روی اشیاء در C# 8.0

فرض کنید شیء پایه‌ی Shape را تعریف و بر اساس آن دو شیء جدید دایره و مستطیل را ایجاد کرده‌ایم:
    class Shape
    {
        protected internal double Height { get; }
        protected internal double Length { get; }

        protected Shape(double height = 0, double length = 0)
        {
            Height = height;
            Length = length;
        }
    }

    class Circle : Shape
    {
        internal double Radius => Height / 2;
        internal double Diameter => Radius * 2;
        internal double Circumference => 2 * Math.PI * Radius;

        internal Circle(double height = 10, double length = 10)
            : base(height, length) { }
    }

    class Rectangle : Shape
    {
        internal bool IsSquare => Height == Length;

        internal Rectangle(double height = 10, double length = 10)
            : base(height, length) { }
    }
امکان Pattern Matching بر روی اشیاء، در C# 7x نیز وجود دارد؛ اما در C# 8.0 می‌توان از روش جدید بیان عبارت switch آن به صورت زیر نیز در این حالت استفاده کرد:
    static class ObjectPatterns
    {
        internal static string ShapeDetails(this Shape shape)
            => shape switch
        {
            Circle c => $"circle with (C): {c.Circumference}",
            Rectangle s when s.IsSquare => $"L:{s.Length} H:{s.Height}, square",
            Rectangle r => $"L:{r.Length} H:{r.Height}, rectangle",
            _ => "Unknown shape!" // Discard
        };
    }
در اینجا یک شیء، به متد ShapeDetails ارسال شده و سپس جزئیاتی از آن دریافت می‌شود. مطابق روش C# 8.0، در اینجا نیز کار با ذکر نوع و سپس عبارت switch، شروع می‌شود. در ادامه روش بررسی نوع‌ها را در caseهای این سوئیچ ملاحظه می‌کنید. اگر در قسمت case آن Circle c ذکر شد، یعنی نوع shape از نوع دایره بوده و همچنین در همینجا می‌توان متغیر c را بر این اساس تعریف کرد و از آن استفاده نمود و یا می‌توان به کمک واژه‌ی کلیدی when، بر روی این متغیری که جدید تعریف شده، شرطی را نیز بررسی کرد. حالت default آن هم توسط discards معرفی می‌شود.


معرفی Positional Patterns در C# 8.0

در اینجا یک Point را داریم که می‌خواهیم بر اساس آن یک Quadrant را استخراج کنیم:
    class Point
    {
        public int X { get; }

        public int Y { get; }

        public Point(int x, int y) => (X, Y) = (x, y);

        public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
    }

    enum Quadrant
    {
        Unknown,
        Origin,
        One,
        Two,
        Three,
        Four,
        OnBorder
    }
برای این منظور می‌توان از الگوهای موقعیتی C# 8.0 استفاده کرد:
    static class PositionalPatterns
    {
        internal static Quadrant AsQuadrant(Point point) => point switch
        {
            (0, 0) => Quadrant.Origin,
            var (x, y) when x > 0 && y > 0 => Quadrant.One,
            var (x, y) when x < 0 && y > 0 => Quadrant.Two,
            var (x, y) when x < 0 && y < 0 => Quadrant.Three,
            var (x, y) when x > 0 && y < 0 => Quadrant.Four,
            (_, _) => Quadrant.OnBorder, // Either are 0, but not both
            _ => Quadrant.Unknown
        };
    }
اگر به کلاس Point دقت کنید، یک قسمت Deconstruct هم دارد. به همین جهت در قسمت‌های case این switch، زمانیکه برای مثال (0,0) ذکر می‌شود (که یک tuple literal است)، به صورت خودکار یک شیء Point متناظر را با مقادیر X و Y آن، تشکیل می‌دهد. همچنین روش‌های مختلف مقایسه‌ی مقادیر x و y این tuple را نیز در caseهای مختلف آن مشاهده می‌کنید.
در اینجا اگر دقت کنید و case مخصوص discards معرفی شده‌است. اولی برای حالت‌هایی است که هیچکدام از شرایط پیش از آن را برآورده نمی‌کند، مانند حالت (1,0)، در غیراینصورت سطر بعد از آن بازگشت داده می‌شود.
اشتراک‌ها
کتاب رایگان Support Vector Machines Succinctly

Support Vector Machines (SVMs) are some of the most performant off-the-shelf, supervised machine-learning algorithms. In Support Vector Machines Succinctly, author Alexandre Kowalczyk guides readers through the building blocks of SVMs, from basic concepts to crucial problem-solving algorithms. He also includes numerous code examples and a lengthy bibliography for further study. By the end of the book, SVMs should be an important tool in the reader’s machine-learning toolbox.

Table of Contents
  1. Prerequisites
  2. The Perceptron
  3. The SVM Optimization Problem
  4. Solving the Optimization Problem
  5. Soft Margin SVM
  6. Kernels
  7. The SMO Algorithm
  8. Multi-Class SVMs
  9. Conclusion
  10. Appendix A: Datasets
  11. Appendix B: The SMO Algorithm
کتاب رایگان Support Vector Machines Succinctly
مطالب
معرفی فریم ورک Blueprint CSS
احتمالا با عباراتی مانند طراحی table less و مزیت‌های طراحی با CSS، همانند سرعت بالاتر بارگذاری سایت در مقایسه با نمایش یک جدول که نیازمند دریافت تمام جزئیات آن و سپس رندر نهایی اطلاعات آن توسط مرورگر است، بارها برخورد داشته‌اید. اما ... آیا یکبار سعی کرده‌اید که به صورت دستی همان کارهایی را که پیشتر با HTML table انجام می‌دادید، اینبار توسط CSS پیاده سازی کنید؟
در اکثر اوقات نتیجه کار مایوس کننده، بسیار سخت و نگهداری آن در طول زمان بسیار مشکل خواهد بود؛ به علاوه سازگاری با مرورگرهای مختلف و نکات ریز هر کدام را نیز لحاظ کنید. به همین جهت تعدادی فریم ورک CSS برای شبیه سازی گرید و جدول تهیه شده‌اند که کار طراحی table less را بسیار ساده و لذت بخش کرده‌اند. یکی از این موارد، فریم ورک Blueprint CSS نام دارد و در ادامه نحوه استفاده از آن‌را مرور خواهیم کرد. این مرور هم مستقل است از فناوری سمت سرور مورد استفاده و صرفا مباحث html و CSS آن بررسی خواهند شد.


دریافت Blueprint CSS

این فریم ورک سورس باز را از مخزن کدهای آن در GitHub می‌توانید دریافت کنید: (^)
البته نگران حجم نزدیک به 4 مگابایتی بسته دریافتی آن نباشید؛ زیرا نهایتا با سه فایل CSS از آن بیشتر کاری نداریم و مابقی مثال‌های آن هستند.
پس از دریافت آن، یک پوشه را به نام blueprint ایجاد کرده و سه فایل ie.css ،print.css و screen.css را در آن قرار دهید.
به علاوه داخل این پوشه، یک پوشه جدید دیگر را به نام src ایجاد کرده و فایل grid.png موجود در این بسته را نیز در آن کپی کنید.


ساختار ابتدایی یک صفحه مبتنی بر Blueprint CSS

پس از ایجاد پوشه blueprint و src به نحوی که توضیح داده شد، ابتدایی‌ترین ساختار یک صفحه تشکیل شده با blueprint css به نحو زیر است:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Blueprint test page</title>

    <!-- Framework CSS -->
    <link rel="stylesheet" href="blueprint/screen.css" type="text/css" media="screen, projection">
    <link rel="stylesheet" href="blueprint/print.css" type="text/css" media="print">
    <!--[if lt IE 8]><link rel="stylesheet" href="blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
  </head>
  <body>
    <div class="container showgrid">
  test
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  <hr class="space" />
  test
    </div>
  </body>
</html>
توضیحات:
پس از مشخص سازی DocType (مهم)، سه فایل CSS یاد شده به header صفحه اضافه خواهند شد. همانطور که ملاحظه می‌کنید، سازگاری با IE نیز مدنظر آن بوده است.
کار با blueprint css همواره داخل div زیر انجام می‌شود:
<div class="container">
  page
</div>
توسط کلاس container یک گرید به عرض 950px در میانه صفحه برای شما تشکیل خواهد شد.
اگر علاقمند باشید که این گرید را مشاهده نمائید و همچنین بتوانید ستون‌های آن‌را نیز شمارش کنید، تنها کافی است showgrid را به این class تعریف شده اضافه نمائید (همانند ساختار صفحه فوق). به این ترتیب شکل زیر نمایان خواهد شد:

مطابق شکل فوق، در این عرض مشخص، 24 ستون آن در اختیار ما خواهند بود.
به علاوه ذکر hr با class=space سبب خواهد شد تا مطابق تنظیمات و فاصله بندی منظم این فریم ورک، یک سطر خالی برای ما ایجاد شود.


طراحی بدون جدول با Blueprint CSS

در ادامه قصد داریم در این صفحه ابتدایی، یک جدول با دو ستون و دو ردیف را ایجاد کنیم:
  <body>
    <div class="container showgrid">
      <div class="span-12">
        row1-col1
      </div>
      <div class="span-12 last">
        row1-col2
      </div>
      <div class="span-12">
        row2-col1
      </div>
      <div class="span-12 last">
        row2-col2
      </div>
    </div>
  </body>
که شکل زیر را برای ما ایجاد خواهد کرد:

توضیحات:
ستون‌های گرید نهایی با رنگ آبی مشخص هستند (class=container showgrid). اگر نیاز به 12 ستون داریم، می‌نویسیم span-12 و ... همین! به این ترتیب یک سلول جدول، با 12 ستون در اختیار ما خواهد بود. سلول بعدی هم در اینجا 12 ستونه است. اما یک last را اضافه‌تر دارد. در span-12 last این last به معنای انتهای ردیف جاری است و ذکر آن الزامی است.
تا اینجا یک ردیف تمام شد. اکنون در ادامه ردیف دوم را نیز به همین ترتیب با دو div و class‌هایی که ملاحظه می‌کنید، مشخص خواهیم کرد.
نحوه کار کلی با Blueprint css به همین سادگی است که ملاحظه می‌کنید. تعداد ستون‌های مورد نیاز را با ذکر container showgrid به سادگی می‌توان شمارش کرد. سپس این اعداد شمارش شده و مد نظر را پس از span ذکر کنید. مثلا اگر یک طرح سه ستونه نیاز دارید به صورت زیر خواهد بود:
  <body>
    <div class="container showgrid">
      <div class="span-8">
        row1-col1
      </div>
      <div class="span-8">
        row1-col2
      </div>
      <div class="span-8 last">
        row1-col3
      </div>
    </div>
  </body>

طراحی سلول‌های تو در تو


سؤال: ما پیشتر در یک html table به سادگی می‌توانستیم داخل یک سلول آن حتی یک جدول جدید نیز قرار دهیم، اینجا چطور؟
پاسخ: در اینجا هم بجای td و tr و table، از divهای تو در تو استفاده کنید. بستن ستون آخر را با last یاد شده فراموش نکنید. مثلا:
  <body>
    <div class="container showgrid">
      <div class="span-8">
                <div class="span-4">
                    row-1, col1 : cell-1
                </div>
                <div class="span-4 last">
                    row-1, col1 : cell-2
                </div>
      </div>
      <div class="span-8">
        row1-col2
      </div>
      <div class="span-8 last">
        row1-col3
      </div>
    </div>
  </body>
در اینجا در اولین div تعریف شده دو div تو در تو اضافه شده‌اند. البته با توجه به اینکه div والد 8 ستونی است، جمع عرض divهای فرزند باید 8 باشد که در اینجا به دو div چهارستونی تقسیم شده است.


سایر امکانات Blueprint CSS

تا اینجا با کلیات نحوه طراحی یک جدول به کمک CSS و فریم ورک Blueprint CSS آشنا شدیم (به کمک container و span-n آن). در ادامه مرور سریعی خواهیم داشت بر سایر امکانات این فریم ورک CSS و منظور از این امکانات، کلمات و عبارات مجازی است که می‌توانید داخل classهای divهای تعریف شده اضافه نمائید (CSS selectors تعریف شده در آن):
prepend-n و border:
فرض کنید در divهای تو در توی قسمت قبل، قصد داریم عرض ستون اول را بجای 4 ستون به 3 ستون تبدیل کنیم، اما این div را یک ستون به سمت راست حرکت دهیم:
  <body>
    <div class="container showgrid">
      <div class="span-8">
        <div class="prepend-1 span-3 border">
               row-1, col1 : cell-1
        </div>
        <div class="span-4 last">
              row-1, col1 : cell-2
        </div>
      </div>
      <div class="span-8">
           row1-col2
      </div>
      <div class="span-8 last">
           row1-col3
      </div>
    </div>
  </body>
برای این منظور همانطور که ملاحظه می‌کنید از prepend-1 استفاده شده است. border در اینجا سبب خواهد شد تا در سمت راست div یک خط عمودی رسم شود. در مقابل آن colborder هم وجود دارد که سبب ترسیم حاشیه با فاصله بیشتری نسبت به border می‌شود.
شبیه به همین قابلیت، با append-x (افزودن تعدادی ستون به سمت راست)، prepend-top (فاصله‌ای به اندازه 1.5em را به بالای div اضافه می‌کند) و append-bottom (فاصله‌ای به اندازه 1.5em را به پایین div اضافه می‌کند) نیز وجود دارد.
در مقابل این‌ها، push-n و pull-n هم وجود دارند. کار append و prepend اضافه کردن چند ستون به بعد و قبل از یک div است. push یک div را به تعداد واحدی که مشخص می‌کنیم به سمت راست حرکت می‌دهد. pull یک div را n ستون به سمت چپ حرکت خواهد داد (بدون تغییری در تعداد ستون‌ها).


دریافت مرجع سریع Blueprint CSS