صفحه بندی، فیلترینگ و مرتب سازی با ویرایش در modal dialog

شنبه 23 آبان 1394

در این مقاله قصد داریم مثالی در MVC پیاده سازی کنیم که شامل یک نوار منو در سمت راست صفحه باشد و تمام موجودیت ها را به صورت PartialView درون یک HTML DIV بارگذاری کند. همچنین تمام افزودن ها ، ویرایش و حذف و نمایش جزئیات در Modal صورت گیرد. تمام این قابلیت ها را به همراه امکاناتی دیگر مانند Loadmask در MVC پیاده سازی خواهیم کرد.

صفحه بندی، فیلترینگ و مرتب سازی با ویرایش در modal dialog

 در این مقاله قصد داریم مثالی در MVC پیاده سازی کنیم که شامل یک نوار منو در سمت راست صفحه باشد و تمام موجودیت ها را به صورت PartialView درون یک HTML DIV  بارگذاری کند. همچنین تمام افزودن ها ، ویرایش و حذف و نمایش جزئیات در Modal صورت گیرد. تمام این قابلیت ها را به همراه امکاناتی دیگر مانند  Loadmask در MVC پیاده سازی خواهیم کرد.

 صفحه اصلی با سه Div برای لوگو ، جهت یابی و محتوا به صورت زیر ساخته می شود:

<table>
    <tr>
        <td colspan="2">
            <div id="logo" </div>
        </td>
    </tr>
    <tr style="height:600px">
        <td id="navigation" style="width:200px; ">
            <div>
            </div>
        </td>
        <td>
            <div id="contentFrame" />
        </td>
    </tr>
</table>

div جهت یابی خود شامل منو و PartialView هایی است که درون بخش محتوا بارگذاری خواهند شد.بنابراین برای آن یک Actionlinke درون Div جهت یابی قرار می دهیم.

@Html.ActionLink("Customers", "Index", "Customers", new { }, 
new { id = "btnCustomers", @class = "btn btn-default btn-xs" })

برای پیدا کردن این موجودیت به وسیله جاوا اسکریپت برای آن یک Id با نام btnCustomer قرار دادیم.

$(function () {
   //find ActionLink throuth its id 
   $('#btnCustomers').click(function () {
        //show loadmask when it is loading data
        $('#contentFrame').mask("waiting ...");
        //load data in to contentFrame DIV
        $('#contentFrame').load(this.href, function (response, status, xhr) {
            //to disappear loadmask after loading finished
            $('#contentFrame').unmask("waiting ...");
        });
        return false;
    });
});

به دلیل آنکه ممکن است بارگذاری PartialView کمی طولانی شود از Loadmask درون Contentframe هنگام لود داده ها استفاده میکنیم.

$('#contentFrame').mask("waiting ...");

بعد از پایان بارگذاری PartialView  باید Loadmask را نیز پایان داد :

$('#contentFrame').unmask("waiting ...");

باید فایلهای JS و CSS به پروژه اضافه شوند:

~/Scripts/jquery.mask.js
~/Content/jquery.loadmask.css

می توانید این دو فایل را درون BundleConfig.cs قرار دهید. همچنین باید فایل تصویر را نیز اضافه کرد.

~/images/loading.gif

صفحه بندی

برای صفحه بندی از بسته Pagedlist.mvc استفاده کنید. می توانید آن را با استفاده از NuGet Pakage Manager در برنامه خود نصب کنید. لازم است تا این کدهار ا در بالای index view مشتری قرار دهید.

@using PagedList.Mvc
@model PagedList.IPagedList<CompleteMVCExample.Models.Customer>

بعد از View  لازم است تا Page Control را قرار دهیم.

Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
<div id="myPager">
    @Html.PagedListPager(Model, page => Url.Action("Index", 
           new { page, sort = ViewBag.CurrentSort, search = ViewBag.CurrentSearch }))
</div>

و به دلیل آنکه Pager درون PartialView قرار دارد ، رویداد کلیک آن را برای بارگذاری داده ها در Contentframe به صورت زیر می نویسیم.

$('#myPager').on('click', 'a', function (e) {
    //prevent action link normal functionality
    e.preventDefault();
    $('#contentFrame').mask("waiting ...");
    //ajax call index controller action
    $.ajax({
        url: this.href,
        type: 'GET',
        cache: false,
        success: function (result) {
            $('#contentFrame').unmask("waiting ...");
            //load returned data inside contentFrame DIV
            $('#contentFrame').html(result);
        }
    });
});

دوباره می توانید توابع mask و unmask را استفاده کنید. درون کنترلر یک argument از نوع int و null پذیر قرار می دهیم:

public ActionResult Index(int? page)

بعد از پایان تابع index باید PageSize و PageNumber را تنظیم کنیم .

int pageSize = 3;
int pageNumber = page ?? 1;

و در پایان از ToPagedList بجای Tolist استفاده می شود و PageSize  و PageNumber  به PartialView برگردانده می شود.

return PartialView(customers.ToPagedList(pageNumber, pageSize));

 

مرتب سازی

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

@Html.ActionLink("Customer Name", "Index", new { search = ViewBag.CurrentSearch, 
sort = ViewBag.CurrentSort == "CustomerName_ASC" ? "CustomerName_DESC" : "CustomerName_ASC" }, 
new { @class = "SortButton" })

و سپس رویداد کلیک را برای فراخوانی index  از طریق  Ajax و بارگذاری داده ها در Contentframe به صورت زیر می نویسیم.

//wire up all sort ActionLinks throuth their class name 
$(".SortButton").click(function (e) {
    e.preventDefault();
    $('#contentFrame').mask("waiting ...");
    $.ajax({
        url: this.href,
        type: 'POST',
        cache: false,
        success: function (result) {
            $('#contentFrame').unmask("waiting ...");
            //load returned data inside contentFrame DIV
            $('#contentFrame').html(result);
        }
    })
});

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

if(!String.IsNullOrWhiteSpace(sort))
         {
             if(sort == "CustomerName_ASC")
                 customers = customers.OrderBy(c => c.CustomerName);
             else if (sort == "CustomerName_DESC")
                 customers = customers.OrderByDescending(c => c.CustomerName);
             else if (sort == "Address_ASC")
                 customers = customers.OrderBy(c => c.Address);
             else if (sort == "Address_DESC")
                 customers = customers.OrderByDescending(c => c.Address);
             else if (sort == "Phone_ASC")
                 customers = customers.OrderBy(c => c.Phone);
             else if (sort == "Phone_DESC")
                 customers = customers.OrderByDescending(c => c.Phone);
         }
         else
         {
             customers = customers.OrderBy(c => c.CustomerName);
         }

 

فیلترینگ

در اینجا فقط برای نام مشتریان فیلترینگ قرار داده ایم. برای این کار به یک textbox و یک دکمه در View  نیاز داریم.

<td>@Html.TextBox("search", ViewBag.CurrentSearch as string, new { @class = "form-control" }) </td>

<td>@Html.ActionLink("Filter", "Index", new { sort = ViewBag.CurrentSort, search = "xyz" }, 
new { @class = "btn btn-default", btnName = "FilterCustomer" })</td>

رویداد کلیک را برای فراخوانی index action به صورت زیر می نویسیم که اگر نتیجه جستجو موفقیت آمیز بود داده ها را در Contentframe نمایش دهد.

//find filter ActionLink throuth HTML5 attribute named btnName
$("a[btnName=FilterCustomer]").click(function (e) {
    e.preventDefault();
    var search = $('input[name=search]').val();
    this.href = this.href.replace('xyz', search);
    $('#contentFrame').mask("waiting ...");
    $.ajax({
        url: this.href,
        type: 'POST',
        cache: false,
        success: function (result) {
            $('#contentFrame').unmask("waiting ...");
            $('#contentFrame').html(result);
        }
    });
});

 

عمل افزودن

افزودن ، ویرایش و جزئیات همانند یکدیگر هستند ، در اینجا فقط افزودن را مورد بررسی قرار می دهیم . در ابتدای View  باید نام کنترلر، action و متد را برای فرم تعریف کنیم.

@using (Html.BeginForm("Create", "Customers", FormMethod.Post, new { id = "myForm" }))

درون index View  مشتری یک دکمه برای ایجاد Actionlink قرار میدهیم تا از طریقjQuery به این عنصر دسترسی داشته باشیم.

@Html.ActionLink("Create New", "Create", new { id = -1 }, 
                            new { btnName = "btnCreate", @class = "btn btn-default" })

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

$(function () {
     //&hellip;&hellip;
     $.ajaxSetup({ cache: false });
     setDialogLink($('a[btnName=btnCreate]'),'Create Customer', 500, 600, "contentFrame", 
     //&hellip;..

تابع setDialogLink شش مولفه به صورت زیر میگیرد:

    . element : شیء جاوا اسکریپت actionlink

    . DialogTitle : عنوان Modal را نشان می دهد.

    . DialogHeight : ارتفاع Modal  را نشان می دهد.

    . DialogWidth : عرض Modal  را نشان می دهد.

    . UpdateTargetID :   در Div  محتوا یا Content نشان دهنده ID آن است.

    . UpdateUrl : آدرسی از index controller action که بعد از ذخیره داده ها دوباره بارگذاری می شود.

و رویداد کلیک برای ایجاد Modal به صورت زیر می باشد :

function setDialogLink(element, dialogTitle, dialogHeight, dialogWidth, updateTargetId, updateUrl) {

    // Wire up the click event of any dialog links
    element.on('click', function () {

        // Generate a unique id for the dialog div
        var dialogId = 'uniqueName-' + Math.floor(Math.random() * 1000)
        var dialogDiv = "<div id='" + dialogId + "'></div>";

        // Load the form into the dialog div
        $(dialogDiv).load(this.href, function () {
            $(this).dialog({
                modal: true,
                resizable: false,
                title: dialogTitle,
                height: dialogHeight,
                width: dialogWidth,
                buttons: {
                    "Save": function () {
                        // Manually submit the form                        
                        var form = $('form', this);
                        $(form).submit();
                    },
                    "Cancel": function () {
                        $(this).dialog('close');
                    }
                },
                close: function () {

                    // It turns out that closing a jQuery UI dialog
                    // does not actually remove the element from the
                    // page but just hides it. For the server side
                    // validation tooltips to show up you need to
                    // remove the original form the page
                    $('#' + dialogId).remove();
                }
            });

            // Enable client side validation
            $.validator.unobtrusive.parse(this);

            // Setup the ajax submit logic
            wireUpForm(this, updateTargetId, updateUrl);
        });
        return false;
    });

}

تابع wireUpForm درون setDialogLink برای تابع ارسال در Modal به صورت زیر نوشته می شود.

function wireUpForm(dialog, updateTargetId, updateUrl) {
    $('form', dialog).submit(function () {

        // Do not submit if the form
        // does not pass client side validation
        if (!$(this).valid())
            return false;

        // Client side validation passed, submit the form
        // using the jQuery.ajax form
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                // Check whether the post was successful
                if (result.success) {
                    // Close the dialog
                    $(dialog).dialog('close');

                    // Reload the updated data in the target div
                    $("#" + updateTargetId).load(updateUrl);
                    //$(this).dialog('destroy').remove()
                } else {
                    // Reload the dialog to show model errors                    
                    $(dialog).html(result);

                    // Enable client side validation
                    $.validator.unobtrusive.parse(dialog);

                    // Setup the ajax submit logic
                    wireUpForm(dialog, updateTargetId, updateUrl);
                }
            }
        });
        return false;
    });
}

لازم نیست HttpPost عمل افزودن را تغییر دهیم ، فقط لازم است زمانی که داده ها با موفقیت ذخیره شدند یک شیء Json برگردانده شود.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "CustomerID,CustomerName,Phone,Address")] Customer customer)
{
    if (ModelState.IsValid)
    {
        db.Customers.Add(customer);
        db.SaveChanges();
        return Json(new {success = true});
    }
    return PartialView(customer);
}

 

عمل حذف

ما صفت btnName  را از Delete Actionlink به صورت  btnDelete  تنظیم کردیم تا بتوان از طریق جاوا اسکریپت آن را پیدا کرد.

@Html.ActionLink("Delete", "Delete", new { id = item.CustomerID }, 
                    new { @class = "btn btn-default btn-xs" , btnName="btnDelete"})

سپس رویداد کلیک آن را برای فراخوانی عمل حذف در  کنترلر ایجاد میکنیم. برای اطمینان کاربر از حذف داده مورد نظر از یک تابع جاوا اسکریپت با نام Confirm استفاده میکنیم.

$('a[btnName=btnDelete]').click(function (e) {
    e.preventDefault();
    var confirmResult = confirm("Are you sure?");
    if (confirmResult) {
        $('#contentFrame').mask("waiting ...");
        $.ajax(
            {
                url: this.href,
                type: 'POST',
                data: JSON.stringify({}),
                dataType: 'json',
                traditional: true,
                contentType: "application/json; charset=utf-8",
                success: function (data) {
                    if (data.success) {
                        //reload data inside contentFrame
                        $('#contentFrame').load("/Customers/Index");
                    }
                    else {
                        //show error message
                        alert(data.errormessage);
                    }
                    $('#contentFrame').unmask("waiting ...");
                },
                error: function (data) {
                    alert("An error has occured!!!");
                    $('#contentFrame').unmask("waiting ...");
                }
            });
    }

})

  درون HttpPost عمل حذف نیاز است که یک شیء Json بازگردانده شود حتی اگر عمل حذف با اشکال مواجه شد.

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید