مطالب
فرمت کردن اطلاعات نمایش داده شده به کمک jqGrid در ASP.NET MVC
پیشنیاز این بحث مطالعه‌ی مطلب «صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC» است و در اینجا جهت کوتاه شدن بحث، صرفا به تغییرات مورد نیاز جهت اعمال بر روی مثال اول اکتفاء خواهد شد.

صورت مساله

می‌خواهیم اطلاعات نمایش داده شده در گرید را به نحوی فرمت کنیم که
الف) اگر id ردیف مساوی 5 بود، رنگ و پس زمینه‌ی آن تغییر کند.
ب) نام محصول، به جزئیات آن لینک شود و این اطلاعات توسط یک jQuery UI Dialog نمایش داده شود.
ج) عدد قیمت با سه رقم جدا کننده همراه باشد.




تکمیل مدل برنامه

مدل قسمت اول صرفا یک محصول بود. مدل قسمت جاری، اطلاعات تولید/تامین کننده آن‌را توسط کلاس Supplier نیز به همراه دارد:
namespace jqGrid02.Models
{
    public class Product
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public decimal Price { set; get; }
        public Supplier Supplier { set; get; }
    }

    public class Supplier
    {
public int Id { set; get; }
        public string CompanyName { set; get; }
        public string Address { set; get; }
        public string PostalCode { set; get; }
        public string City { set; get; }
        public string Country { set; get; }
        public string Phone { set; get; }
        public string HomePage { set; get; }
    }
}

کدهای سمت سرور

کدهای سمت سرور مانند متد GetProducts به همراه صفحه بندی و مرتب سازی پویای آن دقیقا مانند قسمت قبل است.
در اینجا فقط یک اکشن متد جدید جهت بازگشت اطلاعات تولید کننده‌ای مشخص با فرمت JSON، اضافه شده‌است:
        public ActionResult GetGetSupplierData(int id)
        {
            var list = ProductDataSource.LatestProducts;
            var product = list.FirstOrDefault(x => x.Id == id);
            if (product == null)
                return Json(null, JsonRequestBehavior.AllowGet);

            return Json(new
                          {
                              product.Supplier.CompanyName,
                              product.Supplier.Address,
                              product.Supplier.PostalCode,
                              product.Supplier.City,
                              product.Supplier.Country,
                              product.Supplier.Phone,
                              product.Supplier.HomePage
                          }, JsonRequestBehavior.AllowGet);
        }

کدهای سمت کلاینت

صفحه دیالوگی که قرار است اطلاعات تولید کننده را نمایش دهد، یک چنین ساختاری دارد:
<div dir="rtl" id="supplierDialog">
    <span id="CompanyName"></span><br /><br />
    <span id="Address"></span><br />
    <span id="PostalCode"></span>, <span id="City"></span><br />
    <span id="Country"></span><br /><br />
    <span id="Phone"></span><br />
    <span id="HomePage"></span>
</div>
و تغییرات گرید برنامه به شرح زیر است:
    <script type="text/javascript">
        function showSupplierDialog(linkElement, supplierId) {
            //request json data
            $.getJSON('@Url.Action("GetGetSupplierData","Home")', { id: supplierId }, function (data) {
                //set values in dialog
                for (var property in data) {
                    if (data.hasOwnProperty(property)) {
                        $('#' + property).text(data[property]);
                    }
                }
                
                //get link position
                var linkPosition = $(linkElement).offset();
                $('#supplierDialog').dialog('option', 'position', [linkPosition.left, linkPosition.top]);
                //open dialog
                $('#supplierDialog').dialog('open');
            });
        }

        $(document).ready(function () {

            $('#supplierDialog').dialog({
                 autoOpen: false, bgiframe: true, resizable: false, title: 'تولید کننده'
            });

            $('#list').jqGrid({
                // .... مانند قبل
                colNames: ['شماره', 'نام محصول', 'قیمت'],
                //columns model
                colModel: [
                    {
                        name: 'Id', index: 'Id', align: 'right', width: 20,
                        formatter: function (cellvalue, options, rowObject) {
                            var cellValueInt = parseInt(cellvalue);
                            if (cellValueInt == 5) {
                                return "<span style='background: brown; color: yellow'>" + cellvalue + "</span>";
                            }
                            return cellvalue;
                        }
                    },
                    {
                        name: 'Name', index: 'Name', align: 'right', width: 300,
                        formatter: function (cellvalue, options, rowObject) {
                            return "<a href='#' onclick='showSupplierDialog(this, " + rowObject[0] + ");'>" + cellvalue + "</a>";
                        }
                    },
                    {
                        name: 'Price', index: 'Price', align: 'center', width: 50,
                        formatter: 'currency',
                        formatoptions:
                        {
                            decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$'
                        }
                    }
                ],
                // .... مانند قبل
            });
        });
    </script>
- همانطور که ملاحظه می‌کنید، توسط خاصیت formatter می‌توان عناصر در حال نمایش را فرمت کرد و بر روی نحوه‌ی نمایش نهایی آن‌ها تاثیرگذار بود.
در حالت ستون Id، از یک formatter سفارشی استفاده شده‌است. در اینجا این فرمت کننده به صورت یک callback عمل کرده و پیش از رندر نهایی اطلاعات، مقدار سلول جاری را توسط cellvalue در اختیار ما قرار می‌دهد. در این بین هر نوع فرمتی را که نیاز است می‌توان اعمال کرد و سپس یک رشته را بازگشت می‌دهیم. این رشته در سلول جاری درج خواهد شد.
- اگر مانند ستون Name، نیاز به مقادیر سایر سلول‌ها نیز وجود داشت، می‌توان از آرایه‌ی rowObject استفاده کرد. برای مثال در این حالت، یک لینک که کلیک بر روی آن سبب فراخوانی تابع showSupplierDialog می‌شود، در سلول‌های ستون Name درج خواهند شد. اولین rowObject که در اینجا مورد استفاده است، به ستون اول یا همان Id محصول اشاره می‌کند.
- در ستون Price از یک سری formatter از پیش تعریف شده استفاده شده‌است. نمونه‌ای از آن را در قسمت اول در ستون نمایش وضعیت موجود بودن محصول با تنظیم formatter: checkbox مشاهده کرده‌اید. در اینجا از یک formatter توکار دیگر به نام currency برای کار با مقادیر پولی استفاده شده‌است به همراه تنظیمات خاص آن.
- متد showSupplierDialog طوری تنظیم شده‌است که پس از دریافت Id یک محصول، آن‌را به سرور ارسال کرده و مشخصات تولید کننده‌ی آن‌را با فرمت JSON دریافت می‌کند. سپس در حلقه‌ای که مشاهده می‌کنید، خواص شیء جاوا اسکریپتی دریافتی استخراج و به spanهای supplierDialog انتساب داده می‌شوند. جهت سهولت کار، Id این spanها دقیقا مساوی Id خواص شیء دریافتی از سرور، درنظر گرفته شده‌اند.
- در مورد راست به چپ نمایش داده شدن عنوان دیالوگ، تغییرات CSS ایی لازم است که در قسمت اول بیان شدند.


برای مطالعه بیشتر
لیست کامل فرمت کننده‌های توکار
فرمت کننده‌های سفارشی


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid02.zip
 
مطالب
SQL Server 2008: Script Data

افزونه‌ای برای SQL server 2005 به نام Database Publishing Wizard وجود داشت/دارد که توسعه‌ی آن به ظاهر برای SQL server 2008 متوقف شده است. توسط این افزونه می‌توان رکوردهای یک دیتابیس را به صورت عبارات T-SQL درآورد (هر رکورد را به صورت خودکار تبدیل به یک دستور insert می‌کند). به این صورت کار انتقال دیتا خصوصا به هاست‌هایی که دسترسی مستقیم restore کردن داده را نمی‌دهند، به سادگی صورت می‌گیرد. تنها کافی است خروجی کار یکبار بر روی دیتابیس مقصد اجرا شود تا رکوردهایی دقیقا با همان اطلاعات دیتابیس منبع در آن ایجاد گردند.
این قابلیت اکنون جزئی از management studio اس کیوال سرور 2008 است.
برای استفاده از آن بر روی دیتابیس مورد نظر در management studio 2008 کلیک راست کرده و گزینه زیر را انتخاب کنید:
Tasks -> Generate scripts…

در صفحه ویزاردی که ظاهر می‌شود، بر روی next کلیک کرده و در صفحه‌ی بعدی گزینه script data را یافته و مقدار آن‌را به true تنظیم نمائید (شکل زیر).



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

شایان ذکر است این قابلیت با نگارش‌های پائین‌تر اس کیوال سرور نیز کار می‌کند (برای مثال اتصال به اس کیوال سرور 2000 از طریق management studio 2008).

مطالب
معرفی افزونه CAT.NET

اخیرا مایکروسافت افزونه‌ رایگانی را برای آنالیز امنیتی کدهای برنامه‌های نوشته شده با VS.Net ارائه داده است به نام CAT.Net.
دریافت افزونه 32 بیتی، 64 بیتی

این افزونه قابلیت بررسی کدهای شما را جهت یافتن خطرات جدی SQL Injection ، XSS و XPath Injection دارد.
نحوه استفاده:
VS.Net خود را ببندید. در ادامه، پس از نصب، به منوی Tools و گزینه‌ی جدید CAT.NET Code Analysis مراجعه نمائید.
صفحه‌ای ظاهر خواهد شد که پس از کلیک بر روی دکمه آغاز آنالیز آن، کار بررسی امنیتی پروژه را آغاز می‌کند (شکل زیر)




این افزونه همانند FxCop ، اسمبلی برنامه را آنالیز می‌کند. پس از پایان آنالیز،‌ با کلیک بر روی هر سطری که گزارش داده، آن سطر را می‌توان در پروژه یافت و تغییرات لازم را اعمال نمود.



همچنین پس از پایان کار بررسی، یک فایل xml هم در مسیر فایل‌های پروژه ایجاد می‌کند که در آینده در صورت نیاز، توسط همین افزونه قابل گشودن است.

بعلاوه پس از نصب، یک فایل chm هم در دایرکتوری آن جهت آشنایی بیشتر با اجزای مختلف این افزونه قرار خواهد گرفت.

البته، برنامه هنوز در مراحل آزمایشی به سر می‌برد. برای مثال پس از نصب در مسیر دیگری غیر از مسیر پیش فرض نصب، پیغام می‌داد که فایل‌ها را نمی‌تواند پیدا کند. اگر این مورد برای شما هم رخ داد، مسیری را که گزارش می‌دهد به صورت دستی درست کنید و فایل‌های کانفیگ آن‌را از جایی که نصب کرده‌اید به آنجایی که برنامه به شما گزارش می‌دهد کپی کنید تا کار کند!

یا می‌توان این افزونه را از طریق خط فرمان هم اجرا کرد (مسیر پروژه، اسمبلی آن و مسیر نصب cat.net‌ را لازم دارد). به صورت زیر:

CATNETCmd /file:"I:\prog\bin\prog.dll" /search:"I:\prog" /report:"I:\prog\report.xsl" /rule:"J:\microsoft\cat.net\Rules"

که نتیجه حاصل را در فایل xsl نهایی ثبت خواهد نمود.

اگر علاقمند به مطالعه تاریخچه‌ی این برنامه هستید به وبلاگ زیر مراجعه نمائید:
مشاهده وبلاگ

مطالب
کار با شیوه‌نامه‌های فرم‌ها در بوت استرپ 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
مطالب دوره‌ها
ارائه کاربری ساده‌تر انتخاب چندین آیتم از یک لیست به کمک افزونه 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
نظرات مطالب
نمایش خودکار آیکون لینک‌های سایت‌های خارجی با استفاده از jQuery
این هم فکر جالبیه.
سایت زیر سرویس مورد نظر شما رو به رایگان ارائه میده:
http://w.googlepreview.com/preview?s=https://www.dntips.ir/

البته در سایتش نوشته که مجاز نیستید بجز در افزونه مورد نظر فایرفاکس ازش استفاده کنید، ولی خوب دیگه، محض اطلاع ;)
مطالب
آموزش فایرباگ - #5 - HTML Panel
در این سری از مقالات آموزش FireBug ، به صورت ترتیبی پیش رفتیم و ابتدا توضیحات تقریبا مفصلی در مورد پنل Console دادیم.
اکنون به پرکاربردترین بخش آن ، یعنی پنل HTML می‌رسیم.

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

نگاهی به کدهای ارسال شده به مرورگر در آدرس Google.com و نحوه‌ی نمایش آن در فایرباگ را ملاحظه بفرمایید.
( کدهای ارسال شده سمت چپ - نمایش در فایرباگ سمت راست )


این پنل به سه بخش اصلی تقسیم می‌شود :
  • بخش اصلی یا NodeView که محتوای صفحه را بصورت درختی و مرتب و رنگی نمایش می‌دهد.
  •  Panel Toolbar که در بالای پنل قرار دارد.
  •  Side Panels که شامل پنل‌های Style , Computed , Layout , DOM می‌شود.
    که به ترتیب برای نمایش و ویرایش استایل‌ها ، مشاهده استایل‌های محاسبه شده ، مشاهده Layout یا آرایش و نمایش اطلاعات DOM تگ انتخاب شده در NodeView است.

در این مقاله با دو بخش NodeView و Panel Toolbar ، و در مقاله‌ی بعد با پنل‌های سمت راست یعنی Side Panels آشنا می‌شویم.

ویرایش HTML

هر تگ HTML از یک سری Attribute تشکیل می‌شود که در فایرباگ نام ویژگی بصورت آبی تیره و مقدار آن با رنگ قرمز مشخص شده است. برای ویرایش هریک از آن‌ها کافیست برویش کلیک کنید تا آن مقدار در یک باکس ویرایش به نمایش دربیاید.
با ویرایش مقدار ، این تغییر در لحظه بروی صفحه اعمال می‌شود.


برای اضافه کردن یک Attribute جدید به تگ هم بروی تگ مورد نظر راست کلیک می‌کنید و سپس گزینه‌ی New Attribute را انتخاب می‌کنید. ابتدا نام ویژگی ، یک Enter ، سپس مقدار را وارد می‌کنید. با زدن کلیدهای Enter متوالی ، می‌توانید به وارد کردن ویژگی‌ها ادامه دهید.
برای ویرایش کردن یک تگ و تگ‌های فرزندش ، بروی تگ موردنظر کلیک کنید تا به حالت انتخاب دربیاید ، سپس بروی دکمه‌ی Edit در بالای پنل کلیک کنید. در نهایت بعد از انجام ویرایش ، مجددا بروی دکمه‌ی Edit کلیک کنید.


 Node View
NodeView نام بخش اصلی پنل HTML است که محتویات صفحه را بصورت مرتب شده و درختی نمایش می‌دهد.
تگ (Node) هایی که دارای فرزند می‌باشند ، یک علامت + یا - در کنارشان وجود دارد که با کلیک بروی آن می‌توانید آن تگ را باز/بسته کنید. همچنین این کار با کلید‌های + و - یا Right Arrow و Left Arrow از روی کیبورد هم قابل انجام است.
برای باز کردن یک لینک در این قسمت هم از ترکیب Ctrl و کلیک موس کمک بگیرید.
در نهایت زمانی که موس را بروی یک تگ قرار می‌دهید ، محیطی که توسط آن تگ در صفحه اشغال شده است بصورت رنگی مشخص می‌شود.
که رنگ زرد به معنی محیط margin ، رنگ آبی تیره به معنی محیط padding و رنگ آبی روشن هم به معنی محیط محتوا است.

 Panel Toolbar
این قسمت در بالای پنل HTML قرار دارد که گزینه‌های زیر را دارا می‌باشد:



  •  Break On Mutate
    این دکمه امکان توقف کد JavaScript ای که سعی در ویرایش محتوای صفحه را دارد ، می‌دهد.
    زمانی که FireBug تشخیص دهد که کدی سعی در ویرایش محتوا دارد ، شما را به خط مورد نظر از کد ، در پنل Script منتقل می‌کند.

  • Edit
    این دکمه برای ویرایش مستقیم محتوای یک تگ بکار می‌رود
    نکته‌ی جالب در ویرایش محتوا در فایرباگ این است که تغییرات در لحظه اعمال می‌شوند و نیاز به عمل بروزرسانیِ جداگانه نیست. برای مثال در همین قسمت Edit ، با هر ویرایش محتوا ، تغییرات در لحظه اعمال می‌شوند.

  •  Element Path
    زمانی که یک تگ را در FireBug انتخاب می‌کنید ، لیستی از تگ‌ها که از تگ جاری شروع و به تگ ریشه ختم می‌شود ، نمایش داده می‌شود که با کلیک بروی هرکدام ، همان به عنوان تگ فعلی یا انتخاب شده تعیین می‌شود.
    نتیجه‌ی عملیاتی که بروی این تگ‌های انجام می‌دهید (حرکت موس و راست کلیک کردن) معادل همان عملیات بروی تگ‌های نمایش داده شده در قسمت اصلی (NodeView) است.



Options Menu

هر تب یا پنل در فایرباگ دارای یک سری تنظیمات است که Options Menu نام دارد. تب HTML هم دارای یک سری تنظیمات است که دانشتن آنها بسیار به شما کمک خواهد کرد.
این منو با کلیک کردن بروی فلش تب () یا راست کلیک کردن بروی تب ظاهر می‌شود.


  • Show Full Text
    در صورت فعال بودن ، متون بصورت کامل نمایش داده می‌شوند ، در غیراینصورت بصورت خلاصه شده نمایش داده می‌شوند.

  • Show White Space
    در صورت فعال بودن ، فضاهای خالی ، کرکترهای خط جدید و ... را نمایش می‌دهد.


  • Show Comments
    در صورت فعال بودن ، کامنت‌ها را هم نمایش می‌دهد


  • سه گزینه ی Show Entities As Symbols ، Show Entities As Names و Show Entities As Unicode ، نوع نمایش کرکترهای ویژه را تعیین می‌کنند.

  • Highlight Changes
    در صورت فعال بودن ، تگ تغییر یافته توسط JavaScript (یا تگ والدی که قابل مشاهده باشد) Highlight می‌شود

  • Expand Changes
    در صورت فعال بودن ، زمان تغییر دادن یک تگ توسط JavaScript ، والدهای آن تگ باز (Expand) می‌شوند

  • Scroll Changes Into View
    در صورت فعال بودن این گزینه ، هنگام تغییر یک تگ در صفحه توسط JavaScript ، قسمت NodeView به قسمت تغییر بافته حرکت می‌کند.
    (فعال بودن این گزینه هنگام بررسی سایت هایی که اسلایدر یا سیستم هایی مشابه دارند ، باعث میشه که نتوانید بروی قسمت‌های سایت تمرکز کنید و مدام اسکرول به قسمت تغییرات منتقل می‌شود.)

  • Shade Box Model
    در صورت فعال بودن ، فضای margin , padding و content را به شکلی که در بالا گفته شد نمایش می‌دهد ، در غیر اینصورت فقط یک خط دور تگ نشان می‌دهد.

  • Show Quick Info Box
    در صورت فعال بودن ، یک پاپ‌آپ به همراه اطلاعات مختصری از تگ در صفحه نمایش می‌دهد.




Context Menu
اگر بروی یک تگ راست کلیک کنید ، یک منو نمایش داده می‌شود ، در این قسمت با گزینه‌های این منو آشنا می‌شویم.

  • Copy HTML
    خود تگ و محتوایش را بصورت HTML در حافظه کپی می‌کند.

  • Copy innerHTML
    محتوای تگ را در حافظه کپی می‌کند.

  • Copy XPath
    آدرس XPath تگ را در حافظه کپی می‌کند.

  • Copy CSS Path
    CSS Selector تگ را در حافظه کپی می‌کند.

  • Log Events
    رویدادهای تگ را در پنل Console ثبت می‌کند. (کلیک موس ، فشردن کلید ، ...)
    برای لغو لاگ کردن ، مجددا بروی تگ راست کلیک کرده و این گزینه را از حالت انتخاب خارج کنید.

  • Scroll Into View
    صفحه را به جایی که تگ قابل نمایش است منتقل می‌کند.

  • New Attribute...
    یک attribute جدید به تگ اضافه می‌کند.
    برای لغو عملیات ، دکمه‌ی Esc را بزنید.

  • Edit Attribute "<attribute name>"...
    اگر بروی یک attribute راست کلیک کرده باشید ، این گزینه و گزینه‌ی بعدی را مشاهده خواهید کرد.
    معادل کلیک بروی نام attribute است ، نام را به حالت ویرایش درمی آورد.

  • Delete Attribute "<attribute name>"
    attribute را حذف می‌کند.

  • Edit HTML...
    تگ را به حالت ویرایش می‌برد.
    معادل انتخاب تگ ، زدن کلید Edit است.

  • Delete Element
    تگ را حذف می‌کند.
    راه دیگر حذف یک تگ ، انتخاب تگ و فشردن کلید Del از کیبورد است.

  • Expand/Contract All
    تگ و Childهایش را باز/بسته می‌کند. (بجز تگ های <script> , <style> , <link>)
    می‌توان با ترکیب کلیدShift + * هم این کار را انجام داد که در این حالت تگ‌های فوق هم شامل باز/بسته شدن می‌شوند.

  • Break On Attribute Change
    مانع اجرای کد JavaScript ای که attribute تگ را تغییر می‌دهد می‌شود و فایرباگ به خطی که باعث این عمل شده است در پنل Script منتقل می‌شود.
    به عبارتی دیگر یک Break Point در خط JavaScript ای که باعث ویرایش attribute می‌شود قرار می‌دهد.

  • Break On Child Addition or Removal
    مشابه توضیح قبل ، Break Point را در خطی که باعث اضافه/حذف شدن تگِ Child شده است قرار می‌دهد.

  • Break On Element Removal
    مشابه توضیح قبل ، Break Point را در خطی که باعث حذف شدن تگ شده است قرار می‌دهد.

  • Inspect in DOM Tab
    تگ فعلی را در پنل DOM ، برای بررسی باز می‌کند.


در قسمت بعدی با پنل‌های سمت راست (Side Panels) آشنا می‌شویم.
اشتراک‌ها
ایجاد دیالوگ bootstrap با استفاده از Tag Helper
<modal title="Modal title">
    <modal-body>
        <p>One fine body&hellip;</p>
    </modal-body>
    <modal-footer>
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
    </modal-footer>
</modal>
ایجاد دیالوگ bootstrap با استفاده از Tag Helper
نظرات مطالب
مشکل همزمانی خواندن و به روز رسانی اطلاعات در برنامه‌های وب
به جز وب، این مشکل در چت بات‌ها هم مرسوم است. یادمه یک بات برای پروموشن و دریافت تخفیف یک شرکت ایرانی نوشته بودیم. اما چون race condition رو توش درست هندل نکرده بودیم، بعضی از تینیجرها یک روشی رو یاد گرفته بودند که اینترنت گوشی شون رو قطع میکردند، ده‌ها بار روی دکمه دریافت تخفیف (inline button) کلیک میکردند، بعد اینترنت رو که وصل میکردند، تلگرام همه درخواست‌ها رو یکجا (در کسری از ثانیه) میفرستاد به webhook ما و ممکن بود چندتا کد تخفیف، برای کاربر ارسال بشه.