مطالب دوره‌ها
ارائه کاربری ساده‌تر انتخاب چندین آیتم از یک لیست به کمک افزونه TagIt در ASP.NET MVC
چندی قبل مطلبی را در مورد بررسی تفصیلی رابطه چند به چند در این سایت مطالعه کردید. در آن مطلب صرفا به بحث ذخیره سازی اطلاعات دریافتی از کاربر اشاره شد. برای مثال اگر مطلبی چندین برچسب دارد، چگونه باید این‌ها را در بانک اطلاعاتی به نحو صحیحی ذخیره کرد.
در مطلب جاری قصد داریم با نحوه ارائه یک UI کاربر پسند برای این منظور آشنا شویم و سؤال مهم هم این است: «چگونه می‌توان کار کاربر را در حین وارد کردن تعدادی از برچسب‌های مرتبط با یک مطلب ساده‌تر کرد؟». برای این منظور یکی از راه حل‌هایی که در بسیاری از سایت‌ها مرسوم شده است، استفاده از افزونه‌هایی مانند jQuery TagIt می‌باشد که در ادامه با نحوه استفاده از آن در ASP.NET MVC آشنا خواهیم شد.


پیشنیازها:
دریافت افزونه TagIt
همچنین دریافت jQuery UI (افزونه TagIt برای نمایش لیست Auto Complete آیتم‌ها از jQuery UI در پشت صحنه استفاده می‌کند)
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/TagIt/tagit-simple-blue.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/tagit.js")" type="text/javascript"></script>
    @RenderSection("JavaScript", required: false)
</head>
که نهایتا نیاز است یک چنین تعاریفی را به فایل layout برنامه اضافه کنیم.

آشنایی با مدل برنامه

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace jQueryMvcSample04.Models
{
    public class BlogPostViewModel
    {
        [DisplayName("عنوان"), Required(ErrorMessage = "*")]
        public string Title { set; get; }

        [DisplayName("متن"), Required(ErrorMessage = "*")]
        public string Body { set; get; }

        /// <summary>
        /// آرایه‌ای محدود از برچسب‌های این مطلب خاص به صورت جی‌سون که پیشتر ثبت شده است
        /// هدف استفاده در حین ویرایش مطلب
        /// </summary>
        public string InitialTags { set; get; }

        /// <summary>
        /// آرایه‌ای جی‌سونی از تمام برچسب‌های موجود در سیستم
        /// هدف نمایش منوی انتخاب برچسب‌ها از لیست
        /// </summary>
        public string TagsSource { set; get; }

        /// <summary>
        /// آرایه‌ای از برچسب‌های وارد شده توسط کاربر در حین ثبت مطلب
        /// </summary>
        [DisplayName("برچسب‌ها"), Required(ErrorMessage = "*")]
        public string[] Tags { set; get; }

        public int? Id { set; get; }
    }
}
اگر به نام این کلاس دقت کنید، به ViewModel ختم شده است. از این لحاظ که حاوی خواصی می‌باشد که عموما جهت رندر کردن صحیح UI مورد استفاده قرار می‌گیرند و معادلی در سمت بانک اطلاعاتی نخواهند داشت.
افزونه TagIt برای نمایش اطلاعات خود به دو منبع داده نیاز دارد:
الف) TagsSource : لیستی است به فرمت JSON، از هر آنچه که در سیستم پیشتر به عنوان یک برچسب ثبت شده است. از این لیست برای نمایش منوی خودکار انتخاب آیتم‌ها استفاده می‌شود.
ب) InitialTags : لیستی است به فرمت JSON، از تمام برچسب‌های مرتبط با یک مطلب. از این اطلاعات در حین ویرایش یک مطلب استفاده خواهد شد.

در این ViewModel یک خاصیت دیگر به شکل آرایه، به نام Tags تعریف شده است که لیست برچسب‌های وارد شده توسط کاربر را دریافت خواهد کرد.


معرفی کنترلر برنامه

using System.Web.Mvc;
using jQueryMvcSample04.Extensions;
using jQueryMvcSample04.Models;

namespace jQueryMvcSample04.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index(int? id)
        {
            //در ابتدای کار تمام تگ‌های موجود در سیستم از بانک اطلاعاتی دریافت خواهند شد
            //از این تگ‌ها برای تشکیل منوی انتخاب برچسب‌ها استفاده می‌شود
            var tagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();

            //همچنین صرفا برچسب‌های مطلب جاری که پیشتر ثبت شده‌اند نیز باید از بانک اطلاعاتی دریافت گردند
            //از این برچسب‌ها برای ویرایش یک مطلب موجود استفاده خواهد شد
            var init = new[] { "ASP.NET" }.ToJson();

            var model = new BlogPostViewModel
            {
                TagsSource = tagsSource,
                InitialTags = init,
                Id = id
            };
            return View(model);
        }

        [HttpPost]
        public ActionResult Index(BlogPostViewModel data)
        {
            if (this.ModelState.IsValid)
            {
                //todo: save data
                // ...
                return RedirectToAction(actionName: "index", controllerName: "home");
            }

            //در صورت بروز خطا مجددا اطلاعات موجود نمایش داده خواهند شد
            data.TagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();
            data.InitialTags = data.Tags.ToJson();
            return View(data);
        }
    }
}


با توجه به توضیحاتی که ارائه شد، کنترلر برنامه ساختار واضح‌تری را یافته است. در اولین بار نمایش صفحه، لیست منبع داده تگ‌ها و همچنین تگ‌های مرتبط با یک مطلب (در صورت وجود) به View ارائه خواهند شد.
از همین ViewModel، در عملیات Post نیز استفاده گردیده و اطلاعات دریافت می‌گردد.
تعریف متد الحاقی ToJson مورد استفاده را نیز در ادامه ملاحظه می‌نمائید:
using System.Linq;
using System.Web.Script.Serialization;

namespace jQueryMvcSample04.Extensions
{
    public static class JsonExt
    {
        public static string ToJson(this string[] initialTags)
        {            
            if (initialTags == null || !initialTags.Any())
                return "[]";
            else
                return new JavaScriptSerializer().Serialize(initialTags);
        }
    }
}

و مرحله آخر تعریف View متناظر است

@model jQueryMvcSample04.Models.BlogPostViewModel
@{
    ViewBag.Title = "Index";
}
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>ثبت مطلب جدید</legend>
        @Html.HiddenFor(model => model.Id)
        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Body)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Body)
            @Html.ValidationMessageFor(model => model.Body)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Tags)
        </div>
        <div class="editor-field">
            <ul id="tagsList" dir="ltr" name="Tags">
            </ul>
            @Html.ValidationMessageFor(model => model.Tags)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
@section JavaScript
{
    <script type="text/javascript">
    $(document).ready(function () {
            var tagsSource = @Html.Raw(Model.TagsSource);
            $('#tagsList').tagit({
                 tagSource: tagsSource, 
                 select: true, 
                 triggerKeys: ['enter', 'comma', 'tab'],
                 initialTags:  @Html.Raw(Model.InitialTags) 
              });
});
    </script>
}
در این View دو نکته حائز اهمیت هستند:
الف) برای نمایش افزونه TagIt از یک ul با id مساوی tagsList استفاده شده است.
ب) خواص اضافی موجود در ViewModel که اطلاعات JSON ایی مورد نیاز را بازگشت می‌دهند در قسمت اسکریپت صفحه مورد استفاده قرار گرفته‌اند. در اینجا نیاز است از Html.Raw استفاده شود تا اطلاعات مرتبط با JSON اشتباها encode نشده و قابل استفاده باشند.

دریافت مثال و پروژه کامل این قسمت
jQueryMvcSample04.zip
مطالب
ساخت یک برنامه ساده‌ی جاوااسکریپتی با استفاده از الگوی MVC

در این مقاله قصد داریم با استفاده از جاوااسکریپت خالص، یک برنامه‌ی ساده را با الگوی MVC انجام دهیم. این برنامه، عملیات CRUD را پیاده سازی میکند و تنها به سه فایل index.html , script.js , style.css  نیاز دارد و از هیچ کتابخانه یا فریم ورک دیگری در آن استفاده نمیکنیم.

در الگوی MVC
  • M مخفف  Model می‌باشد و کار مدیریت داده‌ها را بر عهده دارد.
  • V مخفف View  می‌باشد و وظیفه‌ی نمایش داده‌ها به کاربر را بر عهده دارد.
  • C  مخفف  Controller می‌باشد و پل ارتباطی بین Model و  View می‌باشد و مدیریت درخواست‌ها را بر عهده دارد.
در برنامه‌ی جاری همه چیز با جاوا اسکریپت هندل میشود و فایل  index.html  فقط دارای یک المنت با آیدی مشخصی است. کد زیر ساختار فایل  index.html می‌باشد:
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>الگوی MVC در جاوااسکریپت</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <div id="root"></div>

    <script src="script.js"></script>
</body>

</html>
فایل  style.css  آن نیز دارای دستورات ساده‌ای است و یا میتوان از Normalize.css به همراه استایل دلخواه استفاده کرد و یا از فریم ورکهای مطرح دیگر استفاده نمود. کدهای فایل sytle.css آن نیز به شکل زیر خواهد بود:
*,
*::before,
*::after {
  box-sizing: border-box
}

html {
  color: #444;
}

#root {
  max-width: 450px;
  margin: 2rem auto;
  padding: 0 1rem;
}

form {
  display: flex;
  margin-bottom: 2rem;
}

[type="text"],
button {
  display: inline-block;
  -webkit-appearance: none;
  padding: .5rem 1rem;
  border: 2px solid #ccc;
  border-radius: 4px;
}

button {
  cursor: pointer;
  background: #007bff;
  color: white;
  border: 2px solid #007bff;
  margin: 0 .5rem;
}

[type="text"] {
  width: 100%;
}

[type="text"]:active,
[type="text"]:focus {
  outline: 0;
  border: 2px solid #007bff;
}

[type="checkbox"] {
  margin-right: 1rem;
}

h1 {
  color: #222;
}

ul {
  padding: 0;
}

li {
  display: flex;
  align-items: center;
  padding: 1rem;
  margin-bottom: 1rem;
  background: #f4f4f4;
  border-radius: 4px;
}

li span {
  display: inline-block;
  padding: .5rem;
  width: 250px;
  border-radius: 4px;
  border: 2px solid transparent;
}

li span:hover {
  background: rgba(179, 215, 255, 0.52);
}

li span:focus {
  outline: 0;
  border: 2px solid #007bff;
  background: rgba(179, 207, 255, 0.52)
}


فایلهای HTML و CSS را برای شروع کار آماده نمودیم و از این پس با فایل  script.js، ادامه کار را پیش می‌بریم. برای جداسازی هر قسمت از اجزای MVC، کلاسی خاص را تدارک می‌بینیم. پس سه کلاس خواهیم داشت به‌نام‌های  Model , View , Controller و در سازنده کلاس کنترلر، دو شی از View و  Model را بعنوان ورودی دریافت میکنیم. همانطور که پیش‌تر توضیح داده شد، قسمت Controller، پل ارتباطی بین View و Model می‌باشد. کد فایل  script.js را به شکل زیر تغییر میدهیم:
class Model {
    constructor() {}
}

class View {
    constructor() {}
}

class Controller {
    constructor(model, view) {
        this.model = model
        this.view = view
    }
}


const app = new Controller(new Model(), new View())

در ادامه کار در کلاس Model شروع به کدنویسی میکنیم و متدهای مد نظر را برای عملیات CRUD، در آن اضافه میکنیم. چهار تابع را به کلاس Model به‌نامهای addTodo  ، editTodo  ، deleteTodo ، toggleTodo اضافه میکنیم. در کد زیر، در بالای هر تابع، توضیحی در مورد عملکرد تابع ذکر شده است:
class Model {
    constructor() {
        // یک آرایه از اطلاعات پیش فرض 
        this.todos = [{
                id: 1,
                text: 'Run a marathon',
                complete: false
            },
            {
                id: 2,
                text: 'Plant a garden',
                complete: false
            },
        ]
    }

    // متدی برای افزودن آیتم جدید به آرایه
    addTodo(todoText) {
        const todo = {
            id: this.todos.length > 0 ? this.todos[this.todos.length - 1].id + 1 : 1,
            text: todoText,
            complete: false,
        }

        this.todos.push(todo)
    }

    // متدی برای بروزسانی آیتم مورد نظر
    editTodo(id, updatedText) {
        this.todos = this.todos.map(todo =>
            todo.id === id ? {
                id: todo.id,
                text: updatedText,
                complete: todo.complete
            } : todo
        )
    }

    // انجام میدهد filter با استفاده از متد id  تابعی که عملیات حذف را بوسیله فیلد   
    deleteTodo(id) {
        this.todos = this.todos.filter(todo => todo.id !== id)
    }

    //  متدی که در آن مشخص میکنیم کار مد نظرانجام شده یا خیر 
    toggleTodo(id) {
        this.todos = this.todos.map(todo =>
            todo.id === id ? {
                id: todo.id,
                text: todo.text, 
                complete: !todo.complete
            } : todo
        )
    }
}

میتوانیم برای تست و نحوه عملکرد آن با استفاده از شیء app،  با دستور زیر، آیتمی را به آرایه اضافه کنیم و در کنسول آن را نمایش دهیم:
app.model.addTodo('Take a nap')
console.log(app.model.todos)

در حال حاضر با هر بار reload  شدن صفحه، فقط اطلاعات پیش فرض، درون آرایه todos قرار میگیرد؛ ولی در ادامه آن را در local storage ذخیره میکنیم.

برای ساختن قسمت View، از جاوااسکریپت استفاده میکنیم و DOM را تغییر میدهیم. البته اینکار را بدون استفاده از JSX و یا یک templating language انجام خواهیم داد. قسمت‌های دیگر برنامه مانند Controller و Model نباید درگیر تغییرات DOM یا CSS یا عناصر HTML باشند و تمام این موارد توسط View هندل میشود. کد View به نحو زیر خواهد بود:

class View {
    constructor() {}

    // ایجاد یک المنت با کلاسهای استایل دلخواه
    createElement(tag, className) {
        const element = document.createElement(tag)
        if (className) element.classList.add(className)

        return element
    }

    // DOM انتخاب و گرفتن آیتمی خاص از 
    getElement(selector) {
        const element = document.querySelector(selector)

        return element
    }
}

سپس قسمت سازنده کلاس View را تغییر میدهیم و تمام المنت‌های مورد نیاز را در آن ایجاد میکنیم:

  • ارجاعی به المنتی با آی‌دی root
  • تگ h1 برای عنوان
  • یک form، input  و دکمه‌ای برای افزودن آیتمی جدید به آرایه‌ی todos
  • یک المنت ul برای نمایش آیتم‌های  todos
سپس کلاس  View به شکل زیر خواهد بود:
    constructor() {
        // root ارجاعی به المنتی با آیدی
        this.app = this.getElement('#root')

        // عنوان برنامه
        this.title = this.createElement('h1')
        this.title.textContent = 'Todos'

        // فرم ، اینپوت ورودی و دکمه
        this.form = this.createElement('form')

        this.input = this.createElement('input')
        this.input.type = 'text'
        this.input.placeholder = 'Add todo'
        this.input.name = 'todo'

        this.submitButton = this.createElement('button')
        this.submitButton.textContent = 'Submit'

        // برای نمایش عناط آرایه یا همان لیست کارها
        this.todoList = this.createElement('ul', 'todo-list')

        // افزودن اینپوت ورودی و دکمه به فرم
        this.form.append(this.input, this.submitButton)

        // ایجاد شده است app که اینجا ارجاعی به آن بنام  root اضافه کردن تمام آیتمهای بالا در المنتی با آیدی 
        this.app.append(this.title, this.form, this.todoList)
    }

در قسمت View، دو تابع هم برای getter و setter داریم که از underscore در اول نام آنها استفاده شده که نشان دهنده این است، توابع از خارج از کلاس در دسترس نیستند (شبیه private  در سی شارپ؛ البته این یک قرارداد هست یا convention)
get _todoText() {
  return this.input.value
}

_resetInput() {
  this.input.value = ''
}

در ادامه این کلاس، یک تابع دیگر هم برای نمایش آرایه داریم که هر زمان عناصر آن تغییر کردند، بتواند نمایش به‌روز اطلاعات را نشان دهد:
displayTodos(todos){
 //...
}

متد displayTodos یک المنت ul و li‌هایی را به تعداد عناصر todos ایجاد میکند و آنها را نمایش میدهد. هر زمانکه تغییراتی مانند اضافه شدن، حذف و ویرایش در todos صورت گیرد، این متد دوباره فراخوانی میشود و لیست جدید را نمایش میدهد. محتوای متد dispayTodos به شکل زیر خواهد بود:

  displayTodos(todos) {
    // حذف تمام نودها
    while (this.todoList.firstChild) {
      this.todoList.removeChild(this.todoList.firstChild)
    }

    // اگر هیچ آیتمی در آرایه نبود این پاراگراف با متن پیش فرض نمایش داده میشود
    if (todos.length === 0) {
      const p = this.createElement('p')
      p.textContent = 'Nothing to do! Add a task?'
      this.todoList.append(p)
    } else {
      // وعناصرمربوطه را ایجاد میکند liاگه درون آرایه آیتمی قرار دارد پس به ازای آن یک عنصر 
      todos.forEach(todo => {
        const li = this.createElement('li')
        li.id = todo.id

        const checkbox = this.createElement('input')
        checkbox.type = 'checkbox'
        checkbox.checked = todo.complete

        const span = this.createElement('span')
        span.contentEditable = true
        span.classList.add('editable')

        if (todo.complete) {
          const strike = this.createElement('s')
          strike.textContent = todo.text
          span.append(strike)
        } else {
          span.textContent = todo.text
        }

        const deleteButton = this.createElement('button', 'delete')
        deleteButton.textContent = 'Delete'
        li.append(checkbox, span, deleteButton)

        // نود ایجاد شده به لیست اضافه میکند
        this.todoList.append(li)
      })
    }

    // برای خطایابی و نمایش در کنسول
    console.log(todos)
  }


در نهایت قسمت Controller را که پل ارتباطی بین View و Model می‌باشد، کامل میکنیم. اولین تغییراتی که در کلاس Controller ایجاد میکنیم، استفاده از متد displayTodos در سازنده‌ی این کلاس می‌باشد و با هر بار تغییر این متد، دوباره فراخوانی میشود:

class Controller {
  constructor(model, view) {
    this.model = model
    this.view = view

    // نمایش اطلاعات پیش فرض
    this.onTodoListChanged(this.model.todos)
  }

  onTodoListChanged = todos => {
    this.view.displayTodos(todos)
  }
}


چهار تابعی را که در قسمت Model ایجاد نمودیم و کار ویرایش، حذف، افزودن و اتمام کار را انجام میدادند، در کلاس کنترلر آنها را هندل میکنیم و زمانیکه کاربر دکمه‌ای را برای افزودن یا تیک حذف آیتمی، زد، تابع مربوطه توسط کنترلر در Model فراخوانی شود:

handleAddTodo = todoText => {
  this.model.addTodo(todoText)
}

handleEditTodo = (id, todoText) => {
  this.model.editTodo(id, todoText)
}

handleDeleteTodo = id => {
  this.model.deleteTodo(id)
}

handleToggleTodo = id => {
  this.model.toggleTodo(id)
}

چون کنترلر نمیتواند بصورت مستقیم فراخوانی شود و این توابع باید درون DOM تنظیم شوند تا به ازای رخدادهایی همچون click و change، فراخوانی شوند. پس از این توابع در قسمت View استفاده میکنیم و به کلاس View، موارد زیر را اضافه میکنیم:

bindAddTodo(handler) {
  this.form.addEventListener('submit', event => {
    event.preventDefault()

    if (this._todoText) {
      handler(this._todoText)
      this._resetInput()
    }
  })
}

bindDeleteTodo(handler) {
  this.todoList.addEventListener('click', event => {
    if (event.target.className === 'delete') {
      const id = parseInt(event.target.parentElement.id)

      handler(id)
    }
  })
}

bindToggleTodo(handler) {
  this.todoList.addEventListener('change', event => {
    if (event.target.type === 'checkbox') {
      const id = parseInt(event.target.parentElement.id)

      handler(id)
    }
  })
}


برای bind کردن این متدها در کلاس Controller، کدهای زیر را اضافه میکنیم:

this.view.bindAddTodo(this.handleAddTodo)
this.view.bindDeleteTodo(this.handleDeleteTodo)
this.view.bindToggleTodo(this.handleToggleTodo)


برای ذخیره اطلاعات در local storage، در سازنده کلاس Model، کد زیر را اضافه میکنیم:

 this.todos = JSON.parse(localStorage.getItem('todos')) || []

متد دیگری هم در کلاس Model برای به‌روز رسانی مقادیر local storage قرار میدهیم:

_commit(todos) {
  this.onTodoListChanged(todos)
  localStorage.setItem('todos', JSON.stringify(todos))
}

متدی هم برای تغییراتی که هر زمان بر روی todos  اتفاق می‌افتد، فراخوانی شود:

deleteTodo(id) {
  this.todos = this.todos.filter(todo => todo.id !== id)

  this._commit(this.todos)
}
در پایان میتوانید سورس کد مقاله جاری را از اینجا دانلود نمایید.
این مقاله صرفا جهت آشنایی و نمونه کدی از پیاده سازی الگوی  MVC  در جاوااسکریپت می‌باشد.
مطالب
آشنایی با Refactoring - قسمت 2

قسمت دوم آشنایی با Refactoring به معرفی روش «استخراج متدها» اختصاص دارد. این نوع Refactoring بسیار ساده بوده و مزایای بسیاری را به همراه دارد؛ منجمله:
- بالا بردن خوانایی کد؛ از این جهت که منطق طولانی یک متد به متدهای کوچکتری با نام‌های مفهوم شکسته می‌شود.
- به این ترتیب نیاز به مستند سازی کدها نیز بسیار کاهش خواهد یافت. بنابراین در یک متد، هر جایی که نیاز به نوشتن کامنت وجود داشت، یعنی باید همینجا آن‌ قسمت را جدا کرده و در متد دیگری که نام آن، همان خلاصه کامنت مورد نظر است، قرار داد.
- این نوع جدا سازی منطق‌های پیاده سازی قسمت‌های مختلف یک متد، در آینده نگهداری کد نهایی را نیز ساده‌تر کرده و انجام تغییرات بر روی آن را نیز تسهیل می‌بخشد؛ زیرا اینبار بجای هراس از دستکاری یک متد طولانی، با چند متد کوچک و مشخص سروکار داریم.

برای نمونه به مثال زیر دقت کنید:
using System.Collections.Generic;

namespace Refactoring.Day2.ExtractMethod.Before
{
public class Receipt
{
private IList<decimal> Discounts { get; set; }
private IList<decimal> ItemTotals { get; set; }

public decimal CalculateGrandTotal()
{
// Calculate SubTotal
decimal subTotal = 0m;
foreach (decimal itemTotal in ItemTotals)
subTotal += itemTotal;

// Calculate Discounts
if (Discounts.Count > 0)
{
foreach (decimal discount in Discounts)
subTotal -= discount;
}

// Calculate Tax
decimal tax = subTotal * 0.065m;
subTotal += tax;

return subTotal;
}
}
}

همانطور که از کامنت‌های داخل متد CalculateGrandTotal مشخص است، این متد سه کار مختلف را انجام می‌دهد؛ جمع اعداد، اعمال تخفیف، اعمال مالیات و نهایتا یک نتیجه را باز می‌گرداند. بنابراین بهتر است هر عمل را به یک متد جداگانه و مشخص منتقل کرده و کامنت‌های ذکر شده را نیز حذف کنیم. نام یک متد باید به اندازه‌ی کافی مشخص و مفهوم باشد و آنچنان نیازی به مستندات خاصی نداشته باشد:

using System.Collections.Generic;

namespace Refactoring.Day2.ExtractMethod.After
{
public class Receipt
{
private IList<decimal> Discounts { get; set; }
private IList<decimal> ItemTotals { get; set; }

public decimal CalculateGrandTotal()
{
decimal subTotal = CalculateSubTotal();
subTotal = CalculateDiscounts(subTotal);
subTotal = CalculateTax(subTotal);
return subTotal;
}

private decimal CalculateTax(decimal subTotal)
{
decimal tax = subTotal * 0.065m;
subTotal += tax;
return subTotal;
}

private decimal CalculateDiscounts(decimal subTotal)
{
if (Discounts.Count > 0)
{
foreach (decimal discount in Discounts)
subTotal -= discount;
}
return subTotal;
}

private decimal CalculateSubTotal()
{
decimal subTotal = 0m;
foreach (decimal itemTotal in ItemTotals)
subTotal += itemTotal;
return subTotal;
}
}
}

بهتر شد! عملکرد کد نهایی، تغییری نکرده اما کیفیت کد ما بهبود یافته است (همان مفهوم و معنای Refactoring). خوانایی کد افزایش یافته است. نیاز به کامنت نویسی به شدت کاهش پیدا کرده و از همه مهم‌تر، اعمال مختلف، در متدهای خاص آن‌ها قرار گرفته‌اند.
به همین جهت اگر حین کد نویسی، به یک متد طولانی برخوردید (این مورد بسیار شایع است)، در ابتدا حداقل کاری را که جهت بهبود کیفیت آن می‌توانید انجام دهید، «استخراج متدها» است.

ابزارهای کمکی جهت پیاده سازی روش «استخراج متدها»:

- ابزار Refactoring توکار ویژوال استودیو پس از انتخاب یک قطعه کد و سپس کلیک راست و انتخاب گزینه‌ی Refactor->Extract method، این عملیات را به خوبی می‌تواند مدیریت کند و در وقت شما صرفه جویی خواهد کرد.
- افزونه‌های ReSharper و همچنین CodeRush نیز چنین قابلیتی را ارائه می‌دهند؛ البته توانمندی‌های آن‌ها از ابزار توکار یاد شده بیشتر است. برای مثال اگر در میانه کد شما جایی return وجود داشته باشد، گزینه‌ی Extract method ویژوال استودیو کار نخواهد کرد. اما سایر ابزارهای یاده شده به خوبی از پس این موارد و سایر موارد پیشرفته‌تر بر می‌آیند.

نتیجه گیری:
نوشتن کامنت، داخل بدنه‌ی یک متد مزموم است؛ حداقل به دو دلیل:
- ابزارهای خودکار مستند سازی از روی کامنت‌های نوشته شده، از این نوع کامنت‌ها صرفنظر خواهند کرد و در کتابخانه‌ی شما مدفون خواهند شد (یک کار بی‌حاصل).
- وجود کامنت در داخل بدنه‌ی یک متد، نمود آشکار ضعف شما در کپسوله سازی منطق مرتبط با آن قسمت است.


و ... «لطفا» این نوع پیاده سازی‌ها را خارج از فایل code behind هر نوع برنامه‌ی winform/wpf/asp.net و غیره قرار دهید. تا حد امکان سعی کنید این مکان‌ها، استفاده کننده‌ی «نهایی» منطق‌های پیاده سازی شده توسط کلاس‌های دیگر باشند؛ نه اینکه خودشان محل اصلی قرارگیری و ابتدای تعریف منطق‌های مورد نیاز قسمت‌های مختلف همان فرم مورد نظر باشند. «لطفا» یک فرم درست نکنید با 3000 سطر کد که در قسمت code behind آن قرار گرفته‌اند. code behind را محل «نهایی» ارائه کار قرار دهید؛ نه نقطه‌ی آغاز تعریف منطق‌های پیاده سازی کار. این برنامه نویسی چندلایه که از آن صحبت می‌شود، فقط مرتبط با کار با بانک‌های اطلاعاتی نیست. در همین مثال، کدهای فرم برنامه، باید نقطه‌ی نهایی نمایش عملیات محاسبه مالیات باشند؛ نه اینکه همانجا دوستانه یک قسمت مالیات حساب شود، یک قسمت تخفیف، یک قسمت جمع بزند، همانجا هم نمایش بدهد! بعد از یک هفته می‌بینید که code behind فرم در حال انفجار است! شده 3000 سطر! بعد هم سؤال می‌پرسید که چرا اینقدر میل به «بازنویسی» سیستم این اطراف زیاد است! برنامه نویس حاضر است کل کار را از صفر بنویسد، بجای اینکه با این شاهکار بخواهد سرو کله بزند! هر چند یکی از روش‌های برخورد با این نوع کدها جهت کاهش هراس نگهداری آن‌ها، شروع به Refactoring است.

مطالب
آموزش فایرباگ - #7 - CSS Panel
پنل CSS مانند پنل جانبیِ Style که در مقاله‌ی قبل بررسی کردیم است با این تفاوت که امکانات بیشتری برای افرادی که قصد تصریف استایل دارند محیا کرده است.
در این پنل می‌توان به اضافه ، ویرایش و حذف استایل هایی که به صفحه‌ی جاری توسط فایل‌های مختلف اضافه شده اند و یا داخل خود صفحه تعریف شده اند پرداخت.



همچنین در این پنل امکان ویرایش یک فایل css بصورت کامل وجود دارد ، به این صورت که می‌توانید تمام محتویات فایل مورد نظر را در یک Text area ویرایش کنید.

مطابق با روالی که در قسمت قبل پیش گرفتیم عمل می‌کنیم و به ترتیب به تشریح ابزار پنل ، خود پنل ، منوی راست کلیک و پنل جانبی ِ Elements می‌پردازیم.


Options Menu
با راست کلیک کردن بروی تب CSS و یا کلیک کردن بروی فلش کوچک روی تب CSS قابل دسترس است و امکانات زیر را محیا می‌کند:

  • Expand Shorthand Properties : نمایش دستورات css بصورت کامل ، به این معنی که دستوراتی مانند margin که هم بصورت خلاصه و هم بصورت کامل تعریف می‌شوند را بصورت کامل نمایش می‌دهد. مثلا margin را چهار مقدار margin-top , margin-right , margin-bottom , margin-left نمایش می‌دهد.

  • Color As Hex , Color As RGB , Color As HSL : با انتخاب یکی از سه مقدار ذکر شده ، فرمت نمایش رنگ‌ها به حالت انتخاب شده تغییر می‌کند.
    مثلا دستور color : #000000 به این صورت نمایش داده می‌شود :‭ color : rgb(0, 0, 0) 

  • Refresh : این گزینه محتویات پنل را بروز می‌کند.

Panel Toolbar
ابزاری موجود در این پنل مشابه پنل‌های دیگر در بالای پنل و در زیر تب پنل‌ها قرار دارد و شامل ابزارهای زیر می‌شود:

  • Edit Button : این ابزار به دو صورت Live Edit و Source Edit در دسترس هست که با کلیک بروی فلش کوچکی که در سمت راست دکمه قرار دارد می‌توان نوع آن را تغییر داد.
    انتخاب حالت Source Edit ، باعث می‌شود سورس فایل به همان صورتی که به مرورگر ارسال شده نمایش داده شود. ( کامنت‌ها ، فرمت فایل و ... حفظ می‌شود. )
    حالت Live Edit هم باعث نمایش محتویات فایل بصورت مرتب شده می‌شود. ( کامنت‌ها و دستوراتی که توسط مرورگر تفسیر نشده اند نمایش داده نمی‌شوند. )



  • CSS Location Menu : نام فایل جاری که در پنل در حال نمایش است را نمایش می‌دهد و همچنین با کلیک بروی آن ، فایل‌های استایل دیگری که در صفحه بارگزاری شده اند را نمایش می‌دهد. با کلیک بروی هرکدام از فایل‌های نمایش داده شده ، همان فایل در پنل باز می‌شود.

    استایل هایی که در خود صفحه تعریف شده اند با نام خود صفحه در این قسمت نمایش داده می‌شوند و اگر استایل‌های در چندین تگ style تعریف شده باشند ، دومین تگ با #2 ، سومین با #3 و ... مشخص می‌شوند.

    هنگام باز بودن :
    1- فایل هایی که از پوشه‌های مختلف در صفحه بارگذاری شده اند ، با نام پوشه از هم تفکیک شده اند:



    2- با تایپ کردن عبارت دلخواه می‌توان نتایج را محدود کرد:



Panel
منظور از پنل ، قسمتی هست که استایل بصورت فرمت شده و نمایش داده شده است.


 Infotips
توسط این قابلیت ، زمانی که موس را بروی آدرس تصاویر ، نوع فونت و ... ببرید ، پاپ آپ کوچکی باز می‌شود و اطلاعاتی در مورد مقادیر می‌دهد. مثلا اینکه آیا تصویر مورد نظر به درستی بارگذاری شده یا نه ، نمای کوچکی از تصویر ، شکل فونت و ...

 Editing rules
برای ویرایش تعاریف CSS شامل Selector‌ها ، دستورات و مقادیر ، این پنل ابزارهای مفیدی ارائه می‌کند.
برای ویرایش یک selector ، دستور یا مقدار آن بروی آن عبارت کلیک کنید ، در همین حال یک text box ظاهر می‌شود و می‌توانید مقدار جدید را وارد کنید. پس از انجام ویرایش مورد نظر بروی قسمتی از صفحه کلیک کنید یا کلید Tab سپس Esc را بزنید. برای ویرایش دستورات بعدی ، کلید Tab را بزنید و برای لغو تغییر جاری ، کلید Esc را بزنید.

برای ایجاد یک rule جدید ، بروی قسمتی از پنل راست کلیک کرده و سپس گزینه‌ی "New Rule..." را برگزینید.
برای ایجاد یک property هم چند راه وجود دارد:

  • بروی قسمت از فضای خالی تعریف یک استایل دوبار کلیک کنید.
  • در قسمتی از تعریف یک استایل راست کلیک کرده و گزینه‌ی "New Property..." را انتخاب کنید.
  • بروی مقدار آخرین property کلیک کرده و کلید Tab را بزنید.


هنگام ویرایش یا ایجاد یک Rule , Propery یا مقدار بصورت inline ، حاشیه‌ی text box متناسب با مقدار وارد شده رنگی را که نمایانگر حالت ذخیره‌ی مقدار وارد شده است نمایش می‌دهد. برای مثال اگر مقداری که برای یک selector وارد شده است نامعتبر باشد ، رنگ حاشیه‌ی text box قرمز می‌شود.
جدول زیر حالت‌های مختلف را شرح می‌دهد:

رنگ حاشیه
Selectorها
نام Properyها
مقادیر Propertyها و Ruleها
 خاکستری تغییری ایجاد نشده استتغییری ایجاد نشده است
تغییری ایجاد نشده است
 قرمز selector نامعتبر است
نام نامعتبر است
مقدار نامعتبر است
 زرد selector صحیح است اما بروی Element فعلی تاثیر ندارد
نام صحیح است اما مقدار Property غیر مجاز است یا وارد نشده است
 
 سبز selector صحیح است نام و مقدار صحیح هستند
مقدار صحیح است


Auto-completion
زمانی که در حال ایجاد/ویراش کردن یک Rule, Propery یا مقدار آنها هستید ، میتوانید از این قابلیت استفاده کنید. مثلا با وارد کردن # تمام Selector هایی که می‌توانند با # شروع شوند در دسترس شما هستند و با دکمه‌های Up/Down می‌توانید مقادیر ممکن را مرور کنید.
اگر در حال ویرایش یک مقدار عددی هستید ، می‌توانید با دکمه‌های Up/Down مقادیر را یک واحد یک واحد افزایش دهید. با نگه داشتن کلید Shift و فشردن Up/Down می‌توانید مقادیر را 10تا 10تا تغییر دهید و با نگه داشتن Ctrl بجای Sihft ، یک دهم یک دهم.

Toggling styles
با این امکان می‌توانید یک Property را بطور موقت فعال/غیرفعال کنید. با حرکت موس از یک Property یک آیکون قرمز رنگ در کنار آن نمایش داده می‌شود که با کلیک بروی آن ، Property و مقدار آن کمرنگ شده و آیکون قرمز رنگ کنار آن ثابت می‌شود. با کلیک مجدد بروی آن ، Property فعال می‌شود.



Context Menu
این منو زمانی که در پنل راست کلیک کنید ظاهر می‌شود و نسبت به منطقه (Context)ای که در آن راست کلیک کرده اید ، گزینه‌های متفاوتی را مشاهده خواهید کرد. در جدول زیر ، گزینه‌ها ، Contextشان و توضیح هر گزینه آمده است.

 گزینهContext
توضیحات
Copy Location
CSS Location Menuآدرس فایل استایل را در حافظه کپی می‌کند.
Open in New TabCSS Location Menuفایل استایل را در یک تب جدید باز می‌کند.
Copy Image Location
image valuesآدرس تصویر را در حافظه کپی می‌کند.
Open Image in New Tabimage valuesتصویر را در یک تب جدید باز می‌کند.
Copy Colorcolor valuesمقدار رنگ را در حافظه کپی می‌کند.
Copy Rule DeclarationCSS selectorSelector و Propertyها را در حافظه کپی می‌کند.
Copy Style DeclarationCSS selectorفقط Propertyها را در حافظه کپی می‌کند.
New Rule...
همه جای پنل
یک Rule جدید بالای قسمتی که راست کلیک شده ایجاد می‌کند.
Delete "<selector>"
CSS selectorRule را حذف می‌کند.
New Property...
CSS rule
یک Property جدید در Ruleی که در آن راست کلیک شده ایجاد می‌کند.
Edit "<property name>"...
CSS property
Property فعلی به حالت ویرایش درمی آید. ( راه ساده‌تر ، کلیک بروی Property است. )
Delete "<property name>"...CSS propertyProperty فعلی را حذف می‌کند.
Disable "<property name>"...
CSS property
Property فعلی را غیرفعال می‌کند.
Refreshهمه جای پنل
محتویات پنل را بروز رسانی می‌کند.
Inspect in DOM panel
CSS Location Menu, CSS rule
فایل استایل یا Rule را در پنل DOM باز می‌کند.


Elements Side Panel
در این پنل که سمت راست پنل CSS قرار دارد ، با وارد کردن یک CSS Selector می‌توانید Elementهایی که در صفحه با آن مطابقت دارند را مشاهده کنید.
برای وارد کردن CSS Selector هم می‌توان مقدار مورد نظر را در قسمت Try a selector... وارد کرد هم می‌توان بروی یکی از Selectorهای پنل راست کلیک کرد و گزینه‌ی Get Matching Elements را برگزید.
Context Menu این قسمت هم مشابه Context Menu پنل HTML هست و فقط در ورژن 1.11 گزینه‌ی Paste HTML اضافه شده که در این کامنت از مقاله‌ی آموزش فایرباگ - #5 - HTML Panel توضیح داده شده است.

مطالب
آشنایی با ساختار IIS قسمت نهم
در قسمت قبلی  ما یک هندلر ایجاد کردیم و درخواست‌هایی را که برای فایل jpg و به صورت GET ارسال میشد، هندل می‌کردیم و تگی را در گوشه‌ی تصویر درج و آن را در خروجی نمایش میدادیم. در این مقاله قصد داریم که کمی هندلر مورد نظر را توسعه دهیم و برای آن یک UI یا یک رابط کاربری ایجاد نماییم. برای توسعه دادن ماژولها و هندلر‌ها ما یک dll نوشته و باید آن را در GAC که مخفف عبارت Global Assembly Cache ریجستر کنیم.


جهت اینکار یک پروژه از نوع class library ایجاد کنید. فایل class1.cs را که به طور پیش فرض ایجاد می‌شود، حذف کنید و رفرنس‌های Microsoft.Web.Management.dll و Microsoft.Web.Administration.dll را از مسیر زیر اضافه کنید:
\Windows\system32\inetsrv
اولین رفرنس شامل کلاس‌هایی است که جهت ساخت ماژول‌ها برای کنسول IIS مورد نیاز است و دومی هم برای خواندن پیکربندی‌های نوشته شده مورد استفاده قرار می‌گیرد.
برای طراحی UI  بر پایه winform باید رفرنس‌های System.Windows.Forms.dll و System.Web.dll را از سری اسمبلی‌های دات نت نیز اضافه کنیم و در مرحله‌ی بعدی جهت ایجاد امضاء یا strong name (^  و  ^  ) به خاطر ثبت در GAC پروژه را انتخاب و وارد Properties پروژه شوید. در تب signing گزینه sign the assembly را تیک زده و در لیست باز شده گزینه new را انتخاب نمایید و نام  imageCopyrightUI را به آن نسبت داده و گزینه تعیین کلمه عبور را غیرفعال کنید و تایید و تمام. الان باید یک فایل snk مخفف strong name key ایجاد شده باشد تا بعدا با استفاده از این کلید dll ایجاد شده را در GAC ریجستر کنیم.

در مرحله بعدی در تب Build Events کد زیر را در بخش Post-build event command line اضافه کنید. این کد باعث می‌شود بعد از هر بار کامپایل پروژه، به طور خودکار در GAC ثبت شود:

call "%VS80COMNTOOLS%\vsvars32.bat" > NULL 
gacutil.exe /if "$(TargetPath)"

نکته:در صورتی که از VS2005 استفاده می‌کنید در تب Debug در قسمت Start External Program مسیر زیر را قرار بدهید. اینکار برای تست و دیباگینگ پروژه به شما کمک خواهد کرد. این تنظیم شامل نسخه‌های اکسپرس نمی‌شود.
 \windows\system32\inetsrv\inetmgr.exe

بعد از پایان اینکار پروژه را Rebuild کنید. با اینکار dll در GAC ثبت می‌شود. استفاده از سوییچ‌های if به طور همزمان در درستور gacutil به معنی این هست که اگر اولین بار است نصب می‌شود، پس با سوییچ i نصب کن. ولی اگر قبلا نصب شده است نسخه جدید را به هر صورتی هست جایگزین قبلی کن یا همان reinstall کن.
 
ساخت یک Module Provider
رابط‌های کاربری IIS همانند هسته و کل سیستمش، ماژولار و قابل خصوصی سازی است. رابط کاربری، مجموعه‌ای از ماژول هایی است که میتوان آن‌ها را حذف یا جایگزین کرد. تگ ورودی یا معرفی برای هر UI یک module provider است. خیلی خودمانی، تگ ماژول پروایدر به معرفی یک UI در IIS می‌پردازد. لیستی از module provider‌ها را می‌توان در فایل زیر در تگ بخش <modules> پیدا کرد.
%windir%\system32\inetsrv\Administration.config

در اولین گام یک کلاس را به اسم imageCopyrightUIModuleProvider.cs ایجاد کرده و سپس آن‌را به کد زیر، تغییر می‌دهیم. کد زیر با استفاده از ModuleDefinition  یک نام به تگ Module Provider داده و کلاس imageCopyrightUI را که بعدا تعریف می‌کنیم، به عنوان مدخل entry رابط کاربری معرفی کرده:

using System;
using System.Security;
using Microsoft.Web.Management.Server;
    
namespace IIS7Demos           
{
    class imageCopyrightUIProvider : ModuleProvider
    {
        public override Type ServiceType              
        {
            get { return null; }
        }

        public override ModuleDefinition GetModuleDefinition(IManagementContext context)
        {
            return new ModuleDefinition(Name, typeof(imageCopyrightUI).AssemblyQualifiedName);
        }

        public override bool SupportsScope(ManagementScope scope)
        {
            return true;
        }
    }            
}

با ارث بری از کلاس module provider، سه متد بازنویسی می‌شوند که یکی از آن ها SupportsScope هست که میدان عمل پروایدر را مشخص می‌کند، مانند اینکه این پرواید در چه میدانی باید کار کند که می‌تواند سه گزینه‌ی server,site,application باشد. در کد زیر مثلا میدان عمل application انتخاب شده است ولی در کد بالا با برگشت مستقیم true، همه‌ی میدان را جهت پشتیبانی از این پروایدر اعلام کردیم.

 public override bool SupportsScope(ManagementScope scope)
        {
            return (scope == ManagementScope.Application) ;
        }

حالا که پروایدر (معرف رابط کاربری به IIS) تامین شده، نیاز است قلب کار یعنی ماژول معرفی گردد. اصلی‌ترین متدی که باید از اینترفیس ماژول پیاده سازی شود متد initialize است. این متد جایی است که تمام عملیات در آن رخ می‌دهد. در کلاس زیر imageCopyrightUI ما به معرفی مدخل entry رابط کاربری می‌پردازیم. در سازنده‌های این متد، پارامترهای نام، صفحه رابط کاربری وتوضیحی در مورد آن است. تصویر کوچک و بزرگ جهت آیکن سازی (در صورت عدم تعریف آیکن، چرخ دنده نمایش داده می‌شود) و توصیف‌های بلندتر را نیز شامل می‌شود.

 internal class imageCopyrightUI : Module
    {
        protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo)
        {            
            base.Initialize(serviceProvider, moduleInfo);
            IControlPanel controlPanel = (IControlPanel)GetService(typeof(IControlPanel));
            ModulePageInfo modulePageInfo = new ModulePageInfo(this, typeof(imageCopyrightUIPage), "Image Copyright", "Image Copyright",Resource1.Visual_Studio_2012,Resource1.Visual_Studio_2012);
            controlPanel.RegisterPage(modulePageInfo);
        }
    }

شیء ControlPanel مکانی است که قرار است آیکن ماژول نمایش داده شود. شکل زیر به خوبی نام همه قسمت‌ها را بر اساس نام کلاس و اینترفیس آن‌ها دسته بندی کرده است:

پس با تعریف این کلاس جدید ما روی صفحه‌ی کنترل پنل IIS، یک آیکن ساخته و صفحه‌ی رابط کاربری را به نام imageCopyrightUIPage، در آن ریجستر می‌کنیم. این کلاس را پایینتر شرح داده‌ایم. ولی قبل از آن اجازه بدهید تا انواع کلاس هایی را که برای ساخت صفحه کاربرد دارند، بررسی نماییم. در این مثال ما با استفاده از پایه‌ای‌ترین کلاس، ساده‌ترین نوع صفحه ممکن را خواهیم ساخت. 4 کلاس برای ساخت یک صفحه وجود دارند که بسته به سناریوی کاری، شما یکی را انتخاب می‌کنید.

 ModulePage   شامل اساسی‌ترین متدها و سورس‌ها شده و هیچگونه رابط کاری ویژه‌ای را در اختیار شما قرار نمی‌دهد. تنها یک صفحه‌ی خام به شما می‌دهد که می‌توانید از آن استفاده کرده یا حتی با ارث بری از آن، کلاس‌های جدیدتری را برای ساخت صفحات مختلف و ویژه‌تر بسازید. در حال حاضر که هیچ کدام از ویژگی‌های IIS فعلی از این کلاس برای ساخت رابط کاربری استفاده نکرده‌اند.
 ModuleDialogPage   یک صفحه شبیه به دیالوگ را ایجاد می‌کند و شامل دکمه‌های Apply و Cancel میشود به همراه یک سری متدهای اضافی‌تر که اجازه‌ی override کردن آنها را دارید. همچنین یک سری از کارهایی چون refresh  و از این دست عملیات خودکار را نیز انجام میدهد. از نمونه رابط‌هایی که از این صفحات استفاده می‌کنند میتوان  machine key و management service را اسم برد.
 ModulePropertiesPage   این صفحه یک رابط کاربری را شبیه پنجره property که در ویژوال استادیو وجود دارد، در دسترس شما قرار میدهد. تمام عناصر آن در یک حالت گرید grid لیست می‌شوند. از نمونه‌های موجود میتوان به CGI,ASP.Net Compilation اشاره کرد.
 ModuleListPage   این کلاس برای مواقعی کاربرد دارد که شما قرار است لیستی از آیتم‌ها را نشان دهید. در این صفحه شما یک ListView دارید که میتوانید عملیات جست و جو، گروه بندی و نحوه‌ی نمایش لیست را روی آن اعمال کنید.
در این مثال ما از اولین کلاس نامبرده که پایه‌ی همه کلاس هاست استفاده می‌کنیم. کد زیر را در کلاسی به اسم imageCopyrightUIPage  می‌نویسیم:
    public sealed class imageCopyrightUIPage : ModulePage
    {
        public string message;
        public bool featureenabled;
        public string color;

        ComboBox _colCombo = new ComboBox();
        TextBox _msgTB = new TextBox();
        CheckBox _enabledCB = new CheckBox();

        public imageCopyrightUIPage()
        {
            this.Initialize();
        }


        void Initialize()
        {
            Label crlabel = new Label();
            crlabel.Left = 50;
            crlabel.Top = 100;
            crlabel.AutoSize = true;
            crlabel.Text = "Enable Image Copyright:";
            _enabledCB.Text = "";
            _enabledCB.Left = 200;
            _enabledCB.Top = 100;
            _enabledCB.AutoSize = true;

            Label msglabel = new Label();
            msglabel.Left = 150;
            msglabel.Top = 130;
            msglabel.AutoSize = true;
            msglabel.Text = "Message:";
            _msgTB.Left = 200;
            _msgTB.Top = 130;
            _msgTB.Width = 200;
            _msgTB.Height = 50;

            Label collabel = new Label();
            collabel.Left = 160;
            collabel.Top = 160;
            collabel.AutoSize = true;
            collabel.Text = "Color:";
            _colCombo.Left = 200;
            _colCombo.Top = 160;
            _colCombo.Width = 50;
            _colCombo.Height = 90;
            _colCombo.Items.Add((object)"Yellow");
            _colCombo.Items.Add((object)"Blue");
            _colCombo.Items.Add((object)"Red");
            _colCombo.Items.Add((object)"White");

            Button apply = new Button();
            apply.Text = "Apply";
            apply.Click += new EventHandler(this.applyClick);
            apply.Left = 200;
            apply.AutoSize = true;
            apply.Top = 250;

            Controls.Add(crlabel);
            Controls.Add(_enabledCB);
            Controls.Add(collabel);
            Controls.Add(_colCombo);
            Controls.Add(msglabel);
            Controls.Add(_msgTB);
            Controls.Add(apply);
        }

        public void ReadConfig()
        {
            try
            {
                ServerManager mgr;
                ConfigurationSection section;
                mgr = new ServerManager();
                Configuration config =
                mgr.GetWebConfiguration(
                       Connection.ConfigurationPath.SiteName,
                       Connection.ConfigurationPath.ApplicationPath +
                       Connection.ConfigurationPath.FolderPath);

                section = config.GetSection("system.webServer/imageCopyright");
                color = (string)section.GetAttribute("color").Value;
                message = (string)section.GetAttribute("message").Value;
                featureenabled = (bool)section.GetAttribute("enabled").Value;

            }

            catch
            { }

        }
      
        void UpdateUI()
        {
            _enabledCB.Checked = featureenabled;
            int n = _colCombo.FindString(color, 0);
            _colCombo.SelectedIndex = n;
            _msgTB.Text = message;
        }


        protected override void OnActivated(bool initialActivation)
        {
            base.OnActivated(initialActivation);
            if (initialActivation)
            {
                ReadConfig();
                UpdateUI();
            }
        }



        private void applyClick(Object sender, EventArgs e)
        {
            try
            {
                UpdateVariables();
                ServerManager mgr;
                ConfigurationSection section;
                mgr = new ServerManager();
                Configuration config =
                mgr.GetWebConfiguration
                (
                       Connection.ConfigurationPath.SiteName,
                       Connection.ConfigurationPath.ApplicationPath +
                       Connection.ConfigurationPath.FolderPath
                );

                section = config.GetSection("system.webServer/imageCopyright");
                section.GetAttribute("color").Value = (object)color;
                section.GetAttribute("message").Value = (object)message;
                section.GetAttribute("enabled").Value = (object)featureenabled;

                mgr.CommitChanges();

            }

            catch
            { }

        }

        public void UpdateVariables()
        {
            featureenabled = _enabledCB.Checked;
            color = _colCombo.Text;
            message = _msgTB.Text;
        }
    }
اولین چیزی که در کلاس بالا صدا زده می‌شود، سازنده‌ی کلاس هست که ما در آن یک تابع تعریف کردیم به اسم initialize که به آماده سازی اینترفیس یا رابط کاربری می‌پردازد و کنترل‌ها را روی صفحه می‌چیند. این سه کنترل، یکی Combox برای تعیین رنگ، یک Checkbox برای فعال بودن ماژول و دیگری هم یک textbox جهت نوشتن متن است. مابقی هم که سه label برای نامگذاری اشیاست. بعد از اینکه کنترل‌ها روی صفحه درج شدند، لازم است که تنظیمات پیش فرض یا قبلی روی کنترل‌ها نمایش یابند که اینکار را به وسیله تابع readConfig انجام می‌دهیم و تنظیمات خوانده شده را در متغیر‌های عمومی قرار داده و با استفاده از تابع UpdateUI این اطلاعات را روی کنترل‌ها ست می‌کنیم و به این ترتیب UI به روز می‌شود. این دو تابع را به ترتیب پشت سر هم در یک متد به اسم OnActivated  که override کرده‌ایم صدا میزنیم. در واقع این متد یک جورایی همانند رویداد Load می‌باشد؛ اگر true برگرداند اولین فعال سازی رابط کاربری بعد از باز شدن IIS است و در غیر این صورت false بر میگرداند.

در صورتی که کاربر مقادیر را تغییر دهد و روی گزینه apply کلیک کند تابع applyClick اجرا شده و ابتدا به تابع UpdateVariables ارجاع داده میشود که در آن مقادیر خوانده شده و در متغیرهای Global قرار می‌گیرند و سپس با استفاده از دو شیء از نوع serverManger و ConfigSection جایگذاری یا ذخیره می‌شوند.
استفاده از دو کلاس Servermanager و Configsection در دو قسمت خواندن و نوشتن مقادیر به کار رفته‌اند. کلاس servermanager به ما اجازه دسترسی به تنظیمات IIS و قابلیت‌های آن را میدهد. در تابع ReadConfig مسیر وب سایتی را که در لیست IIS انتخاب شده است، دریافت کرده و به وب کانفیگ آن وب سایت رجوع نموده و تگ imageCopyright آن را که در تگ system.webserver قرار گرفته است، میخواند (در صورتی که این تگ در آن وب کانفیگ موجود نباشد، خواندن و سپس ذخیره مجدد آن روی تگ داخل فایل applicationHost.config اتفاق میفتد که نتیجتا برای همه‌ی وب سایت هایی که این تگ را ندارند یا مقدارهای پیش فرض آن را تغییر نداده‌اند رخ میدهد) عملیات نوشتن هم مشابه خواندن است. تنها باید خط زیر را در آخر برای اعمال تغییرات نوشت؛ مثل EF با گزینه Context.SaveChanges:
mgr.CommitChanges();
وقت آن است که رابط کاربری را به IIS اضافه کنیم: پروژه را Rebuild کنید. بعد از آن با خطوطی که قبلا در Post-Build Command نوشتیم باید dll ما در GAC ریجستر شود. برای همین آدرس زیر را در cmd تایپ کنید:
%vs110comntools%\vsvars32.bat
عبارت اول که مسیر ویژوال استودیوی  شماست و عدد 110 یعنی نسخه‌ی 11. هر نسخه‌ای را که استفاده میکنید، یک صفر جلویش بگذارید و جایگزین عدد بالا کنید. مثلا نسخه 8 می‌شود 80 و فایل بچ بالا هم دستورات visual studio را برای شما آزاد می‌کند.
سپس دستور زیر را وارد کنید:
GACUTIL /l ClassLibrary1
کلمه classLibrary1 نام پروژه‌ی ما بود که در GAC ریجستر شده است. با سوییچ l تمامی اطلاعات اسمبلی‌هایی که در GAC ریجستر شده‌اند، نمایش می‌یابند. ولی اگر اسم آن اسمبلی را جلویش بنویسید، فقط اطلاعات آن اسمبلی نمایش میابد. با اجرای خط فوق میتوانیم کلید عمومی public key اسمبلی خود را بدانیم که در شکل زیر مشخص شده است:

پس اگر کلید را دریافت کرده‌اید، خط زیر را به فایل administration.config در تگ <ModuleProviders> اضافه کنید:
<add name="imageCopyrightUI" type="ClassLibrary1.imageCopyrightUIProvider,   ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d0b3b3b2aa8ea14b"/>
عبارت ClassLibrary1.imageCopyrightUIProvider به کلاس imageCopyrightUIProvider اشاره می‌کند که در این کلاس UI معرفی می‌شود. مابقی عبارت هم کاملا مشخص است و در لینک‌های بالا در مورد Strong name توضیح داده شده اند. 
فایل administration.config  در مسیر زیر قرار دارد:
%windir%\system32\inetsrv\config\administration.config
حالا تنها کاری که نیاز است، باز کردن IIS است. به بخش وب سایت‌ها رفته و اپلیکیشنی که قبلا با نام mypictures را ایجاد کرده بودیم، انتخاب کنید. در سمت راست، آخر لیست، بخش others باید ماژول ما دیده شود. بازش کنید و تنظمیات آن را تغییر دهید و حالا یک تصویر را از اپلیکیشن mypictures، روی مرورگر درخواست کنید تا تغییرات را روی تگ مشاهده کنید:

 
حالا دیگر باید ماژول نویسی برای IIS را فراگرفته باشیم. این ماژول‌ها می‌توانند از یک مورد ساده تا یک کلاس مهم و امنیتی باشند که روی سرور شما برای همه یا بعضی از وب سایت‌ها در حال اجرا هستند و در صورت لزوم و اجازه شما، برنامه نویس‌ها میتوانند مثل همه‌ی تگ‌های موجود در وب کانفیگ سایتی را که  مینویسند، تگ ماژول شما و  تنظیمات آن را با استفاده از attribute یا خصوصیت‌های تعریف شده، بر اساس سلایق و نیازهایشان تغییر دهند و روی سرور شما آپلود کنند. الان شما یک سرور خصوصی سازی شده دارید.
از آنجا که این مقاله طولانی شده است، باقی موارد ویرایشی روی این UI را در مقاله بعدی بررسی خواهیم کرد. 
مطالب
تکنیک‌های ایجاد سایت‌های چند زبانه
امروزه چند زبانه بودن سایت‌ها، از اهمیت بالایی برخوردار شده است و هر سایتی که نیاز داشته باشد در سایر نقاط جهان شناخته شود و کاربران مناطق مختلف، به راحتی از آن استفاده کنند، سایت‌های خود را بر پایه‌ی چندین زبان ایجاد می‌کنند. در این نوشتار سعی داریم بر این موضوع بررسی اجمالی داشته باشیم و نکات زیر را بررسی نماییم.

  • طراحی دیتابیس یا بانک اطلاعاتی بر پایه چند زبانه بودن و بررسی سناریوهای مختلف.
  • نکاتی که باید در ساخت سایت‌های چند زبانه به آن‌ها دقت کرد.
  • شیوه‌ی تشخیص و تغییر زبان سایت
  • معرفی چند کامپوننت وب، برای مباحث چند زبانه

طراحی مدل دیتابیس

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

مزایا:

  1. پیاده سازی آسان

معایب:

  1. در این روش با زیاد شدن هر زبان، تعداد ستون‌ها افزایش می‌یابد که باعث می‌شود طراحی مناسبی نداشته باشد.
  2. در ضمن این مورد باید توسط برنامه نویس مرتبا اضافه گردد یا اینکه برنامه نویس این امکان را در سیستم قرار دهد که مدیر سایت بتواند در پشت صحنه کوئری افزودن ستون را ایجاد کند که باید جدول مرتبا مورد alter گرفتن قرار بگیرد.
  3. ممکن است همیشه برای هر زبانی مطلبی قرار نگیرد و این مورد باعث می‌شود بی جهت فضایی برای آن در نظر گرفته شود.

پی نوشت: با اینکه امروزه بحث فیلدهای sparse Column وجود دارد ولی این فیلد‌ها در هر شرایطی مورد استفاده قرار نمی‌گیرند وبیشتر متعلق به زمانی است که می‌دانیم آن فیلد به شدت کم مورد استفاده قرار می‌گیرد.

پی نوشت دوم : در صورتی که فیلد شما مانند متن مقاله که عموما از نوع داده (varchar(max است استفاده می‌کنید و در صورتی که زبان مورد استفاده قرار نگیرد در خیلی از اوقات بی جهت فیلد‌های Blob ساخته اید که بهینه سازی آن را نیز باید در نظر بگیرید.


در مرحله‌ی بعد برای رفع مشکلات بالا یک جدول از زبان‌ها، مانند جدول زیر را ایجاد می‌کنیم:
 
 ID  کد
 Language  زبان
 ISO  کد دو رقمی آن زبان
 Flag  پرچم آن کشور
بعد از آن هر مقاله برای یک زبان ایجاد خواهد شد؛ چیزی مانند تصویر زیر:

مزایا:

  1. پیاده سازی آسان

معایب:

  1. ایجاد رکوردهای تکراری، هر مقاله برای بعضی از اطلاعاتش که چند زبانه نیستند داده‌های تکراری خواهد داشت.
  2. هر مقاله یک مقاله‌ی جدا شناخته می‌شود و ارتباطی میان آنان نخواهد بود. بدین ترتیب توانایی ایجاد گزارش‌هایی چون هر گروه از مقاله و دسته بندی آن‌ها از بین خواهد رفت. در ضمن مدیر عموما در یک سیستم مدیریتی می‌خواهد تنها یک لینک را به یک مقاله بدهد و سایت بنا به تشخیص در زبان مزبور، یکی از این مقالات را به کاربر نمایش دهد؛ نه اینکه مرتبا مدیر برای هر زبان، لینکی را مهیا کند و در این حالت چنین چیزی ممکن نخواهد بود.
  3. در یک سیستم فروشگاهی همانند تصویر بالا کار هم سخت‌تر می‌شود و هر رکورد، یک محصول جدا شناخته می‌شود و ویرایش‌ها هم برای هر کدام باید جداگانه صورت بگیرد که در عمل این طرح را رد می‌کند.


سومین راه حل این است که سه جدول ایجاد کنیم:

یک. جدول زبان‌ها (که بالاتر ایجاد شده بود)

دو . جدول نام مقاله به همراه اطلاعات پایه و فیلدها بی نیاز به چند زبانه بودن

سه : یک جدول که هر دو ستون آن کدهای کلید دو جدول بالا را دارند و فیلدهای چند زبانه در آن وجود دارند.

جدول پایه

 ID  کد
 Name  نام مقاله
 CreationDate  تاریخ ایجاد
 Writer  نویسنده
 Visibilty  وضعیت نمایش
 جدول مقالات
 LanguageCode کد زبان
 ArticleID  کد مقاله
 CreationDate  تاریخ ایجاد
 Visibility  وضعیت نمایش مقاله
Title
عنوان مقاله
ContentText
متن مقاله

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

مزایا:

  1. گزارش گیری آسان برای هر دسته مقاله با زبان‌های مختلف و ارتباط و یکپارچگی
  2. آسان در افزودن زبان.

معایب:

  1. ایجاد کوئری‌های پیچیده‌تر و جوین دار که به نسبت روش‌های قبلی کوئری‌ها پیچیده‌تر شده اند.
  2. کدنویسی زیادتر.

استفاده از ساختارهای XML یا JSON برای ذخیره سازی اطلاعات چند زبانه مانند ساختارهای زیر:

XML
<Articles>
<Article>
this is english text
</Article>
<Article>
این یک متن فارسی است
</Article>
</Articles>

یا 
<Articles>
<en-us>
this is english text
</en-us>
<fa-ir>
این یک متن فارسی است
</fa-ir>
</Articles>
JSON
"Articles":["en-us':{"title":"this is english text","content":" english content"},"fa-ir":{"title":"متن فارسی","content":"محتوای فارسی"}]
ازSQL Server 2005 به بعد از نوع داده xml پشتیبانی می‌شود و در نسخه‌ی 2016 آن نیز پشتیبانی از Json اضافه شده است که حتی شامل اندیکس‌های اختصاصی هم برای این دو نوع می‌باشد.
از مزایای این روش ذخیره‌ی همه داده‌ها در یک ستون و یک جدول است و نیازی به ستون‌های اضافه یا جداول اضافه نیست ولی معایب این روش استفاده از کوئری‌های پیچیده‌تر جهت ارتباط و خواندن است.

استفاده از بانک‌های اطلاعاتی NO SQL
در این بانک‌ها دیگر درگیر تعداد ستون‌ها و جنس آن‌ها نیستیم و میتوانیم برای هر مقاله یا محصول، هر تعداد زبان و یا فیلد را که می‌خواهیم، در نظر بگیریم و اضافه کنیم. برای آشنایی بیشتر با این نوع بانک‌ها و انواع آن، مقالات مربوط به nosql را در سایت دنبال کنید.

نکاتی که در یک سایت چند زبانه باید به آن‌ها توجه کرد.

یک . زبان آن صفحه را معرفی کنید: این کار هم به موتورهای جست و جو برای ثبت سایت شما کمک می‌کند و هم برای معلولین که از ابزارهای صفحه خوان استفاده می‌کنند، کمک بزرگی است. در این روش، صفحه خوان‌ها و دستگاه‌های خط بریل که زبان صفحه را تشخیص نمی‌دهند با خواندن کد زبان می‌توانند زبان صفحه را تشخیص دهند. با استفاده از خط زیر میتوانید زبان اصلی صفحه‌ی خود را تنظیم نمایید:
<html lang="en">

اگر از XHTML استفاده می‌کنید خاصیت زیر را فراموش نکنید. دریافت W3C Validation بدون آن امکان پذیر نخواهد بود.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
با تغییر زبان هر صفحه، باید تنظیم زبان آن تغییر یابد:



دو. چند زبانه بودن صفحه: در بالا یاد گرفتیم که چگونه زبان اصلی صفحه را تنظیم کنیم، ولی گاهی اوقات صفحه به غیر از زبان اصلی، شامل زبان‌های دیگر هم می‌شود؛ مثل نقل قول‌ها یا موارد دیگر. برای این‌کار می‌توانید از خصوصیت lang که در اکثر تگ‌ها پشتیبانی می‌شود، استفاده کنید. مثال پایین یک نقل قول فرانسوی است که ما آن را به خصوصیت lang، جهت تایید زبانش مزین کرده‌ایم:
<blockquote lang=”fr”>

<p>Le plus grand faible des hommes, c'est l'amour qu'ils ont de la vie.</p>

</blockquote>

سه. لینک ها : اگر دارید در صفحه‌ای لینک به جایی می‌دهید که متفاوت از زبان شماست، حتما باید زبان صفحه یا سایت مقصد را مشخص کنید. مثلا لینک زیر برای صفحه‌ای است که از یک زبان غیر فرانسوی به یک صفحه‌ی با زبان فرانسوی هدایت می‌شود:
<a href="" hreflang="fr">French</a>

همچنین اگر متن لینک شما هم به زبان فرانسوی باشد خیلی خوب می‌شود که آن را هم بیان کنید و از خاصیت lang و هم hreflang همزمان استفاده کنید:
<a href="" hreflang="fr">Francais</a>

چهار. جهت صفحه: به طور پیش فرض شما این مورد را به خاطر طراحی ظاهر صفحه رعایت می‌کنید ولی خالی از لطف نیست که بگوییم جهت زبان را هم با خصوصیت dir  که مقدار پیش فرضش راست به چپ هست، ذکر کنید. هر چند که عموما وب سایت‌های چند زبانه شامل یک قالب دیگر به صورت راست چین یا چپ چین هم هستند که بر اساس زبان انتخاب شده، قالب خود را تغییر خواهند داد.

پنج. انکودینگ صفحه را مشخص کنید: برای اینکه نحوه‌ی رمزگذاری و رمزگشایی حروف و نمادها مشخص گردد، باید انکودینگ تنظیم شود و حتی برای بعضی از موتورهای جست و جو که ممکن است با وب سایت شما به مشکل بر بخورند. امروزه بیشتر از صفحات یونیکد استفاده می‌شود که سطح وسیعی از کاراکترها را پشتیبانی می‌کند.
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

HTML5
<meta charset="UTF-8">

شش. اندازه‌ی فونت:
موقعی که یک سایت چند زبانه را طراحی می‌کنید این نکته خیلی مهم هست که بدانید اندازه فونت‌های زبان پیش فرض، برای باقی زبان‌ها مناسب نیستند. به عنوان مثال ممکن است اندازه فونتی برای زبان‌های انگلیسی، فرانسوی و آلمانی مناسب باشد ولی برای زبان‌های فارسی و عربی و چینی و ... مناسب نباشد و خواندن آن سخت شود. به همین جهت یکی از راه‌های حل این مشکل استفاده از قالب css است که وابسته به خصوصیت lang ای است که شما برای صفحه و هر المان یا تگی که از این خصوصیت استفاده می‌کند، تعیین کرده‌اید.
:lang(en) {

font-size: 85%;

font-family: arial, verdana, sans-serif;

}

:lang(zh) {

font-size: 125%;

font-family: helvetica, verdana, sans-serif;

}

خط زیر تعیین میکند که از استایل اول استفاده شود:
<html lang="en">
و خط زیر تعیین می‌کند که از استایل دوم استفاده شود:
<html lang="zh">
البته این کد بالا در مرورگرهای فایرفاکس، اپرا و IE8 به بالا پاسخ می‌دهد. برای سایر مروگرها چون کروم و نسخه‌های پیشین IE باید از شیوه‌ی زیر بهره ببرید:
<body class="english"> or <body class="chinese">

و استایل:
.english {

font-size: 85%;

font-family: arial, verdana, sans-serif;

}

.chinese {

font-size: 125%;

font-family: helvetica, verdana, sans-serif;

}
در این شیوه برای تگ مربوطه یک کلاس با نام آن زبان ایجاد کرده که محتوای آن تنظیمات قلم آن زبان می‌باشد.

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

هشت : زمان را نیز تغییر دهید: یکی از مواردی که در کمتر سایت چند زبانه‌ای به چشم می‌خورد و به نظر بنده می‌تواند بسیار مهم باشد این است که time zone منطقه‌ی هر زبان را بدانید. به عنوان مثال برای مقاله‌ی خود، تاریخ ایجاد را به صورت UTC ذخیره کنید و سپس نمایش را بر اساس زبان یا حتی بهتر و دقیق‌تر از طریق IP کشور مربوطه به دست آورید. برای کاربران ثبت نام شده این تاریخ می‌تواند دقیق‌تر باشد همانند انجمن‌های وی بولتین.


شیوه‌های تشخیص زبان سایت

یکی از راه‌های تشخیص زبان این است که موقعی که برای اولین بار کاربری به سایت مراجعه می‌کند، زبان مورد نظرش را سوال کنید و این اطلاعات را در یک کوکی بدون تاریخ انقضاء ذخیره کنید تا در دفعات بعدی آن را بررسی نمایید.

دومین راه، استفاده از IP کاربر مراجعه کننده است تا بر اساس آن زبان مورد نظر را انتخاب کنید.

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

پی نوشت: فراموش نگردد که امکان تغییر زبان همیشه برای کاربر مهیا باشد و طوری نباشد که کاربر در آینده نتواند زبان سایت را تغییر دهد؛ حتی اگر تشخیص خودکار سایت برای زبان فعال باشد.

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


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

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

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

پلاگین بعدی International Telephone Input است که  پیاده سازی پلاگین بالا می‌باشد. برای مواردی مفید است که شما نیاز دارید کد تلفنی کشوری را انتخاب کنید.

در مقاله‌های زیر که در سایت جاری است در مورد Globalization و به خصوص استفاده از ریسورس‌ها مطالب خوبی بیان شده است:

قسمت بیست و دوم آموزش MVC که مبحث Globalization را دنبال می‌کند.

قسمت اول از شش قسمت مباحث Globalization  که دنباله‌ی آن را می‌توانید در مقاله‌ی خودش دنبال کنید.

مطالب
زیباتر کد بنویسیم

داشتن آگاهی در مورد ساختارهای داده‌‌ها، الگوریتم‌ها و یا عملگرهای بیتی بسیار عالی است و یا تسلط بر نحوه‌ی کارکرد ابزارهایی مانند SharePoint و امثال آن این روزها ضروری است. اما باید در نظر داشت، کدی که امروز تهیه می‌شود شاید فردا یا ماه دیگر یا چند سال بعد نیاز به تغییر داشته باشد، بنابراین دانش زیبا نوشتن یک قطعه کد که خواندن آن‌را ساده‌تر می‌کند و در آینده افرادی که از آن نگهداری خواهند کرد زیاد "زجر" نخواهند کشید، نیز ضروری می‌باشد. (اگر کامنت‌های سایت را خوانده باشید یکی از دوستان پیغام گذاشته بود، اگر به من بگویند یک میلیون بگیرید و برنامه فعلی را توسعه دهید یا رفع اشکال کنید، حاضرم 10 هزارتومان بگیرم و آن‌را از صفر بنویسم! متاسفانه این یک واقعیت تلخ است که ناشی از عدم خوانا بودن کدهای نوشته شده می‌باشد.)
در ادامه یک سری از اصول زیبا نویسی کدها را بررسی خواهیم کرد.


1- سعی کنید میزان تو در تو بودن کدهای خود را محدود کنید.
لطفا به مثال زیر دقت نمائید:

void SetA()
{
if(a == b)
{
foreach(C c in cs)
{
if(c == d)
{
a = c;
}
}
}
}

توصیه شده است فقط یک سطح تو در تو بودن را در یک تابع لحاظ کنید. تابع فوق 4 سطح تو رفتگی را نمایش می‌دهد (برای رسیدن به a=c باید چهار بار از tab استفاده کنید). برای کاهش این تعداد سطح می‌توان به صورت زیر عمل کرد:

void SetA()
{
if(a != b)
return;

foreach(C c in cs)
a = GetValueOfA(c);
}

TypeOfA GetValueOfA(C c)
{
if(c == d)
return c;

return a;
}

خواناتر نشد؟!

افزونه‌های CodeRush و refactor pro مجموعه‌ی DevExpress از لحاظ مباحث refactoring در ویژوال استودیو حرف اول را می‌زنند. فقط کافی است برای مثال قطعه کد if داخلی را انتخاب کنید، بلافاصله سه نقطه زیر آن ظاهر شده و با کلیک بر روی آن امکان استخراج یک تابع از آن‌را برای شما به سرعت فراهم خواهد کرد.



مثالی دیگر:

if (foo) {
if (bar) {
// do something
}
}

به صورت زیر هم قابل نوشتن است (جهت کاهش میزان nesting):

if (foo && bar) {
// do something
}

افزونه‌ی Resharper امکان merge خودکار این نوع if ها را به همراه دارد.



و یا یک مثال دیگر:
میزان تو در تو بودن این تابع جاوا اسکریپتی را ملاحظه نمائید:

function findShape(flags, point, attribute, list) {
if(!findShapePoints(flags, point, attribute)) {
if(!doFindShapePoints(flags, point, attribute)) {
if(!findInShape(flags, point, attribute)) {
if(!findFromGuide(flags,point) {
if(list.count() > 0 && flags == 1) {
doSomething();
}
}
}
}
}
}

آن‌را به صورت زیر هم می‌توان نوشت با همان کارآیی اما بسیار خواناتر:

function findShape(flags, point, attribute, list) {
if(findShapePoints(flags, point, attribute)) {
return;
}

if(doFindShapePoints(flags, point, attribute)) {
return;
}

if(findInShape(flags, point, attribute)) {
return;
}

if(findFromGuide(flags,point) {
return;
}

if (!(list.count() > 0 && flags == 1)) {
return;
}

doSomething();
}

2- نام‌های با معنایی را برای متغیرها وتوابع خود انتخاب کنید.
با وجود پیشرفت‌های زیادی که در طراحی و پیاده سازی IDE ها صورت گرفته و با بودن ابزارهای تکمیل سازی خودکار متن تایپ شده در آن‌ها، این روزها استفاده از نام‌های بلند برای توابع یا متغیرها مشکل ساز نیست و وقت زیادی را تلف نخواهد کرد. برای مثال به نظر شما اگر پس از یک سال به کدهای زیر نگاه کنید کدامیک خود توضیح دهنده‌تر خواهند بود (بدون مراجعه به مستندات موجود)؟

void UpdateBankAccountTransactionListWithYesterdaysTransactions()
//or?
void UpdateTransactions()

3- تنها زمانی از کامنت‌ها استفاده کنید که لازم هستند.
اگر مورد 2 را رعایت کرده باشید، کمتر به نوشتن کامنت نیاز خواهد بود. از توضیح موارد بدیهی خودداری کنید، زیرا آن‌ها بیشتر سبب اتلاف وقت خواهند شد تا کمک به افراد دیگر یا حتی خود شما. همچنین هیچگاه قطعه کدی را که به آن نیاز ندارید به صورت کامنت شده به مخزن کد در یک سیستم کنترل نگارش ارسال نکنید.

//function thisReallyHandyFunction() {
// someMagic();
// someMoreMagic();
// magicNumber = evenMoreMagic();
// return magicNumber;
//}

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

از کامنت‌های نوع زیر پرهیز کنید که بیشتر سبب رژه رفتن روی اعصاب خواننده می‌شود تا کمک به او! (خواننده را بی‌سواد فرض نکنید)

// Get the student's id
thisId = student.getId();

کامنت زیر بی معنی است!

// TODO: This is too bad. FIX IT!

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


4- عدم استفاده از عبارات شرطی بی‌مورد هنگام بازگشت دادن یک مقدار bool:
مثال زیر را درنظر بگیرید:

if (foo>bar) {
return true;
} else {
return false;
}

آن‌را به صورت زیر هم می‌توان نوشت:

return foo>bar;

5- استفاده از متغیرهای بی مورد:
برای مثال:

Something something = new Something(foo);
return something;

که می‌شود آ‌ن را به صورت زیر هم نوشت:

return new Something(foo);

البته یکی از خاصیت‌های استفاده از افزونه‌ی Resharper ویژوال استودیو، گوشزد کردن و اصلاح خودکار موارد 4 و 5 است.



6- در نگارش‌های جدید دات نت فریم ورک استفاده از ArrayList منسوخ شده است. بجای آن بهتر است از لیست‌های جنریک استفاده شود. کدی که در آن از ArrayList استفاده می‌شود طعم دات نت فریم ورک 1 را می‌دهد!

7- لطفا بین خطوط فاصله ایجاد کنید. ایجاد فواصل مجانی است!

دو تابع جاوا اسکریپتی زیر را (که در حقیقت یک تابع هستند) در نظر بگیرید:

function getSomeAngle() {
// Some code here then
radAngle1 = Math.atan(slope(center, point1));
radAngle2 = Math.atan(slope(center, point2));
firstAngle = getStartAngle(radAngle1, point1, center);
secondAngle = getStartAngle(radAngle2, point2, center);
radAngle1 = degreesToRadians(firstAngle);
radAngle2 = degreesToRadians(secondAngle);
baseRadius = distance(point, center);
radius = baseRadius + (lines * y);
p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
// Now some more code
}

function getSomeAngle() {
// Some code here then
radAngle1 = Math.atan(slope(center, point1));
radAngle2 = Math.atan(slope(center, point2));

firstAngle = getStartAngle(radAngle1, point1, center);
secondAngle = getStartAngle(radAngle2, point2, center);

radAngle1 = degreesToRadians(firstAngle);
radAngle2 = degreesToRadians(secondAngle);

baseRadius = distance(point, center);
radius = baseRadius + (lines * y);

p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);

pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
// Now some more code

}

کدامیک خواناتر است؟
استفاده از فاصله بین خطوط در تابع دوم باعث بالا رفتن خوانایی آن شده است و این طور به نظر می‌رسد که سطرهایی با عملکرد مشابه در یک گروه کنار هم قرار گرفته‌اند.

8- توابع خود را کوتاه کنید.
یک تابع نباید بیشتر از 50 سطر باشد (البته در این مورد بین علما اختلاف هست!). اگر بیشتر شد بدون شک نیاز به refactoring داشته و باید به چند قسمت تقسیم شود تا خوانایی کد افزایش یابد.
به صورت خلاصه یک تابع فقط باید یک کار را انجام دهد و باید بتوان عملکرد آن‌را در طی یک جمله توضیح داد.

9- از اعداد جادویی در کدهای خود استفاده نکنید!
کد زیر هیچ معنایی ندارد!

if(mode == 3){ ... }
else if(mode == 4) { ... }

بجای این اعداد بی مفهوم باید از enum استفاده کرد:

if(mode == MyEnum.ShowAllUsers) { ... }
else if(mode == MyEnum.ShowOnlyActiveUsers) { ... }

10- توابع شما نباید تعداد پارامتر زیادی داشته باشند
اگر نیاز به تعداد زیادی پارامتر ورودی وجود داشت (بیش از 6 مورد) از struct و یا کلاس جهت معرفی آن‌ها استفاده کنید.

مطالب
آموزش فایرباگ - #2 - تب Console
در قسمت قبلی با تب‌های فایرباگ آشنا شدیم . در این قسمت و قسمت‌های بعدی ، با هر تب به صورت کامل آشنا خواهیم شد .

در این قسمت با تب Console  آشنا خواهیم شد .
در قسمت قبل در مورد این تب گفتیم :

در این تب دو بخش وجود دارد :
در بخش Log هشدار‌ها ، پیغام‌ها ، درخواست‌های XHR و ... نمایش داده می‌شوند .
بخش دیگر هم که در سمت راست قرار دارد ، مخصوص اجرای کدهای جاوا اسکریپت می‌باشد .

 

پس یک قسمت داریم برای نوشتن کدهای جاوا اسکریپت و یک قسمت هم برای مشاهده‌ی رویداد‌ها .

اکنون 2 سوال مطرح می‌شود :
  1. برای اجرای یک کد در حالت معمول چگونه عمل می‌کنیم ؟
    پاسخ : کدهای مورد نظر را بین تگ باز و بسته‌ی script قرار می‌دهیم و صفحه مورد نظر را در مرورگر باز می‌کنیم و مرورگر کد را اجرا می‌کند .
  2. در صورتی که کدهای ما خطا داشته باشند ، چگونه خطایابی می‌کنیم ؟
    پاسخ : در بین کدهای نوشته شده چند alert قرار می‌دهیم و سعی می‌کنیم مشکل را پیدا کنیم .

همین 2 سوال اهمیت و قدرت فایرباگ و قسمت Console آن را برای ما آشکار می‌کند ، زیرا ما می‌توانیم بدون Reload کردن صفحه ، کدهایمان را اجرا و نتیجه را مشاهده کنیم یا بوسیله توابع موجود خیلی ساده کدهایمان را خطایابی کنیم .



چگونه کدهای نوشته شده را سریع اجرا کنم ؟ کلید میانبر ( HotKey ) اجرای کد‌ها چیست ؟
پاسخ : فشردن کلید CTRL + Enter

امتحان کنید :

کد زیر را در بخش کدنویسی تایپ کنید و بوسیله کلید میانبر گفته شده ، آن را اجرا کنید .
document.getElementsByTagName("div");
 
تصویر ذیل نتیجه اجرای کدی هست که اجرا کردیم .



چند نکته :
  • قسمت هایی که با رنگ قرمز و یک دات ( . ) بعد از نام تگ قرار گرفته اند ، کلاس‌های CSS ای هستند که المنت دارد .
  • قسمت هایی که با رنگ آبی تیره و یک شارپ ( # ) بعد از نام تگ قرار گرفته اند ، ID تگ‌ها هستند .
  • قسمت هایی که کمرنگ هستند ، المنت هایی هستند که در صفحه قابل نمایش نیستند .
  • اگر یک تگ چند کلاس داشته باشد ، فقط اولین کلاس نمایش داده می‌شود .


دکمه‌ها و حالت‌های نمایش بخش کد نویسی

4 دکمه در قسمت کدنویسی وجود دارد :
  1. Run : اجرای کد
  2. Clear : خالی کردن بخش کد نویسی
  3. Copy : کپی کردن کد موجود در بخش کد نویسی در حافظه
  4. History : کدهای نوشته شده در نشست ( Session ) فعلی مرورگر

بخش کد نویسی می‌تواند به 2 شکل نمایش داده شود :
  1. بصورت جعبه چند خطی ( Command Editor )
  2. بصورت تک خطی ( Command Line )
با دکمه‌ی قرمز رنگ موجود در پایین - چپ می‌توانید بین این دو حالت سوئیچ کنید .


9 دکمه هم در بالای بخش log وجود دارد ( به ترتیب از چپ به راست ) :


  • دکمه ای با عنوان “Break On All Errors” . زمانی فعال شود ، در اولین اجرای یک کد از داخل صفحه ، به تب Script منتقل می‌شود و در خطی که کد در حال اجرا است توقف می‌کند .
  • Clear : بخش log را خالی می‌کند .
  • Persist : فعال بودن این دکمه باعث می‌شود که محتویات بخش Console در بارگزاری مجدد صفحه حفظ شود .
  • Profile : بوسیله این گزینه می‌توانید کدهای اجرایی خود در مدت زمان فعال بودن این دکمه ، تحت نظر بگیرید .
    به این صورت که پس از غیر فعال کردن این دکمه ( کلیک مجدد بروی آن ) می‌توانید تابع‌های اجرا شده ، تعداد فراخوانی آنها ، مدت زمان اجرای هر یک ، میانگین زمان اجرای هر بار یک تابع و ... را مشاهده کنید .
  • 5 دکمه‌ی بعدی هم برای فیلتر کردن Log هستند .


نمایش تعداد خطاهای اتفاق افتاده ، در نوار وضعیت ( Status Bar ) :

اگر در زمان فعال بودن فایرباگ ، در صفحه خطایی رخ دهد ، در نوار وضعیت عدد 1 را نمایش می‌دهد و به ازای هر خطای جدید ، یکی به تعداد خطاها اضافه می‌کند .
البته اگر از ورژن‌های جدید فایرفاکس استفاده می‌کنید ، نوار وضعیت را نخواهید داشت و تعداد خطاها را در کنار آیکون فایرباگ خواهید داشت .


در کنار همه این امکانات ، فایرباگ یک مجموعه کامل از توابع کاربردی برای توسعه جاوا اسکریپت و خطایابی جاوا اسکریپت در اختیار ما می‌گذارد .

چند متد کمکی برای نوشتن Log‌های مختلف در Console :
console.debug('This is a Debug message'); 
console.info('This is an Information'); 
console.warn('This is a Warning message'); 
console.error('This is an Error message');


در قسمت بعدی توابع مربوط به توسعه جاوا اسکریپت ( Command Line API & Console API ) در فایرباگ را بررسی خواهیم کرد .
مطالب
مروری بر کتابخانه ReactJS - قسمت دوم - نصب و پیکربندی React‌JS برای Visual Studio Code

برای شروع به نصب و پیکربندی، باید بدانیم به چه چیزهایی احتیاج داریم. قطعا به کتابخانه React نیاز داریم. اما بسته به نوع کدنویسی که میخواهیم در پیش بگیریم، احتمالا به کتابخانه‌های دیگری هم احتیاج پیدا خواهیم کرد. در قسمت قبل نحوه‌ی ساخت یک تگ HTML، با React آورده شد. دوباره به آن نگاهی بیاندازیم:

var ClickableImage = function (props) {
    return (
        <a href={props.href}>
            <img src={props.src} />
        </a>
    )
};
میان یک فایل جاوااسکریپت، از تگ‌های HTML استفاده شده‌است. چرا و چطور؟! 

React از دو روش برای ساخت تگ‌ها استفاده میکند. روش ساده‌تر همین مثالی است که در بالا آمده؛ یعنی از تگ‌های HTML را به صورت مستقیم استفاده می‌کند. روش دیگر، استفاده از زبان جاوااسکریپت به تنهایی است. مثلا تگ‌های <a> و <img>  بالا به صورت زیر نوشته میشوند:

var ClickableImage = function(props) {
    React.createElement(
        "a", 
        {href: props.href}, 
        React.createElement(
            "img",
            {src: props.src}
        )
    )
};

وقتی تصور کنیم که قرار است یک جدول یا یک فرم را ایجاد کنیم، ارزش روش ساده‌تر، مشخص میشود. در واقع تگ‌هایی که استفاده شده، واقعا تگ‌های HTML نیستند؛ چیزی هستند به نام JSX.  


JSX

JSX زبان یا روشی است که اجازه میدهد از تگ‌های مشابه با HTML در جاوااسکریپت استفاده کنیم. به دلیل تفاوت‌های مختصری که دارند، گفته شد که این تگ‌ها دقیقا HTML نیستند. برای مثال در تگ‌های HTML خاصیت‌های class و for را داریم؛ اما در تگ‌های JSX باید به ترتیب از className و htmlFor استفاده کنیم. مسئله بعدی این است که اساسا JSX همراه با React ارائه نشده و برای اینکه بتوانیم از JSX استفاده کنیم، نیاز به ابزاری اضافه داریم تا JSX را برای JavaScript و مرورگر ترجمه کند که فیسبوک، Babel را پیشنهاد میدهد. اگر از JSX بدون ابزار مترجم استفاده کنیم با پیام خطای زیر مواجه می‌شویم. 

  > Uncaught SyntaxError: Unexpected token

یعنی کاراکتر شروع (>) تگ‌ها را تشخیص نمیدهد.


نصب کتابخانه‌ها

این کتابخانه‌ها را می‌شود به مدل‌های مختلفی دریافت و پیکربندی کرد. بسته به نوع پروژه و محیط توسعه و پیکربندی‌های خاص هر پروژه، روش کار میتواند متفاوت باشد. هدف اصلی، مروری بر امکانات React است. پس ساده‌ترین روش نصب را برای ادامه کار انتخاب میکنیم. مانند هر کتابخانه‌ی دیگری میشود بطور یکجا React و Babel را از سایت‌های اصلی یا Github دانلود و به پروژه اضافه و استفاده کرد؛ مثل jQuery و Bootstrap. اما راه استاندارد و پیشنهاد شده، استفاده از ابزارهای مدیریت بسته‌ها مثل npm است. در قدم اول با فرض بر اینکه VSCode و npm بر روی سیستم نصب هستند، اول یک پوشه خالی را در VSCode به عنوان پروژه باز میکنیم و از منوی View -> Integrated Terminal  را انتخاب میکنیم. در ترمینال باز شده دستور نصب زیر را وارد میکنیم.

npm install react react-dom
با این کار پوشه node_modules به ریشه پروژه اضافه میشود که حاوی کتابخانه React است. پوشه node_modules مختص به React نیست. هر کتابخانه‌ای را که به این صورت نصب کنیم، به این پوشه اضافه میشود. مرحله بعد، نصب کتابخانه Babel جهت استفاده‌ی از JSX است. کتابخانه Babel یک مترجم بزرگ است که اجزای مختلفی دارد. ما به حداقل‌هایی از آن برای ترجمه تگ‌های JSX  احتیاج داریم. برای این کار همانند قبل، ترمینال را انتخاب میکنیم و دستور نصب زیر را اجرا میکنیم. با این دستور نصب، کتابخانه مختصر babel-standalone به پوشه node_modules اضافه میشود.
npm install babel-standalone

نحوه استفاده

فایل index.html را به ریشه پروژه اضافه کنید و کدهای زیر را درون تگ Body قرار دهید: 

<div id="reactTestContainer"></div>

<script src="node_modules/react/dist/react.min.js"></script>
<script src="node_modules/react-dom/dist/react-dom.min.js"></script>
<script src="node_modules/babel-standalone/babel.min.js"></script>
<script src="react-test.js" type="text/babel"></script>

برای کار با کتابخانه React به دو فایل react.js و react-dom.js نیاز داریم. اولی بخش اصلی کتابخانه و دومی برای ساخت تگ‌ها و تبادل با بخش HTML DOM استفاده میشود؛ مثلا اضافه کردن تگ‌های ساخته شده به HTML. ذکر ویژگی "type="text/babel برای فایل‌هایی که در آنها از تگ‌های JSX استفاده شده ضروری است؛ در غیر اینصورت Babel  تشخیص نمیدهد که کار ترجمه را برای چه فایلهایی باید انجام دهد. در نهایت قطعه کد زیر را در فایل react-test.js وارد کنید. 

var ClickableImage = function (props) {
    return (
        <a href={props.href}>
            <img src={props.src} />
        </a>
    )
};

ReactDOM.render(
    <ClickableImage href="http://google.com" src="google.jpg"/>, 
    document.getElementById("reactTestContainer")
)

با اجرا کردن پروژه، تصویری قابل کلیک به مقصد گوگل، در تگ <div>، با ویژگی "id=”reactTestContainer ایجاد خواهد شد.

تا به این قسمت متوجه شدیم که کتابخانه React چیست و چطور میشود از آن استفاده کرد. در قسمت بعد نگاهی دقیق‌تر به کامپوننت‌های React خواهیم داشت.

مطالب
آشنایی با Virtual Directory
نمیدانم آیا تا به حال برای شما پیش آمده است که بخواهید اطلاعاتتان را در جایی غیر از زیرشاخه‌های wwwroot ذخیره نمایید یا خیر؟
یادم هست برای یکی از مشتریانم یک سرور خریده بودیم که دو پارتیشن داشت و آن موقع به ذهنم خطور کرد که اگه بخواهم مثلا فایل‌های سیستم مدیریتی را داخل یک پارتیشن دیگر قرار بدهم چگونه انجام می‌شود؛ چطوری میتوانم به مکانی غیر از شاخه‌ی wwwroot، عمل mappath را انجام بدم؟ چگونه میتوانم یک لینک مستقیم، به مکانی دیگر داشته باشم؟
جواب تمام این سوالات در امکانی از IIS به اسم virtual Directory بود. در این روش ما یک مکان فیزیکی را به iis معرفی می‌کنیم و به آن یک نام مجازی میدهیم که از آن در برنامه استفاده خواهیم کرد.
در iis، در بخش sites، همه سایت‌های ایجاد شده بر روی IIS لیست می‌شوند. یک context menu بر روی سایت مورد نظر خود باز کنید و گزینه‌ی add virtual directory انتخاب کنید. یک نام مجازی به آن بدهید و مکان مورد نظر را انتخاب کنید و کادر را تایید کنید.

برای ویرایش آن یعنی تغییر مسیر فیزیکی هم میتوان از طریق مسیر زیر عمل کرد

Right Click On virtual Directory>Manage Virtual Directory >Advanced Settings

از این پس در IIS دسترسی به پوشه، از طریق ~/media میسر هست؛ ولی بسیاری از ما برای تست، برنامه را از طریق IIS Express اجرا و تست می‌کنیم. پس بهتر هست این گزینه در آنجا هم مورد بررسی قرار بگیرد.

برای دسترسی به کانفیگ iis express عموما مسیرهای زیر معتبر هستند:

%userprofile%\documents\iisexpress\config\applicationhost.config
%userprofile%\my documents\iisexpress\config\applicationhost.config

فایل applicationhost.config  فایلی است که قرار است تغییر بدهیم.

ولی اگر مسیرهای بالا راهگشا نبود، برای پیدا کردن فایل‌های کانفیگ می‌توانید از طریق آیکن IIS Express که حین اجرای پروژه در notification area باز می‌شود نیز اقدام کرد.

یک context menu از طریق این آیکن باز کرده و گزینه‌ی show all applications را انتخاب کنید. لیست تمامی سایت‌های در حال اجرا نمایش داده می‌شود. یکی را انتخاب کنید تا در زیر اطلاعات نمایش یابد. در قسمت کانفیگ، آدرس فایل کانفیگ داده شده است و مسیر نیز مشخص است. با کلیک بر روی آن فایل applicationhost.config باز می‌شود.

فایل مورد نظر را باز کنید. این فایل را می‌توان با نوت پد یا یک xml editor گشود. در آن یک تگ sites وجود دارد که تمامی پروژه‌های تحت وبی را که تا الان دارید، درون خودش ذخیره کرده‌است. به ازای هر سایت یک تگ site هست و خصوصیات هر کدام، داخل این تگ قرار گرفته‌است. اگر دقت کنید هر پروژه شما یا همان تگ site، شامل تگ زیر می‌باشد:

<application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="E:\website\oscar\panel\Oscar_Manager\Oscar_Manager" />
                </application>

در اینجا خود IIS Express اقدام به ساخت یک دایرکتوری مجازی که همان مسیر ذخیره پروژه هست کرده. برای معرفی دایرکتوری مجازی جدید، یک کپی از تگ application را ایجاد کنید.

برای مثال من قصد دارم یک دایرکتوری مجازی به اسم media بسازم تا تصاویر و دیگر فایل‌های چندرسانه ای را در آن ذخیره کنم و محل فیزیکی آن نیز D:\testvd می‌باشد. پس تگ کپی شده را به نحو زیر تغییر می‌دهم:

 <application path="/media" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="d:\testvd" />
                </application>

بنابراین در کل، تگ site من به شکل زیر تغییر پیدا می‌کند:

<site name="Oscar_Manager" id="23">
                <application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="E:\website\oscar\panel\Oscar_Manager\Oscar_Manager" />
                </application>
 <application path="/media" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="d:\testvd" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:1844:localhost" />
                </bindings>
            </site>

الان مسیر مجازی ما ساخته شده است. برای تست صحت این کار، یک فایل تصویری را در آن قرار می‌دهم و در کنترلر مورد نظر، این کد را می‌نویسم تا یک تصویر به سمت کلاینت در یک virtual directory ارسال شود.

var dir = Server.MapPath("~/media");
            var path = System.IO.Path.Combine(dir, "1.jpg");
            return base.File(path, "image/jpeg");

کنترلر را صدا زده تا نتیجه کار را ببنید.

همانطور که حتما متوجه شدید IIS Express محیط GUI ندارد. البته مدتی است افزونه‌ای برای این کار محیا شده ولی بیشتر کاربرد آن ایجاد یک سایت جدید و اجرا و توقف IIS می‌باشد که می‌توانید آن را از اینجا  دریافت نمایید.

نکته:البته بنده این نکته را تایید نمی‌کنم ولی شنیده‌ام که در نسخه‌های بالاتر ویژوال استادیو با راست کلیک بر روی نام پروژه، گزینه Use IIS Express وجود دارد که به یک محیط گرافیکی ختم می‌شود و از آنجا که بنده نسخه 2012 را دارم این مورد را تست نکردم.


ایجاد virtual Directory از طریق Appcmd

دسترسی به appcmd از طریق مسیر زیر امکان پذیر است:

%systemroot%\system32\inetsrv\AppCmd.exe

این دستور به تمامی اشیاء سرور، از قبیل سایت‌ها و اپلیکیشن‌ها دسترسی دارد و میتواند هر نوع متدی را بر روی اشیاء سرور، از قبیل ثبت، ویرایش و حذف را انجام دهد.

یکی از این دستورات، برای ساخت Virtual Directory استفاده می‌شود:

APPCMD add vdir /app.name:"default we site/" /path:/vdir1 /physicalPath:d:\testvd

سوییچ /app برای نام وب سایت یا پروژه است و حتما در انتهای نام، علامت / قرار گیرد. سوییچ بعدی هم که /path هست برای دادن مسیر مجازی و سوییچ آخری هم آدرس محل فزیکی است. بعد از اجرای دستور، پیام زیر نمایش داده می‌شود:

VDIR object "Default Web Site/vdir1" added

پنجره commandprompt باید به صورت Run a Administrator باز شده باشد.

برای تغییر محل فیزیکی یک virtual directory میتوان از دستور زیر استفاده کرد:

appcmd.exe set vdir "Default Web Site/vdir1/" -physicalPath:"D:\Files"

از این پس مسیر فیزیکی این آدرس مجازی مسیر D:\Files خواهد بود.

البته مباحث پیشرفته‌تری چون application pool‌ها نیز وجود دارد که از حوصله این مقاله خارج هست و در وقت و مقاله دیگر بررسی خواهیم کرد.

برای ارسال دستور به IIS Express هم از طریق مسیر زیر appcmd را باز کنید:

%ProgramFiles%\IIS Express\appcmd.exe

امکان ایجاد virtual directory عموما در سرورهای مجازی و اختصاصی در پنل مربوطه نیز وجود دارد.

ساخت virtual Directory در وب سایت پنل 

ساحت virtual Directory در پلسک