مطالب
آموزش فریم ورک Vuetify قسمت اول - نصب و بررسی ساختار grid؛ بخش اول
برای طراحی ui برنامه‌هایی که با فریم ورک vue.js توسعه داده می‌شوند، داشتن یک css framework مناسب، جهت زیبا سازی برنامه، یکی از انتخابهای مهم می‌باشد. در این سری از آموزش‌ها، نحوه‌ی بکارگیری فریم ورک Vuetify را یاد خواهیم گرفت.

چرا vuetify ؟

دلایل زیادی برای استفاده از این framework وجود دارد که از جمله آنها می‌توان به مواردی از قبیل رابط کاربری خوب برای طرح بندی صفحه برنامه،  پشتیبانی از تمام مرورگرها، پشتیبانی از RTL (راست به چپ کردن صفحه)، پشتیبانی از cli3  و موارد دیگر می‌توان اشاره نمود. 


روش نصب vuetify

پس از ایجاد یک برنامه Vuejs  با دستور زیر، فریم ورک Vuetify را اضافه می‌کنیم. 
vue add vuetify


ساختار grid

grid  برای طرح بندی، یا بخش بندی محتوای برنامه استفاده می‌شود .vuetify همانند bootstrap، از سیستم بخش بندی 12 تایی برای تقسیم بندی صفحه استفاده می‌کند. در این روش ما به 5 حالت مختلف می‌توانیم صفحه را بخش بندی کنیم:


طریقه‌ی استفاده

ساختار برنامه‌ی ما شامل یک سری از کامپوننت‌های از پیش تعیین شده برای راحتی و سادگی کار می‌باشد که در زیر به آنها اشاره شده‌است. 

ساختار برنامه ما باید دارای یک v-container باشد تا به درستی کار کند. این کامپوننت در بر گیرنده‌ی تمام صفحه‌ی برنامه است. هر کامپوننت می‌تواند تنظیمات خاص خود را داشته باشد.

برای مثال در کد پایین، تنظیم fluid باعث می‌شود تا کامپوننت v-container تمام عرض صفحه‌ی ما را در بر بگیرد.

<v-container fluid></v-container>

کامپوننت v-layout برای کار با ردیفها مورد استفاده قرار میگیرد که تنظیمات مخصوص به خود را دارد.

برای بخش بندی هر ستون در صفحه می‌توان از کامپوننت v-flex استفاده کرد.

نکته: در توضیح کد پایین، در قسمت تعریف v-layout به وسیله row مشخص میکنیم که می‌خواهیم یک ردیف را ایجاد کنیم و در قسمت تعریف v-flex به وسیله md6 مشخص می‌کنیم که 6 خانه از 12 خانه موجود در ردیف را می‌خواهیم در اختیار داشته باشیم:

<v-container>
  <v-layout row>
    <v-flex md6>
      ...
    </v-flex>
    <v-flex md6>
      ...
    </v-flex>
  </v-layout>
</v-container>


نتیجه‌ی قطعه کد بالا بدین ترتیب است:

Two elements in row

هر کدام از کامپوننت‌های گفته شده، properties مختص به خود را دارد که در برخی موارد مشابه یکدیگر هستند. این properties مشابه flexbox در css عمل میکنند.
نکته: تمامی این property‌ها از نوع داده boolean بوده و مقدار اولیه آنها false می‌باشد. 

breakpoint‌ها : 
به وسیله breakpoint‌ها می‌توانیم مشخص کنیم که هر المان درون هر مدیا (موبایل، تبلت، کامپیوتر و ...) به چه صورتی نمایش داده شود. در حالت کلی پنج نوع breakpoint وجود دارند که به ترتیب شامل:
xs جهت نمایش درون دستگاه‌هایی است که اندازه‌ی صفحه‌ی آنها از 600 پیکسل کمتر باشد.
sm جهت نمایش درون دستگاه‌هایی است که اندازه‌ی آنها از 600 پیکسل بزرگتر و از 960 پیکسل کوچکتر باشد.
md جهت نمایش درون دستگاه‌هایی است که اندازه‌ی آنها از 960 پیکسل بزرگتر و از 1264 پیکسل کوچکتر باشد.
lg جهت نمایش درون دستگاه‌هایی است که اندازه‌ی آنها از 1264 پیکسل بزرگتر و از 1904 پیکسل کوچکتر باشد.
xl جهت نمایش درون دستگاه‌هایی است که اندازه‌ی آنها بیش از 1904 پیکسل باشد.

هر یک از این breakpointها می‌توانند به وسیله‌ی کامپوننت v-flex، حاوی مقداری بین 1 تا 12 باشند تا درون هر مدیا به صورت مشخصی نمایش داده شوند.
نکته: ما میتوانیم برای هر کدام از این مدیا‌ها به صورت جداگانه این تنظیمات را اعمال کنیم.
نکته: اگر برای کوچکترین حالت، یعنی xs، یک مقدار را مشخص کنیم و برای حالت‌های دیگر مقداری را تنظیم نکنیم، به این معنا است که حالتهای دیگر نیز با مقداری که برای xs تنظیم کرده‌ایم، تنظیم می‌شوند.

برای مثال: در این قطعه کد ما برای xs، مقدار 6 و برای sm، مقدار 5 را در نظر گرفته‌ایم. یعنی در دستگاه‌های Extra small، این پارامتر تعداد 6 خانه از 12 خانه در دسترس را اشغال میکند؛ اما درون دستگاه‌های small، تعداد 5 خانه از 12 خانه را اشغال می‌نماید. باید بدانیم به صورت خودکار برای md  وlg ،xl نیز مقدار 5 در نظر گرفته می‌شود. 
<v-flex xs6 sm5>
</v-flex>

به این مثال توجه کنید: در این مثال ما برای مدیاهای xs، مقدار 6 و برای مدیاهای md، مقدار 8 را در نظر گرفته‌ایم. نکته قابل توجه اینجاست که در چنین حالتی برای مدیهای sm، مقدار 6 به صورت خودکار در نظر گرفته میشود و برای مدیاهای lg و xl نیز به صورت خودکار مقدار 8 در نظر گرفته می‌شوند.
<v-flex xs5 md8>
</v-flex>

Offset
به وسیله‌ی offset می‌توانیم مشخص کنیم که هر کامپوننت v-flex از چه موقعیتی در v-layout شروع به گرفتن فضا کند. در پایین یک مثال کاربردی آورده شده‌است تا به صورت بهتر کارکرد offset‌ها را درک کنیم. در توضیح کد پایین در خط پنجم، مقدار offset-xs1 در نظر گرفته شده است. بدین معنا که با توجه به بخش بندی 12 تایی که داریم، کلاس offset-xs1 باعث می‌شود برای گرفتن  فضای تعریف شده توسط xs10، ستون اول را نادیده گرفته و از ستون دوم فضا را اشغال کند. همانطور که قبل‌تر نیز گفته شد، به دلیل اینکه برای xs مقدار 10 تنظیم شده‌است، درون تمامی مدیاها v-flex، تعداد 10 خانه از 12 خانه در دسترس را اشغال می‌کند.
<div id="app">
  <v-app id="inspire">
    <v-container grid-list-xl text-xs-center>
      <v-layout row wrap>
        <v-flex xs10 offset-xs1>
          <v-card dark color="purple">
            <v-card-text>xs10 offset-xs1</v-card-text>
          </v-card>
        </v-flex>
        <v-flex xs7 offset-xs5 offset-md2 offset-lg5>
          <v-card dark color="secondary">
            <v-card-text>xs7 offset-(xs5 | md2 | lg5)</v-card-text>
          </v-card>
        </v-flex>
        <v-flex xs12 sm5 md3>
          <v-card dark color="primary">
            <v-card-text>(xs12 | sm5 | md3)</v-card-text>
          </v-card>
        </v-flex>
        <v-flex xs12 sm5 md5 offset-xs0 offset-lg2>
          <v-card dark color="green">
            <v-card-text>(xs12 | sm5 | md5) offset-(xs0 | lg2)</v-card-text>
          </v-card>
        </v-flex>
      </v-layout>
    </v-container>
  </v-app>
</div>

نتیجه‌ی قطعه کد بالا بدین صورت است:


نظرات مطالب
فرم‌های مبتنی بر قالب‌ها در Angular - قسمت چهارم - اعتبارسنجی ورودی‌ها
یک نکته‌ی تکمیلی
در صورتی که تعداد فیلد‌های فرم زیاد باشد با غیر فعال کردن دکمه submit کاربر نمی‌تواند تشخیص دهد که کدام المان ورودی را باید مقدار دهی کند یک راه حل این است که در کنار المان‌های که required  می‌باشند یک * قرمز رنگ قرار دهیم . 
راه حل دوم این است که کاربر با زدن submit  خطاهای فرم را مشاهده کند : 
// student-model.ts
export interface Student {
    id: number;
    name: string;
}
کامپوننت : 
  // app.component.ts
  public model: Student;

  ngOnInit(): void {
    this.setDefaultValueForModel();
  }

  saveForm(form: NgForm, evetn: Event) {
    evetn.preventDefault();
    if (form.valid) {
      alert('everything is ok');
    }
  }

  setDefaultValueForModel() {
    this.model = {
      id: 1,
      name: ''
    };
  }
و در نهایت محتوای app.component.html
        <form #form="ngForm" novalidate (submit)="saveForm(form,$event)">
          <div>
            <label>Name</label>
            <input type="text" required name="name" autocomplete="off" [(ngModel)]="model.name" #name="ngModel">

            <p [hidden]="name.valid || (name.pristine && !form.submitted)">
              Name is required and should be minimum 4 characters.
            </p>

          </div>

          <div>
            <input type="submit" value="submit">
          </div>
        </form>
مطالب
آشنایی با نحوه‌ی وهله سازی کنترلرها در ASP.NET MVC با ساخت یک Controller Factory سفارشی
یکی از مزایای مهم فریم ورک ASP.NET MVC، توسعه پذیری کنترلرهای آن است. با مرور قسمت‌هایی از مسیر پردازش درخواست که منجر به اجرای یک اکشن متد می‌شود، شروع می‌کنیم و روش‌های مختلفی را که می‌توان بر روی این پردازش، کنترل داشت، بررسی می‌کنیم. شکل ذیل مسیر یک درخواست را مابین کامپوننت‌های مختلف فریم ورک نشان می‌دهد:
 
 

Controller Factory و Action Invoker وظیفه‌ای مطابق نامشان را عهده دار هستند. اولی برای وهله سازی کنترلرهای مرتبط با درخواست و دومی برای پیدا کردن و تریگر نمودن یک اکشن متد به کار گرفته می‌شوند. فریم ورک MVC پیاده سازی پیش فرضی را از این دو کامپوننت، به صورت توکار دارد. در طی مقالاتی نحوه‌ی کنترل کردن رفتار پیش فرض این Controller Factory و هم نحوه‌ی جایگزین کرن کامل این کامپوننت را بررسی می‌کنیم.

ابتدا پروژه‌ی جدیدی را از نوع MVC و با الگوی Empty به نام ControllerExtensibility ایجاد می‌کنیم. در پوشه‌ی Models یک فایل را به نام Result.cs ساخته و از آن برای معرفی کلاس Result مطابق کدهای ذیل استفاده می‌کنیم:
namespace ControllerExtensibility.Models
{
    public class Result
    {
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
    }
}
در مسیر /Views/Shared ویویی را به نام Result.cshtml اضافه می‌کنیم. این ویویی است که در این مثال، همه‌ی اکشن متدهای کنترلرهایمان، آن را رندر خواهند کرد:
@model ControllerExtensibility.Models.Result
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Result</title>
</head>
<body>
<div>Controller: @Model.ControllerName</div>
<div>Action: @Model.ActionName</div>
</body>
</html>
در خط اول، مدل ویو را از نوع کلاس Result تعیین کرده‌ایم.
دو کنترلر را نیز حاوی کدهای زیر ایجاد می‌کنیم:

کنترلر product
using ControllerExtensibility.Models;
using System.Web.Mvc;
namespace ControllerExtensibility.Controllers
{
    public class ProductController : Controller
    {
        public ViewResult Index()
        {
            return View("Result", new Result
            {
                ControllerName = "Product",
                ActionName = "Index"
            });
        }
        public ViewResult List()
        {
            return View("Result", new Result
            {
                ControllerName = "Product",
                ActionName = "List"
            });
        }
    }
}

کنترلر customer

using System.Web.Mvc;
namespace ControllerExtensibility.Controllers
{
    public class CustomerController : Controller
    {
        public ViewResult Index()
        {
            return View("Result", new Result
            {
                ControllerName = "Customer",
                ActionName = "Index"
            });
        }
        public ViewResult List()
        {
            return View("Result", new Result
            {
                ControllerName = "Customer",
                ActionName = "List"
            });
        }
    }
}
اکشن‌های این دو کنترلر حاوی کد خاصی نبوده و صرفا ویوی Result.cshtml را صدا می‌زنند. ولی در این مرحله این همه‌ی آن چیزی است که برای نشان دادن نحوه‌ی سفارشی کردن کنترلرها بدان نیاز داریم.
ایجاد یک Controller Factory سفارشی بهترین راه برای درک نحوه‌ی وهله سازی کنترلر‌ها توسط MVC است. ولی این کار صرفا جنبه‌ی آموزشی داشته و در یک پروژه‌ی واقعی این نوع پیاده سازی‌ها پیشنهاد نمی‌شود؛ زیرا راه‌های مفیدتر و ساده‌تری با پیاده سازی توکار Controller Factory وجود دارند.
Controller Factory‌ها با پیاده سازی اینترفیس IControllerFactory معرفی می‌شوند. کدهای این اینترفیس را در ذیل می‌بینید:
using System.Web.Routing;
using System.Web.SessionState;
namespace System.Web.Mvc
{
    public interface IControllerFactory
    {
        IController CreateController(RequestContext requestContext,
        string controllerName);
        SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext,
        string controllerName);
        void ReleaseController(IController controller);
    }
}
پوشه‌ای را به نام Infrastructure ساخته و فایلی را به نام CustomControllerFactory.cs ، حاوی کدهای زیر اضافه کنید:
using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using ControllerExtensibility.Controllers;

namespace ControllerExtensibility.Infrastructure
{
    public class CustomControllerFactory : IControllerFactory
    {
        public IController CreateController(RequestContext requestContext,
            string controllerName)
        {
            Type targetType = null;
            switch (controllerName)
            {
                case "Product":
                    targetType = typeof (ProductController);
                    break;
                case "Customer":
                    targetType = typeof (CustomerController);
                    break;
                default:
                    requestContext.RouteData.Values["controller"] = "Product";
                    targetType = typeof (ProductController);
                    break;
            }
            return targetType == null
                ? null
                : (IController) DependencyResolver.Current.GetService(targetType);
        }

        public SessionStateBehavior GetControllerSessionBehavior(RequestContext
            requestContext, string controllerName)
        {
            return SessionStateBehavior.Default;
        }

        public void ReleaseController(IController controller)
        {
            IDisposable disposable = controller as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
    }
}
مهمترین متد کدهای فوق، CreateController است که فریم ورک، بر حسب نیاز، جهت سرویس دهی به درخواست واصله آن را صدا خواهد زد. پارامتر ورودی این متد، شیء RequestContext است که جزئیاتی در خصوص درخواست واصله را در اختیار factory خواهد گذاشت. همچنین یک رشته که نام کنترلر را بر حسب URL واصله تعیین می‌کند:
 

نام

نوع

توضیحات

HttpContext

HttpContextBase

حاوی اطلاعاتی در خصوص درخواست است.

RouteData

RouteData

حاوی اطلاعاتی در خصوص Rout است که با درخواست رسیده همخوانی دارد.

 
یکی از دلایلی که عنوان شد Controller factory سفارشی بدین روش در یک پروژه‌ی عملی به کار گرفته نشود این است که یافتن کلاس‌هایی از نوع Controller در سراسر برنامه و وهله سازی آنها کار دشواری است. چرا که لازم خواهد بود بتوانید به صورت پویا کنترلر را مکان یابی کرده و بین کلاس‌های هم نام در دیگر فضاهای نام تمییز قائل شوید و خطاهای محتمل در حین وهله سازی را کنترل کنید.
در این مثال تنها دو کنترلر داریم و آنها را به صورت مستقیم در Controller Factory وهله سازی می‌کنیم که در یک پروژه‌ی واقعی مطلوب نیست. ولی آنچه را که این روش آشکار‌تر می‌سازد، انعطاف پذیری بالای فریم ورک MVC است که دست ما را برای نفوذ و دخل و تصرف در اعمال و رفتاریهای پیش فرض خود باز گذاشته است و برای مثال در مباحث تزریق وابستگی‌ها و تنظیمات ابتدایی IoC Containers کاربرد دارد.
متد CreateController لازم است وهله‌ای از کلاسی که اینترفیس IController را پیاده سازی کرده برگرداند؛ در غیر اینصورت کار با خطا متوقف خواهد شد. لذا برای زمانی که درخواست کاربر، هیچ کدام از کنترلر‌ها را مشمول عنایت قرار نمی‌دهد، باید چاره‌ای اندیشیده شود.
می‌توان آن را به کنترلر خاصی که پیغام خطایی را رندر می‌کند، هدایت کنیم. به عبارت بهتر باید درخواست را به کنترلری که مطمئن هستیم وجود دارد (اصطلاحا کنترلر جانشین) هدایت نماییم. همان طور که در کد فوق در قسمت default می‌بینید:
default:
requestContext.RouteData.Values["controller"] = "Product";
targetType = typeof(ProductController);
break;
در صورت عدم تطابق با هیچ کدام از حالات تعیین شده، درخواست را به کنترلر ProductController جهت رسیدگی هدایت کرده‌ایم.
در MVC انتخاب ویوی مناسب، بر حسب مقدار RouteData.Values صورت می‌گیرد؛ نه نام کلاس Controller و این سبب خواهد شد فریم ورک، ویوهای مرتبط با کنترلر جانشین شده‌ی توسط ما را جستجو کند و نه کنترلری که کاربر از طریق URL ورودی آن را درخواست کرده است.
لذا Controller Factory صرفا وظیفه مپ کردن درخواست‌های واصله به کنترلر‌ها را ندارد، بلکه توانایی دخل و تصرف در درخواست واصله بر حسب مورد را نیز خواهد داشت.
در نهایت هم نحوه‌ی استفاده از DependencyResolver را برای وهله سازی کلاس‌های کنترلر می‌بینید. متد استاتیک Current یک پیاده سازی از اینترفیس IDependencyResolver را که حاوی متد GetService است، برگشت داده و سپس یک شیء System.Type را به عنوان ورودی گرفته و یک وهله‌ی ساخته شده‌ی از آن را به عنوان خروجی برمی‌گرداند.
متد GetControllerSessionBehavior نیز توسط MVC جهت تعیین اینکه Session data برای کنترلر نیاز است یا خیر به کار گرفته می‌شود.
متد ReleaseController نیز هر گاه به شیء کنترلر ساخته شده در متد CreateController دیگر نیازی نبود، صدا زده خواهد شد. در کدهای ما ابتدا بررسی می‌شود آیا اینترفیس IDisposable توسط کلاس، پیاده سازی شده است یا خیر؟ اگر بلی متد Dispose آن جهت آزاد سازی منابعی که می‌توانند آزاد شوند، صدا زده می‌شود.
جهت ثبت Controller  Factory ساخته شده در متد Application_Start موجود در فایل global.asax.cs بوسیله کلاس ControllerBuilder و مطابق کدهای ذیل عمل می‌نماییم:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using ControllerExtensibility.Infrastructure;
namespace ControllerExtensibility
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(new
            CustomControllerFactory());
        }
    }
}
پس از ثبت به شیوه‌ی فوق، controller factory ساخته شده، مسئول هندل کردن تمامی درخواست‌های واصله‌ی به برنامه خواهد بود. پس از اولین اجرا، مرورگر ریشه‌ی سایت را هدف قرار خواهد داد که توسط سیستم مسیر یابی به کنترلر Home، نگاشت شده و بر اساس تعاریف و کدهای ما، چون با هیچ کدام از کنترلرهای Product و Customer تطابق نخواهد داشت، به کنترلر جایگزین تنظیم شده، یعنی Product هدایت خواهد شد.


 
نظرات مطالب
ASP.NET MVC #7
مباحث اعتبارسنجی در MVC را مطالعه کنید. پایه آن همین مطالب است. پس از اعتبارسنجی کاربر، در یک View می‌شود ساده if و else نوشت. مثلا:
@if (User.Identity.IsAuthenticated && User.IsInRole("Administrator"))
{
     <div id="sidebar">
      data
     </div>
}
در این حالت اگر کاربر به سیستم لاگین کرده باشد و همچنین نقش Administrator نیز به او پیشتر انتساب داده شده باشد، اطلاعات خاصی را مشاهده خواهد کرد.
بازخوردهای دوره
آشنایی با مدل برنامه نویسی TAP
با سلام
آیا دات نت 4.5 رو میشه روی xp نصب کرد ؟ 
دات نت 4 را روی xp sp2  با نصب کامپوننت Window Imaging Component میشد نصب کرد آیا برای نصب دات نت 4.5 روی xp یه چنین راه حلی وجود نداره ؟  
با تشکر
نظرات مطالب
سازماندهی برنامه‌های Angular توسط ماژول‌ها
سلام ؛ اگر قرار باشد کلیه روتهای برنامه به عنوان childrout‌های والدی مثلا pages باشند از آنجا که برنامه حاوی چندین feature modules است تعریف همه  روتها در app-routing باشد ؟ یا در featureX-routing  ؟ (روت والد دارای کامپوننت page-component می‌باشد و منظور گروه بندی  تنها نیست . ) با تشکر .
مطالب
ارتباط بین Controller و Directive در AngularJs
در پست قبلی با کلیات مفاهیم دیرکتیو‌ها آشنا شدید. در این پست قصد داریم برخی توابع  کنترلرهای تعریف شده در Angular را به وسیله دیرکتیو‌های تعریف شده در ماژول فراخوانی نماییم. در ادامه این موضوع را طی یک مثال بررسی خواهیم کرد.
ابتدا View مورد نظر را به صور زیر ایجاد می‌کنیم:
<script type="text/javascript" src="~/scripts/Modules/module4.js"></script>

<div ng-app="myApp">
    <div ng-controller="myCtrl">
        <span enter>Load More Books</span>
    </div>
</div>
برنامه به این صورت است که با ورود نشانگر ماوس بر روی تگ span (فراخوانی رویداد mouseenter برای تگ هایی که دارای دیرکتیو enter باشند) یک تابع به نام loadMoreBook در کنترلر myCtrl فراخوانی می‌شود.
بک فایل جاوااسکریپتی به نام myModule بسازید و ماژول مورد نظر را ایجاد نمایید:
var app = angular.module('myApp', []);
کنترلر مورد نظر به همراه تابع loadMoreBook را به صورت زیر ایجاد می‌کنیم(البته در اینجا به جای لود واقعی داده از یک alert استفاده کردم):
app.controller('myCtrl', function ($scope) {
    $scope.loadMoreBook = function () {
        alert('Loading Books...');
    }
});
حال نوبت به دیرکتیو مورد بحث می‌رسد که به صورت زیر ایجاد می‌شود:
app.directive('enter', function () {
    return function (scope, element) {
        element.bind('mouseenter', function () {
            scope.loadMoreBook();
        })        
    }
});
اولین نکته این است که به در تابع سازنده دیرکتیو به جای برگشت آبجک مورد نظر یک تابع برگشت داه می‌شود. برای اینکه بتوان به توابع کنترلر محصور کننده دیرکتیو دسترسی داشت آرگومان اول تابع معادل scope مورد استفاده در کنترلر خواهد بود. آرگومان دوم معادل المانی است که  دارای دیرکتیو enter است. در این تابع ابتدا برای رویداد mouseenter رویدادگردان آن پیاده سازی شده است که در آن تابع loadMoreBook کنترلر مورد نظر فراخوانی می‌شود.
خروجی


حال فرض بر این است که در کنترلر بالا تابع دیگری به نام loadMoreAuthor برای فراخوانی نویسندگان نیز وجود دارد. به صورت زیر:
app.controller('myCtrl', function ($scope) {
    $scope.loadMoreBook = function () {
        alert('Loading Books...');
    }

    $scope.loadMoreAuthor = function () {
        alert('Loading Authors...');
    }
});
اما برای انعطاف پذیری بیشتر برنامه، قصد داریم دیرکتیو بالا را به گونه ای تغییر دهیم که نام تابع مورد نظر در کنترلر را به عنوان مقدار یک ویژگی دریافت کند. به صورت زیر:

<script type="text/javascript" src="~/scripts/Modules/module4.js"></script>

<div ng-app="myApp">
    <div ng-controller="myCtrl">
        <span enter="loadMoreBook()">Load More Book</span>         
        <hr>
       <span enter="loadMoreAuthor()">Load More Author</span>
    </div>
</div>
برای به دست آوردن مقدار دیرکتیوی که به عنوان ویژگی در المان تعیین شده، باید از آرگومان سوم در تابع سازنده دیرکتیو به صورت زیر استفاده کرد.
app.directive('enter', function () {
    return function (scope, element , attrs) {
        element.bind('mouseenter', function () {
            scope.$apply(attrs.enter);
        })        
    }   
});
در  کد‌های بالا، برای اینکه بتوان بر اساس نام یک تابع آن را فراخوانی کرد، از سرویس apply$ که به صورت توکار در angular تعبیه شده است استفاده کردم. برای به دست آوردن نام تابع، باید از آرگومان سوم تابع (attrs) به همراه نام دیرکتیو استفاده کرد. به دلیل اینکه نام دیرکتیو enter است باید پارامتر سرویس apply$ به صورت attrs.enter باشد. خروجی نیز مانند حالت قبل خواهد بود.
مطالب
آشنایی با Fluent interfaces

تعریف مقدماتی fluent interface در ویکی پدیا به شرح زیر است: (+)

In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a way of implementing an object oriented API in a way that aims to provide for more readable code.

به صورت خلاصه هدف آن فراهم آوردن روشی است که بتوان متدها را زنجیر وار فراخوانی کرد و به این ترتیب خوانایی کد نوشته شده را بالا برد. پیاده سازی آن هم شامل دو نکته است:
الف) نوع متد تعریف شده باید مساوی با نام کلاس جاری باشد.
ب) در این حالت خروجی متد‌های ما کلمه کلیدی this خواهند بود.

برای مثال:
using System;

namespace FluentInt
{
public class FluentApiTest
{
private int _val;

public FluentApiTest Number(int val)
{
_val = val;
return this;
}

public FluentApiTest Abs()
{
_val = Math.Abs(_val);
return this;
}

public bool IsEqualTo(int val)
{
return val == _val;
}
}
}
مثالی هم از استفاده‌ی آن به صورت زیر می‌تواند باشد:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
که در آن توانستیم تمام متدها را زنجیر وار و با خوانایی خوبی شبیه به نوشتن جملات انگلیسی در کنار هم قرار دهیم.
خوب! این مطلبی است که همه جا پیدا می‌کنید و مطلب جدیدی هم نیست. اما موردی را که سخت می‌شود یافت این است که طراحی کلاس فوق ایراد دارد. برای مثال شما می‌توانید ترکیب‌های زیر را هم تشکیل دهید و کار می‌کند؛ یا به عبارتی برنامه کامپایل می‌شود و این خوب نیست:
if(new FluentApiTest().Abs().Number(-10).IsEqualTo(10)) ...
if (new FluentApiTest().Abs().IsEqualTo(10)) ...
می‌شود در کدهای برنامه یک سری throw new exception را هم قرار داد که ... هی! اول باید اون رو فراخوانی کنی بعد این رو!
ولی ... این روش هم صحیح نیست. از ابتدای کار نباید بتوان متد بی‌ربطی را در طول این زنجیره مشاهده کرد. اگر قرار نیست استفاده گردد، نباید هم در intellisense ظاهر شود و پس از آن هم نباید قابل کامپایل باشد.

بنابراین صورت مساله به این ترتیب اصلاح می‌شود:
می‌خواهیم پس از نوشتن FluentApiTest و قرار دادن یک نقطه، در intellisense فقط Number ظاهر شود و نه هیچ متد دیگری. پس از ذکر متد Number فقط متد Abs یا مواردی شبیه به آن مانند Sqrt ظاهر شوند. پس از انتخاب مثلا Abs آنگاه متد IsEqualTo توسط Intellisense قابل دسترسی باشد. در روش اول فوق، به صورت دوستانه همه چیز در دسترس است و هر ترکیب قابل کامپایلی را می‌شود با متدها ساخت که این مورد نظر ما نیست.
اینبار پیاده سازی اولیه به شرح زیر تغییر خواهد کرد:
using System;

namespace FluentInt
{
public class FluentApiTest
{
public MathMethods<FluentApiTest> Number(int val)
{
return new MathMethods<FluentApiTest>(this, val);
}
}

public class MathMethods<TParent>
{
private int _val;
private readonly TParent _parent;

public MathMethods(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public Restrictions<MathMethods<TParent>> Abs()
{
_val = Math.Abs(_val);
return new Restrictions<MathMethods<TParent>>(this, _val);
}
}

public class Restrictions<TParent>
{
private readonly int _val;
private readonly TParent _parent;

public Restrictions(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public bool IsEqualTo(int val)
{
return _val == val;
}
}
}
در اینجا هم به همان کاربرد اولیه می‌رسیم:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
با این تفاوت که intellisense هربار فقط یک متد مرتبط در طول زنجیره را نمایش می‌دهد و تمام متدها در همان ابتدای کار قابل انتخاب نیستند.
در پیاده سازی کلاس MathMethods از Generics استفاده شده به این جهت که بتوان نوع متد Number را بر همین اساس تعیین کرد تا متدهای کلاس MathMethods در Intellisense (یا به قولی در طول زنجیره مورد نظر) ظاهر شوند. کلاس Restrictions نیز به همین ترتیب معرفی شده است و از آن جهت تعریف نوع متد Abs استفاده کردیم. هر کلاس جدید در طول زنجیره، توسط سازنده خود به وهله‌ای از کلاس قبلی به همراه مقادیر پاس شده دسترسی خواهد داشت. به این ترتیب زنجیره‌ای را تشکیل داده‌ایم که سازمان یافته است و نمی‌توان در آن متدی را بی‌جهت پیش یا پس از دیگری صدا زد و همچنین دیگر نیازی به بررسی نحوه‌ی فراخوانی‌های یک مصرف کننده نیز نخواهد بود زیرا برنامه کامپایل نمی‌شود.
مطالب
Roslyn #4
بررسی API کامپایل Roslyn

Compilation API، یک abstraction سطح بالا از فعالیت‌های کامپایل Roslyn است. برای مثال در اینجا می‌توان یک اسمبلی را از Syntax tree موجود، تولید کرد و یا جایگزین‌هایی را برای APIهای قدیمی CodeDOM و Reflection Emit ارائه داد. به علاوه این API امکان دسترسی به گزارشات خطاهای کامپایل را میسر می‌کند؛ به همراه دسترسی به اطلاعات Semantic analysis. در مورد تفاوت Syntax tree و Semantics در قسمت قبل بیشتر بحث شد.
با ارائه‌ی Roslyn، اینبار کامپایلرهای خط فرمان تولید شده مانند csc.exe، صرفا یک پوسته بر فراز Compilation API آن هستند. بنابراین دیگر نیازی به فراخوانی Process.Start بر روی فایل اجرایی csc.exe مانند یک سری کتابخانه‌های قدیمی نیست. در اینجا با کدنویسی، به تمام اجزاء و تنظیمات کامپایلر، دسترسی وجود دارد.


کامپایل پویای کد توسط Roslyn

برای کار با API کامپایل، سورس کد، به صورت یک رشته در اختیار کامپایلر قرار می‌گیرد؛ به همراه تنظیمات ارجاعاتی به اسمبلی‌هایی که نیاز دارد. سپس کار کامپایلر شروع خواهد شد و شامل مواردی است مانند تبدیل متن دریافتی به Syntax tree و همچنین تبدیل مواردی که اصطلاحا به آن‌ها Syntax sugars گفته می‌شود مانند خواص get و set دار به معادل‌های اصلی آن‌ها. در اینجا کار Semantic analysis هم انجام می‌شود و شامل تشخیص حوزه‌ی دید متغیرها، تشخیص overloadها و بررسی نوع‌های بکار رفته‌است. در نهایت کار تولید فایل باینری اسمبلی، از اطلاعات آنالیز شده صورت می‌گیرد. البته خروجی کامپایلر می‌تواند اسمبلی‌های exe یا dll، فایل XML مستندات اسمبلی و یا فایل‌های .netmudule و .winmdobj مخصوص WinRT هم باشد.
در ادامه، اولین مثال کار با Compilation API را مشاهده می‌کنید. پیشنیاز اجرای آن همان مواردی هستند که در قسمت قبل بحث شدند. یک برنامه‌ی کنسول ساده‌ی .NET 4.6 را آغاز کرده و سپس بسته‌ی نیوگت Microsoft.CodeAnalysis را در آن نصب کنید. در ادامه کدهای ذیل را به پروژه‌ی آماده شده اضافه کنید:
static void firstCompilation()
{
    var tree = CSharpSyntaxTree.ParseText("class Foo { void Bar(int x) {} }");
 
    var mscorlibReference = MetadataReference.CreateFromFile(typeof (object).Assembly.Location);
 
    var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
 
    var comp = CSharpCompilation.Create("Demo")
                                .AddSyntaxTrees(tree)
                                .AddReferences(mscorlibReference)
                                .WithOptions(compilationOptions);
 
    var res = comp.Emit("Demo.dll");
 
    if (!res.Success)
    {
        foreach (var diagnostic in res.Diagnostics)
        {
            Console.WriteLine(diagnostic.GetMessage());
        }
    }
}
در اینجا نحوه‌ی کامپایل پویای یک قطعه کد متنی سی‌شارپ را به DLL معادل آن مشاهده می‌کنید. مرحله‌ی اول اینکار، تولید Syntax tree از رشته‌ی متنی دریافتی است. سپس متد CSharpCompilation.Create یک وهله از Compilation API مخصوص #C را آغاز می‌کند. این API به صورت Fluent طراحی شده‌است و می‌توان سایر قسمت‌های آن‌را به همراه یک دات پس از ذکر متد، به طول زنجیره‌ی فراخوانی، اضافه کرد. برای نمونه در این مثال، نحوه‌ی افزودن ارجاعی را به اسمبلی mscorlib که System.Object در آن قرار دارد و همچنین ذکر نوع خروجی DLL یا DynamicallyLinkedLibrary را ملاحظه می‌کنید. اگر این تنظیم ذکر نشود، خروجی پیش فرض از نوع .exe خواهد بود و اگر mscorlib را اضافه نکنیم، نوع int سورس کد ورودی، شناسایی نشده و برنامه کامپایل نمی‌شود.
متدهای تعریف شده توسط Compilation API به یک s جمع، ختم می‌شوند؛ به این معنا که در اینجا در صورت نیاز، چندین Syntax tree یا ارجاع را می‌توان تعریف کرد.
پس از وهله سازی Compilation API و تنظیم آن، اکنون با فراخوانی متد Emit، کار تولید فایل اسمبلی نهایی صورت می‌گیرد. در اینجا اگر خطایی وجود داشته باشد، استثنایی را دریافت نخواهید کرد. بلکه باید خاصیت Success نتیجه‌ی آن‌را بررسی کرده و درصورت موفقیت آمیز نبودن عملیات، خطاهای دریافتی را از مجموعه‌ی Diagnostics آن دریافت کرد. کلاس Diagnostic، شامل اطلاعاتی مانند محل سطر و ستون وقوع مشکل و یا پیام متناظر با آن است.


معرفی مقدمات Semantic analysis

Compilation API به اطلاعات Semantics نیز دسترسی دارد. برای مثال آیا Type A قابل تبدیل به Type B هست یا اصلا نیازی به تبدیل ندارد و به صورت مستقیم قابل انتساب هستند؟ برای درک بهتر این مفهوم نیاز است یک مثال را بررسی کنیم:
        static void semanticQuestions()
        {
            var tree = CSharpSyntaxTree.ParseText(@"
using System;
 
class Foo
{
    public static explicit operator DateTime(Foo f)
    {
        throw new NotImplementedException();
    }
 
    void Bar(int x)
    {
    }
}");
 
            var mscorlib = MetadataReference.CreateFromFile(typeof (object).Assembly.Location);
            var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
            var comp = CSharpCompilation.Create("Demo").AddSyntaxTrees(tree).AddReferences(mscorlib).WithOptions(options);
            // var res = comp.Emit("Demo.dll");
 
            // boxing
            var conv1 = comp.ClassifyConversion(
                comp.GetSpecialType(SpecialType.System_Int32),
                comp.GetSpecialType(SpecialType.System_Object)
                );
 
            // unboxing
            var conv2 = comp.ClassifyConversion(
                comp.GetSpecialType(SpecialType.System_Object),
                comp.GetSpecialType(SpecialType.System_Int32)
                );
 
            // explicit reference conversion
            var conv3 = comp.ClassifyConversion(
                comp.GetSpecialType(SpecialType.System_Object),
                comp.GetTypeByMetadataName("Foo")
                );
 
            // explicit user-supplied conversion
            var conv4 = comp.ClassifyConversion(
                comp.GetTypeByMetadataName("Foo"),
                comp.GetSpecialType(SpecialType.System_DateTime)
                );
        }
تا سطر CSharpCompilation.Create این مثال، مانند قبل است و تا اینجا به Compilation API دسترسی پیدا کرده‌ایم. پس از آن می‌خواهیم یک Semantic analysis مقدماتی را انجام دهیم. برای این منظور می‌توان از متد ClassifyConversion استفاده کرد. این متد یک نوع مبداء و یک نوع مقصد را دریافت می‌کند و بر اساس اطلاعاتی که از Compilation API بدست می‌آورد، می‌تواند مشخص کند که برای مثال آیا نوع کلاس Foo قابل تبدیل به DateTime هست یا خیر و اگر هست چه نوع تبدیلی را نیاز دارد؟


برای مثال نتیجه‌ی بررسی آخرین تبدیل انجام شده در تصویر فوق مشخص است. با توجه به تعریف public static explicit operator DateTime در سورس کد مورد آنالیز، این تبدیل explicit بوده و همچنین user defined. به علاوه متدی هم که این تبدیل را انجام می‌دهد، مشخص کرده‌است.