عنوان:

‫نمایش رکوردها به ترتیب اولویت به کمک jQuery UI sortable در ASP.NET MVC


نویسنده: امیرحسین مرجانی
تاریخ: ۱۳۹۱/۰۹/۰۸ ۲۰:۴۷
آدرس: www.dntips.ir

همان طور که می‌دانید کاربرد پذیری در خیلی از پروژه‌ها حرف اول رو می‌زند و کاربر دوست دارد کارهایی که انجام می‌دهد خیلی راحت و با استفاده از موس باشد.یکی از کار هایی که در اکثر پروژه‌ها نیاز است ، چیدمان ترتیب رکورد‌ها است. ما می‌خواهیم در این پست ترتیبی اتخاذ کنیم که کاربر بتواند رکورد‌ها را به هر ترتیبی که دوست دارد نمایش دهد.

از توضیحاتی که قبلا  دادم مشخص است که این کار احتمالا در ASP.NET WebForm  کار سختی نیست ولی این کار باید در MVC  از ابتدا طراحی شود.

طرح سوال : یک سری رکورد از یک Table داریم که می‌خواهیم به ترتیب وارد شدن رکورد‌ها نباشد و  ترتیبی که ما می‌خواهیم نمایش داده شود.

پاسخ کوتاه : خب باید ابتدا یک فیلد (برای اولویت بندی)  به Table  اضافه کنیم  بعد اون فیلد رو بنا به ترتیبی که دوست داریم رکورد‌ها نمایش داده شود پر کنیم (Sort  می کنیم ) و در آخر هم هنگام نمایش در View رکورد‌ها را بر اساس این فیلد نمایش می‌دهیم.

(این پست هم در ادامه پست قبلی در همان پروژه است و از همان Table  ها استفاده شده است)

اضافه کردن فیلد :

ابتدا یک فیلد به Table  مورد نظر اضافه می‌کنیم. من اسم این فیلد رو Priority گذاشتم. Table  من چنین وضعیتی دارد.

افزودن فایل‌های jQuery UI :

در این مرحله شما نیاز دارید فایل‌های مورد نیاز برای Sort  کردن رکورد‌ها را اضافه کنید. شما می‌توانید فقط فایل‌های مربوط به Sortable  را به صفحه خودتان اضافه کنید و یا مثل من فایل هایی که حاوی تمام قسمت‌های jQuery UI  هست را اضافه کنید.

من برای این کار از Section  استفاده کردم ، ابتدا در Head  فایل Layout  دو Section  تعریف کردم برای CSS  و JavaScript . و فایل‌های مربوط به Sort کردن را در صفحه ای که باید عمل Sort انجام بشود در  این Section ها قرار دادم.

فایل Layout

<head>
    <meta charset="utf-8" />
    @RenderSection("meta", false)
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/~Site.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/redactor/css/redactor.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/css/bootstrap-rtl.min.css")" rel="stylesheet" type="text/css" />
    @RenderSection("css", false)
    <script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/js/bootstrap-rtl.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/redactor/redactor.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    @RenderSection("js", false)

</head>

فایل Index.chtml  در پوشه کنترلر Type


@model IEnumerable<KhazarCo.Models.Type>
@{
    ViewBag.Title = "Index";
    Layout = "~/Areas/Administrator/Views/Shared/_Layout.cshtml";
}
@section css
{<link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" />
}
@section js
{
    <script src="@Url.Content("~/Scripts/jquery-ui-1.9.0.min.js")" type="text/javascript"></script>
}


در آخر فایل Index.chtml  به اینصورت شده است:

<h2>
    نوع ها</h2>
<p>
    @Html.ActionLink("ایجاد یک مورد جدید", "Create", null, new { @class = "btn btn-info" })
</p>
<table>
    <thead>
        <tr>
            <th>
                عنوان
            </th>
            <th>
                توضیحات
            </th>
            <th>
                فعال
            </th>
            <th>
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.OrderBy(m => m.Priority))
        {
            <tr id="@item.Id">
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @(new HtmlString(item.Description))
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.IsActive)
                </td>
                <td>
                    @Html.ActionLink("ویرایش", "Edit", new { id = item.Id }, new { @class = "btnEdit label label-warning" })
                    |
                    @Html.ActionLink("مشاهده", "Details", new { id = item.Id }, new { @class = "btnDetails label label-info" })
                    |
                    @Html.ActionLink("حذف", "Delete", new { id = item.Id }, new { @class = "btnDelete label label-important" })
                </td>
            </tr>
        }
    </tbody>
</table>

** توجه داشته باشید که من به هر tr  یک id  اختصاص داده ام که این مقدار id  همان مقدار فیلد Id  همان رکورد هست ، ما برای مرتب کردن به این Id  نیاز داریم (خط 25).

افزودن کد‌های کلاینت:

حالا باید کدی بنویسم که دو کار را برای ما انجام دهد : اول حالت Sort  پذیری را به سطر‌های Table  بدهد و دوم اینکه هنگامی که ترتیب سطر‌های تغییر کرد ما را با خبر کند:


<script type="text/javascript">
    $(function () {
        $("table tbody").sortable({
            helper: fixHelper,
            update: function (event, ui) {
                jQuery.ajax('@Url.Action("Sort", "Type", new { area = "Administrator" })', {
                    data: { s: $(this).sortable('toArray').toString() }
                });
            }
        }).disableSelection();
    });
    var fixHelper = function (e, ui) {
        ui.children().each(function () {
            $(this).width($(this).width());
        });
        return ui;
    };
</script>


توضیح کد :

در این کد ما حالت ترتیب پذیری را به Table  می دهیم و هنگامی که عمل Update  در Table  انجام شد تابع مربوطه اجرا می‌شود. ما در این تایع، ترتیب جدید سطر‌ها را می‌گیریم ( ** به کمک مقدار Id  که به هر سطر دادیم ، این مقدار Id  برابر بود با Id خود رکورد در Database )  و به کمکjQuery.ajax  به تابع Sort  از کنترلر Type  در منطقه (area ) Administrator  ارسال می‌کنیم و در آنجا ادامه کار را انجام میدهیم.

تابع fixHelper  هم به ما کمک می‌کند که هنگامی که سطر‌ها از جای خود جدا می‌شوند ، دارای عرض یکسانی باشند و عرض آن‌ها تغییری نکند.


افزودن کد Server:

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


        public EmptyResult Sort(string s)
        {
            if (s != null)
            {
                var ids = new List<int>();
                foreach (var item in s.Split(','))
                {
                    ids.Add(int.Parse(item));
                }
                int intpriority = 0;

                foreach (var item in ids)
                {
                    intpriority++;
                    db.Types.Single(m => m.Id == item).Priority = intpriority;
                }
                db.SaveChanges();
            }
            return new EmptyResult();
        }

در ایتدا مقادیر Id  که از کلاینت  به صورت String  ارسال شده است را می‌گیریم و بعد به همان ترتیب ارسال در لیستی از int قرار می‌دهیم ids.

سپس به اضای هر رکورد Type  مقدار اولویت را به فیلدی که برای همین مورد اضافه کردیم Priority اختصاص می‌دهیم. و در آخر هم تغییرات را ذخیره می‌کنیم. (خود کد کاملا واضح است و نیاری به توضیح بیشتر نیست )

حالا باید هنگامی که لیست Type  ها نمایش داده می‌شود به ترتیب (OrderBy) فیلد Priority    نمایش داده شود پس تابع Index را اینطور تغییر می‌دهیم.

        public ViewResult Index()
        {
            return View(db.Types.Where(m => m.IsDeleted == false).OrderBy(m => m.Priority));
        }

این هم خروجی کار من:

این عکس مربوط به است به قسمت مدیریت پروژه شیرآلات مرجان خزر


نظرات

  • مهمان در ۱۳۹۱/۰۹/۰۸ ۲۱:۱۲

    «این کار احتمالا در ASP.NET WebForm  کار سختی نیست»

    اتفاقا کار ساده‌ای نیست و همین مراحل باید طی شود. ضمن اینکه کار با jquery ajax در آنجا به این یک دستی نیست. نیاز است در code behind فرم، متد وب سرویس مانندی  به صورت استاتیک تعریف شود (که خودش سبب می‌شود تا دسترسی به اعتبار سنجی توکار مبتنی بر فرم‌ها محدود شود) یا اینکه از یک هندلر مجزا بجای یک اکشن متد کمک گرفته شود ... خلاصه خیلی داستان دارد.

    • امیرحسین مرجانی در ۱۳۹۱/۰۹/۰۸ ۲۱:۱۶
      من خودم این کار رو در webform انجام ندادم و خیلی هم با سختی هاش آشنایی ندارم.
      ولی فکر کردم شاید با update panel  بشه این کار رو راحت انجام داد.
      خب نظرتون راجب به راه حل من چیه؟
      راه حل خوبی ارائه دادم؟ متن قابل فهم بود؟
      • مهمان در ۱۳۹۱/۰۹/۰۸ ۲۱:۲۰

        بسیار عالی.

        فقط در مورد update panel ... تنها کاری که انجام می‌دهد کپسوله کردن ارسال مقادیر به سرور است به صورت ajax سازگار با کنترل‌های دارای view state . بیشتر از این کاری انجام نمی‌دهد. مابقی آن اگر یک سری کنترل در toolkit آن موجود باشد برای این کارها یا خیر. این toolkit هم محدود است و آنچنان به روز نمی‌شود.

        • امیرحسین مرجانی در ۱۳۹۱/۰۹/۰۸ ۲۱:۲۳
          ممنونم از پاسختون
          از اینکه سوییچ کردم روی MVC  خوشحالم :)
          البته یکی از دلایلی هم که این کار رو کردم همین راحتی استفاده Ajax  بود.
      • محسن موسوی در ۱۳۹۱/۰۹/۰۹ ۰:۲۴
        با استفاده از Web API Controller تقریبا به همین راحتی امکانپذیره. تغییر زیادی هم نیاز نداره. 
        • امیرحسین مرجانی در ۱۳۹۱/۰۹/۰۹ ۰:۳۷
          ممنونم آقای موسوی
          قابل توجه آقا سعید (احتمالا این مطلب برای شما مفیده )
        • مهمان در ۱۳۹۱/۰۹/۰۹ ۰:۵۲
          - نیاز به vs2012 داره.
          - نمیشه یک فرم رو strongly typed تعریف کرد مثل مثال بالا:
          @model IEnumerable<KhazarCo.Models.Type>
          • محسن موسوی در ۱۳۹۱/۰۹/۰۹ ۱۰:۲۱
            بحث در مورد UI نبود.بحث در مورد استفاده کردن بود.
            در ضمن در Web Form‌ها نیز از امکاناتی شبیه به MVC می‌توان بهره برد.(^)
            • مهمان در ۱۳۹۱/۰۹/۰۹ ۱۰:۴۶

              فقط mvc هست که امکان استفاده از چند view engine را با هم دارد.

              صفحه‌ای که لینک دادید مربوط است به asp.net web pages و نه web forms. این web pages برای کار با webmatrix طراحی شده.

              • محسن موسوی در ۱۳۹۱/۰۹/۰۹ ۱۱:۴
                - استفاده از این قابلیت در Web Page‌ها توسط VS
                - استفاده از این قابلیت در Web Form‌ها توسط VS
                هر دو مورد توسط VS استفاده شده است.
                • مهمان در ۱۳۹۱/۰۹/۰۹ ۱۱:۴۵
                  <iframe src="/twitter" frameborder="0" height="400" width="270" scrolling="no"></iframe>

                  فلسفه و کاربرد web pages متفاوت است. در مورد وب فرم‌ها هم نمی‌تونید از razor استفاده کنید چون در یک فایل نمی‌شود از دو موتور view استفاده کرد. موتور view وب فرم‌ها، نامش همین web forms view engine است و قابل تعویض هم نیست (برخلاف MVC). در مقاله‌ای که لینک دادید، داره از یک iframe استفاده می‌کنه برای الحاق razor.

                  به علاوه در مورد Web API که کلا مطلب جدیدی نیست. نسخه ساده شده WCF است.

                  • محسن موسوی در ۱۳۹۱/۰۹/۰۹ ۱۲:۲۷
                    دوست عزیز
                    بحث ما در مورد توانایی انجام موارد فوق الذکر بود. نه اینکه حالا چون ...
                    به توانایی‌های MVC شکی نیست.مسئله اصلی اینه که آیا پست جاری را میتوان به راحتی با WebPage و یا WebForm انجام داد؟!
                    چه از طریق قابلیت‌های ASP.NET Web Form و ASP.NET Web Page راحته.
                    استفاده از Razor چه از طریق Jquery و یا Iframe (نظر قبلی)و یا به طور مستقل و یا روش‌های دیگر در ASP.NET Web Form و ASP.NET Web Page 
                    و استفاده معمولی با توانایی‌های DataBind
                    یا بطور کامل از Razor View Engine در Web Page
                    در نهایت کار سختی نیست.

                    • مهمان در ۱۳۹۱/۰۹/۰۹ ۱۲:۴۳

                      razor بی‌جهت اینجا طرح شد. من در مورد strongly typed view سطری رو نوشتم. این نوع viewها مستقل از نوع view engine هستند. در mvc حتی با موتور web forms هم میشه یک چنین viewهای تحت نظر کامپایلری رو  با پشتیبانی کامل از intelisense داشت.

  • مهمان در ۱۳۹۱/۰۹/۰۸ ۲۲:۴
    سلام
    مطلب مفیدی بود ولی می‌تونستید بیشتر توضیح بدید.
    برای ما افراد مبتدی درک بعضی قسمت‌ها کمی مشکل هست.
    ولی باز هم ممنونم
    • امیرحسین مرجانی در ۱۳۹۱/۰۹/۰۸ ۲۲:۷
      سلام
      بفرمایید کدوم قسمت‌ها تا بیشتر توضیح بدم.
    • وحید نصیری در ۱۳۹۱/۰۹/۰۸ ۲۲:۲۱
      پیشنیازهای مطلب جاری:
      - در مورد RenderSection : (^)
      - مقدمه‌ای بر jQuery Ajax در MVC : (
      ^)
      - db.SaveChanges : کل مباحث Entity framework سایت (
      ^)
      - EmptyResult و کلا خروجی‌های اکشن متدها: (
      ^)
      - علت استفاده از @Url.Action Sort در حین آدرس دهی: (
      ^)

      • امیرحسین مرجانی در ۱۳۹۱/۰۹/۰۸ ۲۲:۲۳
        واقعا تشکر می‌کنم بابت این لیستی که ارائه کردید.
        بحث تکمیل شد
        به نظر شما باید بیشتر به جزییات اهمیت داده می‌شد؟
        • وحید نصیری در ۱۳۹۱/۰۹/۰۸ ۲۲:۴۱
          ضرورتی نداره. چون واقعا به اندازه لیستی که عنوان شد نیاز به پیشنیاز درک این مطالب هست و فرصت تکرار آن‌ها نیست. این مطالب جدید، یک سری مطالب تکمیلی هستند نه مطالب پایه و از صفر.