نظرات مطالب
بالا بردن سرعت DbContext هنگام ثبت داده های زیاد
در کل هر زمان که قصد انجام Bulk Insert رو ندارید این رفتار را غیر فعال نکنید.(به صورت پیش فرض فعال است)
البته بهتره که هر زمان در عملیات Bulk Insert تعداد رکورد‌های مورد نظر خیلی زیاد بود به ازای یک تعداد مشخص از Entity ها(برای مثال 1000) یک بار DbContext رو SaveChanged کرده و اونو Dispose کنید و دوباره یک Instance جدید از DbContext بسازید و ادامه کار(دلیل دوباره ساختن DbContext هم اینه که DbContext ، بعد از دستور SaveChanged  دیتای مورد نظر رو در دیتابیس ذخیره می‌کنهه ولی فقط State هر Entity رو به Unchaged تغییر میده و خود Entity رو Detach نمی‌کنه که این خود باعث افزایش ObjectGraph موجود در DbContext می‌شود و در نتیجه کاهش کارایی).
در ضمن می‌تونید با فراخوانی دستور DetectChanged مستقیما DbContext رو مجبور به بررسی وضعیت خواص CurrentValue  و OriginalValue هر Entity بکنید.
مطالب
ایجاد helper برای Nivo Slider در Asp.net Mvc

کامپوننت‌های jQuery زیادی وجود دارند که توسط آنها میتوان تصاویر را بصورت زمانبندی شده و به همراه افکت‌های زیبا در سایت خود نشان داد. مانند اینجا 

در این قصد ایجاد helper برای کامپوننت NivoSlider را داریم.

1- یک پروژه  Asp.net Mvc 4.0 ایجاد میکنیم.

2- سپس فایل jquery.nivo.slider.pack.js  ، فایل‌های css  مربوط به این کامپوننت و چهار تم موجود را از سایت این کامپوننت و یا درون سورس مثال ارائه شده دریافت می‌کنیم.

3- به کلاس BundleConfig رفته و کدهای زیر را اضافه میکنیم: 

#region Nivo Slider

   bundles.Add(new StyleBundle("~/Content/NivoSlider").Include("~/Content/nivoSlider/nivo-slider.css"));
   bundles.Add(new StyleBundle("~/Content/NivoSliderDefaultTheme").Include("~/Content/nivoSlider/themes/default/default.css"));
   bundles.Add(new StyleBundle("~/Content/NivoSliderDarkTheme").Include("~/Content/nivoSlider/themes/dark/dark.css"));
   bundles.Add(new StyleBundle("~/Content/NivoSliderLightTheme").Include("~/Content/nivoSlider/themes/light/light.css"));
   bundles.Add(new StyleBundle("~/Content/NivoSliderBarTheme").Include("~/Content/nivoSlider/themes/bar/bar.css"));
   bundles.Add(new ScriptBundle("~/bundles/NivoSlider").Include("~/Scripts/jquery.nivo.slider.pack.js"));

#endregion

سپس در فایل shared آنها را بصورت زیر اعمال میکنیم:

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <script type="text/javascript" src="~/Scripts/jquery-1.9.1.min.js"></script>
    @Styles.Render("~/Content/NivoSlider", "~/Content/NivoSliderDefaultTheme","~/Content/NivoSliderLightTheme")
    @Scripts.Render("~/bundles/NivoSlider")
</head>
<body>
    @RenderBody()
    @RenderSection("scripts", required: false)
</body>
</html>

4- یک پوشه با عنوان helper به پروژه اضافه میکنیم. سپس کلاس‌های زیر را به آن اضافه می‌کنیم :

 public class NivoSliderHelper 
 {
        #region Fields

        private string _id = "nivo1";
        private List<NivoSliderItem> _models = null;
        private string _width = "100%";
        private NivoSliderTheme _theme = NivoSliderTheme.Default;
        private string _effect = "random"; // Specify sets like= 'fold;fade;sliceDown'
        private int _slices = 15; // For slice animations
        private int _boxCols = 8; // For box animations
        private int _boxRows = 4; // For box animations
        private int _animSpeed = 500; // Slide transition speed
        private int _pauseTime = 3000; // How long each slide will show
        private int _startSlide = 0; // Set starting Slide (0 index)
        private bool _directionNav = true; // Next & Prev navigation
        private bool _controlNav = true; // 1;2;3... navigation
        private bool _controlNavThumbs = false; // Use thumbnails for Control Nav
        private bool _pauseOnHover = true; // Stop animation while hovering
        private bool _manualAdvance = false; // Force manual transitions
        private string _prevText = "Prev"; // Prev directionNav text
        private string _nextText = "Next"; // Next directionNav text
        private bool _randomStart = false; // Start on a random slide
        private string _beforeChange = ""; // Triggers before a slide transition
        private string _afterChange = ""; // Triggers after a slide transition
        private string _slideshowEnd = ""; // Triggers after all slides have been shown
        private string _lastSlide = ""; // Triggers when last slide is shown
        private string _afterLoad = "";

        #endregion

        string makeParameters()
        {
            var builder = new StringBuilder();
            builder.Append("{");
            builder.Append(string.Format("effect:'{0}'", _effect));
            builder.AppendLine(string.Format(",slices:{0}", _slices));
            builder.AppendLine(string.Format(",boxCols:{0}", _boxCols));
            builder.AppendLine(string.Format(",boxRows:{0}", _boxRows));
            builder.AppendLine(string.Format(",animSpeed:{0}", _animSpeed));
            builder.AppendLine(string.Format(",pauseTime:{0}", _pauseTime));
            builder.AppendLine(string.Format(",startSlide:{0}", _startSlide));
            builder.AppendLine(string.Format(",directionNav:{0}", _directionNav.ToString().ToLower()));
            builder.AppendLine(string.Format(",controlNav:{0}", _controlNav.ToString().ToLower()));
            builder.AppendLine(string.Format(",controlNavThumbs:{0}", _controlNavThumbs.ToString().ToLower()));
            builder.AppendLine(string.Format(",pauseOnHover:{0}", _pauseOnHover.ToString().ToLower()));
            builder.AppendLine(string.Format(",manualAdvance:{0}", _manualAdvance.ToString().ToLower()));
            builder.AppendLine(string.Format(",prevText:'{0}'", _prevText));
            builder.AppendLine(string.Format(",nextText:'{0}'", _nextText));
            builder.AppendLine(string.Format(",randomStart:{0}", _randomStart.ToString().ToLower()));
            builder.AppendLine(string.Format(",beforeChange:{0}", _beforeChange));
            builder.AppendLine(string.Format(",afterChange:{0}", _afterChange));
            builder.AppendLine(string.Format(",slideshowEnd:{0}", _slideshowEnd));
            builder.AppendLine(string.Format(",lastSlide:{0}", _lastSlide));
            builder.AppendLine(string.Format(",afterLoad:{0}", _afterLoad));
            builder.Append("}");
            return builder.ToString();
        }
        public NivoSliderHelper (
            string id, 
            List<NivoSliderItem> models,
            string width = "100%", 
            NivoSliderTheme theme = NivoSliderTheme.Default,
            string effect = "random", 
            int slices = 15, 
            int boxCols = 8, 
            int boxRows = 4,
            int animSpeed = 500, 
            int pauseTime = 3000, 
            int startSlide = 0,
            bool directionNav = true, 
            bool controlNav = true,
            bool controlNavThumbs = false, 
            bool pauseOnHover = true, 
            bool manualAdvance = false,
            string prevText = "Prev",
            string nextText = "Next", 
            bool randomStart = false,
            string beforeChange = "function(){}", 
            string afterChange = "function(){}",
            string slideshowEnd = "function(){}", 
            string lastSlide = "function(){}", 
            string afterLoad = "function(){}")
        {
            _id = id;
            _models = models;
            _width = width;
            _theme = theme;
            _effect = effect;
            _slices = slices;
            _boxCols = boxCols;
            _boxRows = boxRows;
            _animSpeed = animSpeed;
            _pauseTime = pauseTime;
            _startSlide = startSlide;
            _directionNav = directionNav;
            _controlNav = controlNav;
            _controlNavThumbs = controlNavThumbs;
            _pauseOnHover = pauseOnHover;
            _manualAdvance = manualAdvance;
            _prevText = prevText;
            _nextText = nextText;
            _randomStart = randomStart;
            _beforeChange = beforeChange;
            _afterChange = afterChange;
            _slideshowEnd = slideshowEnd;
            _lastSlide = lastSlide;
            _afterLoad = afterLoad;
        }
        public IHtmlString GetHtml()
        {
            var thm = "theme-" + _theme.ToString().ToLower();
            var sb = new StringBuilder();
            sb.AppendLine("<div style='width:" + _width + ";height:auto;margin:0px auto' class='" + thm + "'>");
            sb.AppendLine("<div id='" + _id + "' class='nivoSlider'>");
            foreach (var model in _models)
            {
                string img = string.Format("<img src='{0}' alt='{1}' title='{2}' />", model.ImageFilePath, "", model.Caption);
                string item = "";
                if (model.LinkUrl.Trim().Length > 0 &&
                    Uri.IsWellFormedUriString(model.LinkUrl, UriKind.RelativeOrAbsolute))
                {
                    item = string.Format("<a href='{0}'>{1}</a>", model.LinkUrl, img);
                }
                else
                {
                    item = img;
                }
                sb.AppendLine(item);
            }
            sb.AppendLine("</div>");
            sb.AppendLine("</div>");

            sb.AppendLine("<script type='text/javascript'>");
            //sb.AppendLine("$('#" + _id + "').parent().ready(function () {");
            sb.Append("$(document).ready(function(){");
            sb.AppendLine("$('#" + _id + "').nivoSlider(" + makeParameters() + ");");
            //sb.AppendLine("$('.nivo-controlNav a').empty();");//semi hack for rtl layout
            //sb.AppendLine("$('#" + _id + "').nivoSlider();");
            sb.AppendLine("});");
            sb.AppendLine("</script>");

            return new HtmlString(sb.ToString());
        }
    }
    public enum NivoSliderTheme
    {
        Default, Light, Dark, Bar,
    }
    public class NivoSliderItem
    {
        public string ImageFilePath { get; set; }
        public string Caption { get; set; }
        public string LinkUrl { get; set; }
    }
6-سپس برای استفاده از این helper یک کنترلر به پروژه اضافه میکنیم مانند Home :
 public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
        public virtual ActionResult NivoSlider()
        {
            var models = new List<NivoSliderItem>
            {
                new NivoSliderItem()
                    {
                        ImageFilePath =Url.Content("~/Images/img1.jpg"),
                        Caption = "عنوان اول",
                        LinkUrl = "http://www.google.com",
                    },
                new NivoSliderItem()
                    {
                        ImageFilePath =Url.Content("~/Images/img2.jpg"),
                        Caption = "#htmlcaption",
                        LinkUrl = "",
                    },
                new NivoSliderItem()
                    {
                        ImageFilePath =Url.Content("~/Images/img3.jpg"),
                        Caption = "عنوان سوم",
                        LinkUrl = "",
                    },
                new NivoSliderItem()
                    {
                        ImageFilePath =Url.Content("~/Images/img4.jpg"),
                        Caption = "عنوان چهارم",
                        LinkUrl = "",
                    },
                new NivoSliderItem()
                    {
                        ImageFilePath =Url.Content("~/Images/img5.jpg"),
                        Caption = "عنوان پنجم",
                        LinkUrl = "",
                    }
            };
            return PartialView("_NivoSlider", models);
        }
    }
7- سپس ویوی Home را نیز ایجاد میکنیم:
@{
    ViewBag.Title = "Index";
}

<h2>Nivo Slider Index</h2>

@{ Html.RenderAction("NivoSlider", "Home");}
8- یک پارشال ویو نیز برای رندر کردن NivoSliderهای خود ایجاد میکنیم:
@using NivoSlider.Helper
@model List<NivoSliderItem>
@{
    var nivo1 = new NivoSliderHelper("nivo1", Model.Skip(0).Take(3).ToList(), theme: NivoSliderTheme.Default, width: "500px");
    var nivo2 = new NivoSliderHelper("nivo2", Model.Skip(3).Take(2).ToList(), theme: NivoSliderTheme.Light, width: "500px");
}
@nivo1.GetHtml()

<div id="htmlcaption">
    <strong>This</strong> is an example of a <em>HTML</em> caption with <a href="#">a link</a>.
</div>

<br />
<br />
@nivo2.GetHtml()
نکته اول: اگر بخواهیم بر روی تصویر موردنظر متنی نمایش دهیم کافیست خاصیت Caption را مقداردهی کنیم. برای نمایش توضیحاتی پیچیده‌تر (مانند نمایش یک div و لینک و غیره) بعنوان توضیحات یک تصویر خاص باید خاصیت Caption را بصورت "{نام عنصر}#" مقداردهی کنیم و ویژگی class  مربوط به عنصر موردنظر را با nivo-html-caption مقداردهی کنیم.(همانند مثال بالا)
نکته دوم: این کامپوننت به همراه 4 تم (deafult- light- bar- dark) ارائه شده است. موقع استفاده از تم‌های دیگر باید رفرنس مربوط به آن تم را نیز اضافه کنید.
امیدوارم مفید واقع شود.
نظرات مطالب
صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid
مطلب فوق نه وابستگی خاصی به وب فرم‌ها دارد و نه ASP.NET MVC. ویو آن یک فایل HTML ساده‌است و سمت سرور آن فقط یک کنترلر ASP.NET Web API که با تمام مشتقات ASP.NET سازگار است. در این حالت یک نفر می‌تواند ASP.NET نگارش خودش را خلق کند؛ بدون اینکه نگران جزئیات وب فرم‌ها باشد یا ASP.NET MVC. ضمنا دانش جاوا اسکریپتی آن هم قابل انتقال است؛ چون اساسا Kendo UI برای فناوری سمت سرور خاصی طراحی نشده‌است و حالت اصل آن با PHP، Java و امثال آن هم کار می‌کند.
مطالب
هدایت خودکار کاربر به صفحه لاگین در حین اعمال Ajax ایی
در ASP.NET MVC به کمک فیلتر Authorize می‌توان کاربران را در صورت درخواست دسترسی به کنترلر و یا اکشن متد خاصی در صورت لزوم و عدم اعتبارسنجی کامل، به صفحه لاگین هدایت کرد. این مساله در حین postback کامل به سرور به صورت خودکار رخ داده و کاربر به Login Url ذکر شده در web.config هدایت می‌شود. اما در مورد اعمال Ajax ایی چطور؟ در این حالت خاص، فیلتر Authorize قابلیت هدایت خودکار کاربران را به صفحه لاگین، ندارد. در ادامه نحوه رفع این نقیصه را بررسی خواهیم کرد.

تهیه فیلتر سفارشی SiteAuthorize

برای بررسی اعمال Ajaxایی، نیاز است فیلتر پیش فرض Authorize سفارشی شود:
using System;
using System.Net;
using System.Web.Mvc;

namespace MvcApplication28.Helpers
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public sealed class SiteAuthorizeAttribute : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAuthenticated)
            {
                throw new UnauthorizedAccessException(); //to avoid multiple redirects
            }
            else
            {
                handleAjaxRequest(filterContext);
                base.HandleUnauthorizedRequest(filterContext);
            }
        }

        private static void handleAjaxRequest(AuthorizationContext filterContext)
        {
            var ctx = filterContext.HttpContext;
            if (!ctx.Request.IsAjaxRequest())
                return;

            ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            ctx.Response.End();
        }
    }
}
در فیلتر فوق بررسی handleAjaxRequest اضافه شده است. در اینجا درخواست‌های اعتبار سنجی نشده از نوع Ajax ایی خاتمه داده شده و سپس StatusCode ممنوع (403) به کلاینت بازگشت داده می‌شود. در این حالت کلاینت تنها کافی است StatusCode یاده شده را مدیریت کند:
using System.Web.Mvc;
using MvcApplication28.Helpers;

namespace MvcApplication28.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [SiteAuthorize]
        [HttpPost]        
        public ActionResult SaveData(string data)
        {
            if(string.IsNullOrWhiteSpace(data))
                return Content("NOk!");

            return Content("Ok!");
        }
    }
}
در کد فوق نحوه استفاده از فیلتر جدید SiteAuthorize را ملاحظه می‌کنید. View ارسال کننده اطلاعات به اکشن متد SaveData، در ادامه بررسی می‌شود:
@{
    ViewBag.Title = "Index";
    var postUrl = this.Url.Action(actionName: "SaveData", controllerName: "Home");
}
<h2>
    Index</h2>
@using (Html.BeginForm(actionName: "SaveData", controllerName: "Home",
                method: FormMethod.Post, htmlAttributes: new { id = "form1" }))
{
    @Html.TextBox(name: "data")
    <br />
    <span id="btnSave">Save Data</span>
}
@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $("#btnSave").click(function (event) {
                $.ajax({
                    type: "POST",
                    url: "@postUrl",
                    data: $("#form1").serialize(),
                    // controller is returning a simple text, not json  
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = "/login";
                        }
                    }
                });
            });
        });
    </script>
}
تنها نکته جدید کدهای فوق، بررسی xhr.status == 403 است. اگر فیلتر SiteAuthorize کد وضعیت 403 را بازگشت دهد، به کمک مقدار دهی window.location، مرورگر را وادار خواهیم کرد تا صفحه کنترلر login را نمایش دهد. این کد جاوا اسکریپتی، با تمام مرورگرها سازگار است.


نکته تکمیلی:
در متد handleAjaxRequest، می‌توان یک JavaScriptResult را نیز بازگشت داد تا همان کدهای مرتبط با window.location را به صورت خودکار به صفحه تزریق کند:
filterContext.Result =  new JavaScriptResult { Script="window.location = '" + redirectToUrl + "'"};
البته این روش بسته به نحوه استفاده از jQuery Ajax ممکن است نتایج دلخواهی را حاصل نکند. برای مثال اگر قسمتی از صفحه جاری را پس از دریافت نتایج Ajax ایی از سرور، تغییر می‌دهید، صفحه لاگین در همین قسمت در بین کدهای صفحه درج خواهد شد. اما روش یاد شده در مثال فوق در تمام حالت‌ها کار می‌کند.
مطالب
Vue CLI
تیم Vue یک ابزار را جهت scaffold سریع یک پروژه Vue، به صورت رسمی ارائه کرده‌است. توسط این ابزار به صورت سریع می‌توانیم ساختار یک پروژه استاندارد Vue را ایجاد کنیم.

چرا نیاز به Vue CLI داریم؟
  • زیرا نیاز به build processهایی داریم که به ما امکان استفاده از ES6, SCSS و دیگر ویژگیهای عالی را خواهند داد.
  • جهت ساخت و یکی‌سازی فایل‌های تمپلیت
  • بارگذاری نکردن تمامی فایل‌ها به صورت یکجا در زمان Startup 
  • می‌توانیم تسک‌هایی از قبیل Server-side rendering, code-splitting را انجام دهیم.
 
نصب Vue CLI 
ابتدا مطمئن شوید که آخرین نگارش Node.js را نصب کرده‌اید. سپس جهت نصب Vue CLI، خط فرمان را گشوده و دستور زیر ذیل را صادر کنید: 
npm install -g vue-cli

با اجرای فرمان فوق، ابزار CLI به صورت global و عمومی نصب خواهد شد. در ادامه می‌توانیم با دستور vue list، لیستی از قالب‌های رسمی را که توسط CLI قابل ایجاد هستند، مشاهده نمائید: 

در اینجا ما از قالب webpack-simple استفاده خواهیم کرد. برای اینکار دستور زیر را جهت ایجاد یک پروژه بر اساس این قالب صادر کنید: 
vue init webpack-simple dntVue

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

در اینجا ساختار یک پروژه جدید Vue را مشاهده می‌کنید:

index.html: کار شروع و ارائه برنامه را انجام می‌دهد.
package.json: وابستگی‌های npm برنامه را به همراه دارد.
src/App.vue: کامپوننت اصلی برنامه است.
پوشه src/assets: حاوی فایل‌های استاتیک پروژه است.
src/main.js: نقطه‌ی آغاز برنامه است.
webpack.config.json: تنظیمات وب‌پک جهت اجرای پروژه و بارگذاری ماژول‌های موردنیاز.


اجرای برنامه
ابتدا نیاز است وابستگی‌های برنامه دریافت شوند. اینکار را توسط دستور npm install و یا دستور yarn (در صورتیکه yarn را از قبل بر روی سیستم خود نصب کرده‌اید) انجام خواهیم داد:
npm install
به این ترتیب تمامی وابستگی‌های پروژه درون پوشه‌ی node_module تشکیل خواهند شد. اکنون می‌توانیم با صدور دستور npm run dev پروژه را اجرا کنیم:

بررسی فایل‌های Vue
درون یک برنامه‌ی Vue واقعی، فایل‌هایی با پسوند vue. وجود دارند. این فایل شامل تمپلیت، کدها و همچنین استایل‌های یک کامپوننت می‌باشند. 
<template>
    <div>
        <!-- Write your HTML with Vue in here -->
    </div>
</template>

<script>
    export default {
        // Write your Vue component logic here
    }
</script>

<style scoped>
    /* Write your styles for the component in here */
</style>

بنابراین درون فایلی با ساختار فوق، تمامی موارد مورد نیاز برای یک کامپوننت ویو را خواهیم داشت و به اصطلاح نیازی به context switching نخواهیم داشت؛ زیرا تمامی قسمت‌ها را به صورت یکجا در یک محل در اختیار داریم و به راحتی می‌توانیم تمرکز خود را بر روی کدها قرار دهیم. درون کامپوننت نیز می‌توانیم کامپوننت‌های موردنیاز را ایمپورت و از آن استفاده کنیم: 
import { New } from "./components/New.vue";
export default {
    components: {
        New
    }
}


Vue CLI 3
تا اینجا از نسخه‌ی پایدار Vue CLI استفاده کردیم. نسخه‌ی 3 آن هنوز در مرحله‌ی beta قرار دارد. در این نسخه امکانات و دستورات بیشتری اضافه شده‌است؛ از ایجاد یک پروژه ساده تا ایجاد یک پروژه مبتنی بر TypeScript. برای نصب و یا آپگرید می‌توانید از دستور زیر استفاده کنید:
npm install -g @vue/cli
اکنون می‌توانید با صادر کردن دستور vue --version، شماره نسخه‌ی آن را مشاهده نمائید:
3.0.0-beta.11


ایجاد یک پروژه جدید
برای ایجاد یک پروژه جدید می‌توانید دستور زیر را صادر کنید:
vue create my-project
همانطور که مشاهده می‌کنید در این نسخه بجای استفاده از دستور vue init، از vue create استفاده شده است. در اینحالت می‌توانید نوع ایجاد پروژه را تعیین کنید:
Vue CLI v3.0.0-beta.11
? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint)
  Manually select features
حالت پیش‌فرض، چنین ساختاری را برایتان ایجاد خواهد کرد:

بعد از طی کردن مراحل، می‌توانید قالب پروژه‌ی ایجاد شده را به صورت یک preset داشته باشید تا در پروژه‌های آینده مجبور نباشید مراحل قبل را طی کنید. این preset درون یک فایل JSON به صورت زیر ذخیره خواهد شد و حاوی اطلاعات زیر است:
{
  "useConfigFiles": true,
  "router": true,
  "vuex": true,
  "cssPreprocessor": "sass",
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "airbnb",
      "lintOn": ["save", "commit"]
    }
  }
}

در حالت manually نیز می‌توانید گزینه‌های بیشتری را برای تعیین نوع قالب پروژه، انتخاب نمائید. به عنوان مثال می‌توان از TypeScript یا اینکه از lintter یا formatter خاصی برای کدها استفاده کرد:

در ادامه دیگر آپشن‌ها را نیز می‌توانید تعیین کرده و در نهایت به صورت یک قالب از پیش تعریف شده نیز پروژه را داشته باشید:


Zero-config Prototyping 
یکی از قابلیت‌های جالب Vue، امکان تهیه سریع prototype یا طرح اولیه می‌باشد. شاید اکثر اوقات نیاز داشته باشید یک ویژگی یا قابلیت خاص را با Vue تست کنید. در این موارد ممکن است از سایتی مانند CodePen استفاده کنید. اما توسط افزونه‌ی cli-service-global می‌توانید به صورت لوکال و بدون نیاز به راه‌اندازی یک پروژه‌ی جدید، کدهای موردنیاز را آزمایش کنید. فرض کنید می‌خواهیم تمپلیت زیر را قبل از افزودن آن به پروژه، مورد تست قرار دهیم:
<!-- MyCard.vue -->
<template>
    <div class="card">
    <h1>Card Title</h1>
    <p>Card content goes here. Make sure it's not Lorem.</p>
    </div>
</template>
در این‌حالت می‌توانیم با نصب افزونه موردنظر، فایل فوق را به راحتی و بدون نیاز به راه‌اندازی یک پروژه جدید، تست کنیم:
npm install -g @vue/cli-service-global
اکنون می‌توانیم خروجی را با صدور فرمان زیر درون مرورگر مشاهده کنیم:
vue serve MyCard.vue
با صدرو فرمان فوق، فایل توسط افزونه‌ی عنوان شده، از طریق مرورگر قابل دسترسی می‌باشد:

خروجی:

مطالب
MTOM در WCF
در  WCF سه نوع Message Encoder وجود دارد:
  • Text(Xml) Message Encoder(به صورت پیش فرض در تمام Http-Base Binding‌ها از این Encoder استفاده می‌شود)
  • Binary Message Encoder(به صورت پیش فرض در تمام Net* Binding‌ها از این encoder استفاده می‌شود که برای سرویس‌های وب مناسب نیست)
  • MTOM Message Encoder (در حالت استفاده از Http-Base Binding‌ها و انتقال اطلاعات به صورت باینری از این گزینه استفاده می‌شود که به صورت پیش فرض غیر فعال است)
Encoding یا رمزگذاری در WCF به این معنی است که داده‌های مورد نظر برای انتقال، به یکی از فرمت‌های MTOM ، Text-Xml یا  Binary سریالایز شوند.
 وضعیتی را در نظر بگیرید که در یک پروژه مبتنی بر WCF قصد دارید حجمی زیاد از داده به فرمت باینری  (نظیر فایل ها) را بین سرور و کلاینت رد و بدل کنید. به صورت معمول بسیاری از برنامه نویسان، یک کلاس به همراه DataContractAttribute ایجاد می‌کنند که در آن خاصیتی به صورت آرایه ای از نوع بایت که DataMemberAttribute را نیز دارد برای انتقال محتویات فایل استفاده می‌شود. اما باید یک نکته را مد نظر داشت و آن این است که به صورت پیش فرض فرمت انتقال داده‌ها در WCF به صورت Text/Xml است  و برای انتقال داده‌ها نیز از فرمت Base 64 استفاده خواهد شد. مشکل اصلی این جاست که در حالت Text/Xml Encoding برای انتقال داده‌های باینری، برای هر سه بایت، چهار کاراکتر استفاده می‌شود در نتیجه، این باعث افزایش حجم داده تا 33 درصد خواهد شد که کارایی سیستم را تحت تاثیر قرار می‌دهد.
اما خبر خوب این است که استانداردی وجود دارد به نام MTOM یا همان Message Transmission Optimization Mechanism، برای این که بتوان محتوای باینری را بدون افزایش حجم داده انتقال داد. برای پیاده سازی این روش باید موارد زیر را در نظر داشته باشید:

»متد یا همان OperationContract که وظیفه آن ارسال یا دریافت داده‌ها با فرمت MTOM است فقط کلاس هایی را انتقال دهد که دارای MessageContractAttribute هستند. نباید از DataContractAttribute استفاده نمایید.
»خاصیتی که نوع آن آرایه ای از بایت‌ها است نباید دارای DataMemberAttribute باشد؛ بلکه به جای آن باید از MessageBodyMember استفاده نمایید.
»به جای []Byte می‌توان از نوع Stream نیز استفاده کرد(الزامی نیست).
»مقدار خاصیت MessageEncoding در Binding استفاده شده باید MTOM تعیین شود.
پیاده سازی یک مثال
ابتدا کلاس مورد نظر را به صورت زیر تهیه می‌کنیم:
[MessageContract]
public class MyFile
{
    [MessageHeader] 
    public String Filename { get; set; }

    [MessageBodyMember]    
    public Byte[] Contents { get; set; } 
}
چند تذکر
  • به جای DataContract از MessageContract استفاده می‌شود؛
  • تمام خاصیت هایی که نوع آن‌ها غیر از []Byte است باید دارای MessageHeader باشند؛
  • خاصیتی که برای انتقال محتوای باینری تهیه شده است، باید از MessageBodyMember استفاده نماید؛
  • مجاز به تعریف فیلد یا فیلد هایی که نوع آن‌ها Primitive Type است نمی‌باشید.

تنظیمات مربوط به Binding نیز به صورت خواهد بود:

<bindings> 
    <wsHttpBinding>
        <binding name="WsHttpMtomBinding" messageEncoding="Mtom" /> 
    </wsHttpBinding>
 </bindings>
اما یک نکته...
هدف از استفاده از MTOM برای افزایش کارایی انتقال داده‌های باینری در حجم زیاد است. در زیر نتایج مقایسه بررسی انتقال اطلاعات به دو صورت MTOM و Text برای حجم داده‌های متفاوت را مشاهده می‌کنید:


با دقت در نتایج بالا مشخص می‌شود که این روش در حجم داده‌های پایین (مثل 100 بایت یا 1000 بایت) عملکرد مورد انتظار را نخواهد داشت. پس این نکته را نیز در هنگام پیاده سازی به این روش مد نظر داشته باشید.

 
مطالب دوره‌ها
بررسی مقدماتی مراحل کامپایل یک قطعه کد سی‌شارپ و آشنایی با OpCodes
کامپایلر سی‌شارپ چگونه عمل می‌کند؟

کار یک کامپایلر ترجمه قطعه‌ای از اطلاعات به چیز دیگری است. کامپایلر سی‌شارپ، machine code معادل دستورات دات نتی را تهیه نمی‌کند. Machine code، کدی است که مستقیما بر روی CPU قابل اجرا است. در دات نت این مرحله به CLR یا Common language runtime واگذار شده است تا کار اجرای نهایی کدهای تهیه شده توسط کامپایلر سی‌شارپ را انجام دهد.
بنابراین زمانیکه در VS.NET سعی در اجرای یک قطعه کد می‌نمائیم، مراحل ذیل رخ می‌دهند:
الف) فایل‌های سی‌شارپ پروژه، توسط کامپایلر بارگذاری می‌شوند.
ب) کامپایلر کدهای این فایل‌ها را پردازش می‌کند.
ج) سپس چیزی را به نام MSIL تولید می‌کند.
د) در ادامه فایل خروجی نهایی، با افزودن PE Headers تولید می‌شود. توسط PE headers مشخص می‌شود که فایل تولیدی نهایی آیا اجرایی است، یا یک DLL می‌باشد و امثال آن.
ه) و در آخر، فایل اجرایی تولیدی توسط CLR بارگذاری و اجرا می‌شود.


MSIL چیست؟

MSIL مخفف Microsoft intermediate language است. به آن CIL یا Common intermediate language هم گفته می‌شود و این دقیقا همان کدی است که توسط CLR خوانده و اجرا می‌شود. MSIL یک زبان طراحی شده مبتنی بر پشته‌ها است و بسیار شبیه به سایر زبان‌های اسمبلی موجود می‌باشد.


یک سؤال: آیا قطعه کدهای ذیل، کدهای IL یکسانی را تولید می‌کنند؟

namespace FastReflectionTests
{
    public class Test
    {
        public void Method1()
        {
            var x = 10;
            var y = 20;
            if (x == 10)
            {
                if (y == 20)
                {

                }
            }
        }

        public void Method2()
        {
            var x = 10;
            var y = 20;
            if (x == 10 && y == 20)
            {

            }
        }
    }
}
برای یافتن کدهای MSIL یا IL یک برنامه کامپایل شده می‌توان از ابزارهایی مانند Reflector یا ILSpy استفاده کرد. برای نمونه اگر از برنامه ILSpy استفاده کنیم چنین خروجی IL معادلی را می‌توان مشاهده کرد:
.class public auto ansi beforefieldinit FastReflectionTests.Test
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig 
instance void Method1 () cil managed 
{
// Method begins at RVA 0x3bd0
// Code size 17 (0x11)
.maxstack 2
.locals init (
[0] int32 x,
[1] int32 y
)

IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldloc.0
IL_0007: ldc.i4.s 10
IL_0009: bne.un.s IL_0010

IL_000b: ldloc.1
IL_000c: ldc.i4.s 20
IL_000e: pop
IL_000f: pop

IL_0010: ret
} // end of method Test::Method1

.method public hidebysig 
instance void Method2 () cil managed 
{
// Method begins at RVA 0x3bf0
// Code size 17 (0x11)
.maxstack 2
.locals init (
[0] int32 x,
[1] int32 y
)

IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldloc.0
IL_0007: ldc.i4.s 10
IL_0009: bne.un.s IL_0010

IL_000b: ldloc.1
IL_000c: ldc.i4.s 20
IL_000e: pop
IL_000f: pop

IL_0010: ret
} // end of method Test::Method2

.method public hidebysig specialname rtspecialname 
instance void .ctor () cil managed 
{
// Method begins at RVA 0x3c0d
// Code size 7 (0x7)
.maxstack 8

IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Test::.ctor

} // end of class FastReflectionTests.Test
همانطور که مشاهده می‌کنید، کدهای IL با یک برچسب شروع می‌شوند مانند IL_0000. پس از آن OpCodes یا Operation codes قرار دارند. برای مثال ldc کار load constant را انجام می‌دهد. به این ترتیب مقدار ثابت 10 بارگذاری شده و بر روی پشته ارزیابی قرار داده می‌شود و نهایتا در سمت راست، مقادیر را ملاحظه می‌کنید؛ برای مثال مقادیری مانند 10 و 20.
این کدها در حالت کامپایل Release تهیه شده‌اند و در این حالت، کامپایلر یک سری بهینه سازی‌هایی را جهت بهبود سرعت و کاهش تعداد OpCodes مورد نیاز برای اجرا برنامه، اعمال می‌کند.


بررسی OpCodes مقدماتی

الف) OpCodes ریاضی
مانند Add، Sub، Mul و Div

ب) OpCodes کنترل جریان برنامه
مانند Jmp، Beq، Bge، Ble، Bne، Call و Ret
برای پرش به یک برچسب، بررسی تساوی و بزرگتر یا کوچک بودن، فراخوانی متدها و بازگشت دادن مقادیر

ج) OpCodes مدیریت آرگومان‌ها
مانند Ldarg، Ldarg_0 تا Ldarg_3 ، Ldc_I4 و Ldc_I4_1 تا Ldc_I4_8
برای بارگذاری آرگومان‌‌ها و همچنین بارگذاری مقادیر قرار گرفته شده بر روی پشته ارزیابی.

برای توضیحات بهتر این موارد می‌توان کدهای IL فوق را بررسی کرد:
 IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldloc.0
IL_0007: ldc.i4.s 10
IL_0009: bne.un.s IL_0010
IL_000b: ldloc.1
IL_000c: ldc.i4.s 20
IL_000e: pop
IL_000f: pop
در اینجا تعدادی مقدار بر روی پشته ارزیابی بارگذاری می‌شوند. تساوی آن‌ها بررسی شده و نهایتا متد خاتمه می‌یابد.


Stack چیست و MSIL چگونه عمل می‌کنید؟

Stack یکی از انواع بسیار متداول ساختار داده‌ها است و اگر بخواهیم خارج از دنیای رایانه‌ها مثالی را برای آن ارائه دهیم می‌توان به تعدادی برگه کاغذ که بر روی یکدیگر قرار گرفته‌اند، اشاره کرد. زمانیکه نیاز باشد تا برگه‌ای از این پشته برداشته شود، باید از بالاترین سطح آن شروع کرد که به آن LIFO یا Last in First out نیز گفته می‌شود. چیزی که آخر از همه بر روی پشته قرار می‌گیرد، در ابتدا برداشته و خارج خواهد شد.
در دات نت، برای قرار دادن اطلاعات بر روی Stack از متد Push و برای بازیابی از متد Pop استفاده می‌شود. استفاده از متد Pop، سبب خذف آن شیء از پشته نیز می‌گردد.
MSIL نیز یک Stack based language است. MSIL برای مدیریت یک سری از موارد از Stack استفاده می‌کند؛ مانند: پارامترهای متدها، مقادیر بازگشتی و انجام محاسبات در متدها. OpCodes کار قرار دادن و بازیابی مقادیر را از Stack به عهده دارند. به تمام این‌ها در MSIL، پشته ارزیابی یا Evaluation stack نیز می‌گویند.

یک مثال: فرض کنید می‌خواهید جمع 5+10 را توسط MSIL شبیه سازی کنیم.
الف) مقدار 5 بر روی پشته ارزیابی قرار داده می‌شود.
ب) مقدار 10 بر روی پشته ارزیابی قرار داده می‌شود. این مورد سبب می‌شود که 5 یک سطح به عقب رانده شود. به این ترتیب اکنون 10 بر روی پشته است و پس از آن 5 قرار خواهد داشت.
ج) سپس OpCode ایی مساوی Add فراخوانی می‌شود.
د) این OpCode سبب می‌شود تا دو مقدار موجود در پشته Pop شوند.
ه) سپس Add، حاصل عملیات را مجددا بر روی پشته قرار می‌دهد.


یک استثناء
در MSIL برای مدیریت متغیرهای محلی تعریف شده در سطح یک تابع، از Stack استفاده نمی‌شود. این مورد شبیه سایر زبان‌های اسمبلی است که در آن‌ها می‌توان مقادیر را در برچسب‌ها یا رجیسترهای خاصی نیز ذخیره کرد.
مطالب
استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC
مدل زیر را در نظر بگیرید:
/// <summary>
    /// 
    /// </summary>
    public class CompanyModel
    {
        /// <summary>
        /// Table Identity
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// Company Name
        /// </summary>
        [DisplayName("نام شرکت")]
        public string CompanyName { get; set; }

        /// <summary>
        /// Company Abbreviation
        /// </summary>
        [DisplayName("نام اختصاری شرکت")]
        public string CompanyAbbr { get; set; }
    
    }
از View زیر جهت نمایش لیستی از شرکت‌ها متناظر با مدل جاری استفاده میشود:
@{
    const string viewTitle = "شرکت ها";
    ViewBag.Title = viewTitle;
    const string gridName = "companies-grid";
}
<div class="col-md-12">
    <div class="form-panel">
        <header>
            <div class="title">
                <i class="fa fa-book"></i>
                @viewTitle
            </div>
        </header>
        <div class="panel-body">
            <div id="@gridName">
            </div>
        </div>
    </div>
</div>
</div>
@section scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $("#@gridName").kendoGrid({
                dataSource: {
                    type: "json",
                    transport: {
                        read: {
                            url: "@Html.Raw(Url.Action(MVC.Company.CompanyList()))",
                            type: "POST",
                            dataType: "json",
                            contentType: "application/json"
                        }
                    },
                    schema: {
                        data: "Data",
                        total: "Total",
                        errors: "Errors"
                        }
                    },
                    pageSize: 10,
                    serverPaging: true,
                    serverFiltering: true,
                    serverSorting: true
                },
                pageable: {
                    refresh: true
                },
                sortable: {
                    mode: "multiple",
                    allowUnsort: true
                },
                editable: false,
                filterable: false,
                scrollable: false,
                columns: [ {
                    field: "CompanyName",
                    title: "نام شرکت",
                    sortable: true,
                }, {
                    field: "CompanyAbbr",
                    title: "مخفف نام شرکت",
                    sortable: true
                }]
            });
        });
    </script>
}


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

 /// <summary>
    /// 
    /// </summary>
    public static class PropertyExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static MemberInfo GetMember<T>(this Expression<Func<T, object>> expression)
        {
            var mbody = expression.Body as MemberExpression;

            if (mbody != null) return mbody.Member;
            //This will handle Nullable<T> properties.
            var ubody = expression.Body as UnaryExpression;
            if (ubody != null)
            {
                mbody = ubody.Operand as MemberExpression;
            }
            if (mbody == null)
            {
                throw new ArgumentException("Expression is not a MemberExpression", "expression");
            }
            return mbody.Member;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string PropertyName<T>(this Expression<Func<T, object>> expression)
        {
            return GetMember(expression).Name;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string PropertyDisplay<T>(this Expression<Func<T, object>> expression)
        {
            var propertyMember = GetMember(expression);
            var displayAttributes = propertyMember.GetCustomAttributes(typeof(DisplayNameAttribute), true);
            return displayAttributes.Length == 1 ? ((DisplayNameAttribute)displayAttributes[0]).DisplayName : propertyMember.Name;
        }
    }



public static string PropertyName<T>(this Expression<Func<T, object>> expression)
جهت بدست آوردن نام متغییر هایمان استفاده مینماییم.


public static string PropertyDisplay<T>(this Expression<Func<T, object>> expression)
جهت بدست آوردن DisplayNameAttribute استفده میشود. درصورتیکه این DisplayNameAttribute یافت نشود نام متغییر بازگشت داده میشود.

بنابراین View مربوطه را اینگونه بازنویسی میکنیم:

@using Models
@{
    const string viewTitle = "شرکت ها";
    ViewBag.Title = viewTitle;
    const string gridName = "companies-grid";
}
<div class="col-md-12">
    <div class="form-panel">
        <header>
            <div class="title">
                <i class="fa fa-book"></i>
                @viewTitle
            </div>
        </header>
        <div class="panel-body">
            <div id="@gridName">
            </div>
        </div>
    </div>
</div>
</div>
@section scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $("#@gridName").kendoGrid({
                dataSource: {
                    type: "json",
                    transport: {
                        read: {
                            url: "@Html.Raw(Url.Action(MVC.Company.CompanyList()))",
                            type: "POST",
                            dataType: "json",
                            contentType: "application/json"
                        }
                    },
                    schema: {
                        data: "Data",
                        total: "Total",
                        errors: "Errors"
                        }
                    },
                    pageSize: 10,
                    serverPaging: true,
                    serverFiltering: true,
                    serverSorting: true
                },
                pageable: {
                    refresh: true
                },
                sortable: {
                    mode: "multiple",
                    allowUnsort: true
                },
                editable: false,
                filterable: false,
                scrollable: false,
                columns: [ {
                    field: "@(PropertyExtensions.PropertyName<CompanyModel>(a => a.CompanyName))",
                    title: "@(PropertyExtensions.PropertyDisplay<CompanyModel>(a => a.CompanyName))",
                    sortable: true,
                }, {
                    field: "@(PropertyExtensions.PropertyName<CompanyModel>(a => a.CompanyAbbr))",
                    title: "@(PropertyExtensions.PropertyDisplay<CompanyModel>(a => a.CompanyAbbr))",
                    sortable: true
                }]
            });
        });
    </script>
}


مطالب
کار با شیوه‌نامه‌های فرم‌ها در بوت استرپ 4
بوت استرپ، به همراه کلاس‌هایی است، برای نمایش زیباتر فرم‌ها، که شامل کلاس‌های اعتبارسنجی و حتی کنترل نحوه‌ی چیدمان و اندازه‌ی آن‌ها نیز می‌شود.


ایجاد فرم‌های مقدماتی، با بوت استرپ 4

بوت استرپ به همراه کلاس‌هایی مانند form-group و form-control است که از آن‌ها می‌توان برای ایجاد یک فرم مقدماتی استفاده کرد. در ابتدا مثال غیر تزئین شده‌ی زیر را در نظر بگیرید:
<body>
    <div class="container">
        <h2>Medical Questionnaire</h2>
        <form>
            <fieldset>
                <legend>Owner Info</legend>
                <div>
                    <label for="ownername">Owner name</label>
                    <input type="text" id="ownername" placeholder="Your Name">
                </div>
                <div>
                    <label for="owneremail">Email address</label>
                    <input type="email" id="owneremail" aria-describedby="emailHelp"
                        placeholder="Enter email">
                    <small id="emailHelp">We'll never share your email</small>
                </div>
            </fieldset>

            <fieldset>
                <legend>Pet Info</legend>
                <div>
                    <label for="petname">Pet name</label>
                    <input type="text" id="petname" placeholder="Your Pet's name">
                </div>
                <div>
                    <label for="pettype">Pet type</label>
                    <select id="pettype">
                        <option>Choose</option>
                        <option value="cat">Dog</option>
                        <option value="cat">Cat</option>
                        <option value="bird">Other</option>
                    </select>
                </div>
                <div>
                    <label for="reasonforvisit">Reason for today's visit</label>
                    <textarea id="reasonforvisit" rows="3"></textarea>
                </div>
                <div>
                    <label>Has your pet been spayed or neutered?</label>
                    <label><input type="radio" name="spayneut" value="yes"
                            checked> Yes</label>
                    <label><input type="radio" name="spayneut" value="no"> No</label>
                </div>
                <div>
                    <label>Has the pet had any of the following in the past 30
                        days</label>
                    <label><input type="checkbox"> Abdominal pain</label>
                    <label><input type="checkbox"> Lack of appetite</label>
                    <label><input type="checkbox"> Weakness</label>
                </div>
            </fieldset>
            <button type="submit">Submit</button>
        </form>

    </div><!-- content container -->
</body>
که چنین خروجی ابتدایی را نیز به همراه دارد:


در ادامه شروع می‌کنیم به تزئین کردن این فرم، با کلاس‌های بوت استرپ 4:
- ابتدا به fieldsetهای تعریف شده، کلاس form-goup را انتساب می‌دهیم. این مورد سبب می‌شود تا اندکی فاصله بین آن‌ها ایجاد شود.
- سپس به تمام divهایی که المان‌ها را در بر گرفته‌اند نیز کلاس form-group را اعمال می‌کنیم.
با اینکار فاصله‌ی مناسبی بین المان‌های تعریف شده‌ی در صفحه ایجاد می‌شود:


- در ادامه به تمام المان‌های input، select و textarea (منهای checkboxها) کلاس form-control را نسبت می‌دهیم:


با اینکار، ظاهر این المان‌ها بسیار شکیل‌تر شده‌است و همچنین این فرم واکنشگرا نیز می‌باشد.

- پس از آن، تمام المان‌های label را انتخاب کرده و کلاس form-control-label را به آن‌ها انتساب می‌دهیم. هرچند با اینکار ظاهر فعلی فرم تغییری نمی‌کند، اما چنین تعریفی برای فعالسازی کلاس‌های اعتبارسنجی ضروری است.
اگر به هر دلیلی نخواستید این برچسب‌ها را نمایش دهید، می‌توانید از کلاس sr-only استفاده کنید که صرفا سبب نمایش آن‌ها به screen readers می‌شود.
- ذیل فیلد ورود ایمیل، متنی وجود دارد. این متن را با کلاس‌های form-text text-muted مزین می‌کنیم:


- به دکمه‌ی پایین صفحه نیز کلاس‌های btn btn-primary را اضافه می‌کنیم که در مطلب «بررسی شیوه‌نامه‌های المان‌های پر کاربرد بوت استرپ 4» بیشتر به آن‌ها پرداختیم.


مزین سازی checkboxها و radio-buttonها در بوت استرپ 4

روش مزین سازی checkboxها و radio-buttonها در بوت استرپ، با سایر المان‌ها اندکی متفاوت است:
<div class="form-check">
    <label class="form-check-label">
        <input class="form-check-input" type="checkbox">
        Lack of appetite
    </label>
</div>
در اینجا تفاوتی نمی‌کند که بخواهیم با checkboxها کار کنیم و یا radio-buttonها، هر دوی این المان‌ها ابتدا داخل یک div با کلاس form-check قرار می‌گیرند. سپس برچسب آن‌ها دارای کلاس form-check-label می‌شود و در آخر به خود این المان‌های input، کلاس form-check-input اضافه خواهد شد.

یک نکته: اگر نیاز است این المان‌ها کنار یکدیگر نمایش داده شوند، می‌توان بر روی div آن‌ها از کلاس‌های form-check form-check-inline استفاده کرد. در این حالت اگر می‌خواهید برچسب برای مثال radio-button تعریف شده، در یک سطر و گزینه‌ها آن در سطری دیگر باشند، از کلاس d-block بر روی این برچسب استفاده کنید:
<div class="form-group">
    <label class="d-block">Has your pet been spayed or
        neutered?</label>
    <div class="form-check form-check-inline">
        <label class="form-check-label">
            <input class="form-check-input" type="radio" name="spayneut"
                   value="yes" checked>
            Yes
        </label>
    </div>
    <div class="form-check form-check-inline">
        <label class="form-check-label">
            <input class="form-check-input" type="radio" name="spayneut"
                   value="no"> No
        </label>
    </div>
</div>
با این خروجی:



کلاس‌های کنترل اندازه و اعتبارسنجی المان‌های فرم‌های بوت استرپ 4

- با استفاده از کلاس form-control-sm می‌توان اندازه‌ی فیلدهای input را با ارتفاع کوچکتری نمایش داد و یا توسط کلاس form-control-lg می‌توان آن‌ها را بزرگتر کرد.
- کلاس form-inline سبب می‌شود تا یک form-group به صورت inline نمایش داده شود. یعنی برچسب و کنترل‌های درون آن، در طی یک سطر نمایش داده خواهند شد. در این حالت، نیاز به کلاس‌های Margin مانند mx-sm-2 خواهد بود تا فاصله‌ی بین کنترل‌ها را بتوان کنترل کرد.
- برای نمایش نتایج اعتبارسنجی کنترل‌ها:
  - اگر کل فرم اعتبارسنجی شده‌است، کلاس was-validated را به المان form اضافه کنید.
  - اگر اعتبارسنجی کنترلی با موفقیت روبرو شود، کلاس is-valid و اگر خیر کلاس is-invalid را به آن نسبت دهید.
  - اگر می‌خواهید پیام خاصی را پس از موفقیت اعتبارسنجی نمایش دهید، آن‌را درون یک div با کلاس valid-feedback قرار دهید و یا برعکس از کلاس invalid-feedback استفاده کنید.
  - برای تغییر رنگ برچسب المان‌ها نیز از کلاس‌های text-color همانند قبل استفاده کنید؛ مانند text-success.

یک مثال:
<div class="form-group">
    <label for="owneremail" class="text-success">Email address</label>
    <input class="form-control is-valid" type="email" id="owneremail"
        aria-describedby="emailHelp" placeholder="Enter email">
    <small class="form-text text-muted" id="emailHelp">We'll
        never share your email</small>
    <div class="valid-feedback">
        Looks good!
    </div>
</div>
با این خروجی:



تغییر نحوه‌ی چیدمان عناصر فرم‌ها در بوت استرپ 4

فرم زیر را در نظر بگیرید:


قصد داریم با استفاده از کلاس‌های ویژه‌ی بوت استرپ 4، آن‌را دو ستونی کنیم؛ به طوریکه برچسب‌ها در یک ستون و فیلدهای ورودی، در ستونی دیگر نمایش داده شوند. همچنین این فرم واکنشگرا نیز باشد؛ به این معنا که این دو ستونی شدن، فقط در اندازه‌های پس از md رخ دهد:
<body>
    <div class="container">
        <h2>Medical Questionnaire</h2>
        <form>
            <fieldset class="form-group">
                <legend>Owner Info</legend>
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="ownername">Owner</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="ownername"
                            placeholder="Your Name">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="owneremail">Address</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="owneremail"
                            placeholder="Address">
                    </div>
                </div>
                <div class="form-group row">
                    <div class="form-group col-6 offset-md-2">
                        <label class="form-control-label sr-only" for="ownercity">City</label>
                        <input class="form-control" type="text" id="ownercity"
                            placeholder="City">
                    </div>
                    <div class="form-group col-md-4 col-6">
                        <label class="form-control-label sr-only" for="ownerzip">Zip</label>
                        <input class="form-control" type="text" id="ownerzip"
                            placeholder="Zip">
                    </div>
                </div>

                <div class="form-group row">
                    <div class="offset-md-2 col-md-10">
                        <button class="btn btn-primary" type="submit">Submit</button>
                    </div>
                </div>
            </fieldset>
        </form>
    </div>
</body>
با این خروجی در اندازه‌ی پس از md:


توضیحات:
برای ستونی کردن فرم‌ها، ابتدا کلاس row، به form-group قرار گرفته‌ی داخل container اصلی اضافه می‌شود:
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="ownername">Owner</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="ownername"
                            placeholder="Your Name">
                    </div>
                </div>
سپس توسط کلاس col-md-2 تعریف شده‌ی بر روی برچسب، سبب خواهیم شد تا در اندازه‌ی صفحه‌ی بیش از md، این برچسب در یک ستون با عرض دو واحد قرار گیرد. در یک چنین حالتی، ذکر col-form-label نیز ضروری است. همچنین اگر مایل باشیم تا این برچسب، در سمت راست این ستون قرار گیرد، می‌توان از کلاس واکنشگرای text-md-right استفاده کرد.
پس از آن نوبت به تعریف ستون فیلد تعریف شده‌است که با ایجاد یک div و تعریف تعداد واحدی را که به خود اختصاص می‌دهد (col-md-10)، انجام می‌شود.

در اینجا برچسب‌های فیلدهای city و zip با کلاس sr-only مشخص شده‌اند. به همین جهت فقط به screen readers نمایش داده می‌شوند.
<div class="form-group row">
   <div class="form-group col-6 offset-md-2">
   <label class="form-control-label sr-only" for="ownercity">City</label>
   <input class="form-control" type="text" id="ownercity"placeholder="City">
</div>
در یک چنین حالتی، برای اینکه این فیلدها در ستون دوم ظاهر شوند، از کلاس offset-md-2 استفاده شده‌است. از این offset برای تراز کردن دکمه، با ستون دوم نیز استفاده کرده‌ایم:
<div class="form-group row">
    <div class="offset-md-2 col-md-10">
        <button class="btn btn-primary" type="submit">Submit</button>
    </div>
</div>

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

برای افزودن آیکن‌هایی به فیلدهای ورودی، از روش ایجاد گروهی از ورودی‌ها در بوت استرپ 4 استفاده می‌شود:
<div class="form-group">
    <label class="form-control-label" for="donationamt">
        Donation Amount
    </label>
    <div class="input-group">
        <div class="input-group-prepend">
            <span class="input-group-text">$</span>
        </div>
        <input type="text" class="form-control" id="donationamt"
            placeholder="Amount">
        <div class="input-group-append">
            <span class="input-group-text">.00</span>
        </div>
    </div>
</div>
در مثال فوق، روش تعریف یک input-group را مشاهده می‌کنید. داخل آن یک input-group-prepend و سپس input-group-text تعریف می‌شود که می‌تواند شامل یک متن و یا آیکن باشد. اگر نیاز به تعریف دکمه‌ای وجود داشت، از این کلاس استفاده نکنید. با این خروجی:


در بوت استرپ 4، کلاس‌های input-group-addon و input-group-btn  بوت استرپ 3 حذف و با کلاس‌های input-group-prepend و input-group-append جایگزین شده‌اند. از prepend برای قرار دادن آیکنی پیش از فیلد ورودی و از append همانند مثال فوق، برای قرار دادن آیکنی اختیاری پس از فیلد ورودی استفاده می‌شود.

نمونه‌ی متداول دیگر آن، نحوه‌ی تعریف ویژه‌ی فیلد جستجوی سایت، در منوی راهبری آن است:
    <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
        <div class="container">
            <div class="navbar-brand d-none d-sm-inline-block">
                Wisdom Pet Medicine
            </div>
            <div class="navbar-nav mr-auto">
                <a class="nav-item nav-link active" href="#">Home</a>
                <a class="nav-item nav-link" href="#">Mission</a>
                <a class="nav-item nav-link" href="#">Services</a>
                <a class="nav-item nav-link" href="#">Staff</a>
                <a class="nav-item nav-link" href="#">Testimonials</a>
            </div>
            <form class="form-inline d-none d-md-inline-block">
                <div class="input-group">
                    <label for="search" class="form-control-label sr-only"></label>
                    <input type="text" id="search" class="form-control"
                        placeholder="Search ...">
                    <div class="input-group-append">
                        <button class="btn btn-outline-light" type="submit">Go</button>
                    </div>
                </div>
            </form>
        </div>
    </nav>
با این خروجی که در آن دکمه، توسط کلاس input-group-append، با فیلد ورودی کنار آن، یکپارچه به نظر می‌رسد:




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