65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 jQueryUI、jQuery AJAX、ASP.NET Web Methods 和可分离的 Entity Framework 数据存储库进行拖放排序列表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (25投票s)

2011年7月20日

CPOL

5分钟阅读

viewsIcon

69717

downloadIcon

2676

带有 jQuery 持久化的排序列表。

引言

多年来,我曾多次遇到需要在 Web 上创建和排序列表的界面。我花了很长时间寻找一个简洁简单的界面来实现这一点,但从未找到我喜欢的,所以这是我创建的解决方案。

代码

要查看此演示,请访问:http://www.wesgrant.com/SampleCode/SortableList/SortableListDemo.aspx

此演示的数据模型是一个非常简单的母子关系,其中有一个列表和列表项。该模型启用了延迟加载,因此我将引入一个可分离的数据存储库,以便 Entity 对象可以作为 JSON 返回给 jQuery AJAX 调用。

此演示包含一个具有动画面板的界面,并且能够创建新列表、添加、编辑和删除列表项、使用多个样式表类预览列表以及更改用户界面以使用任何 jQueryUI 主题。在本教程中,我希望演示如何通过 Web 方法使用 jQuery AJAX 传递 JSON 编码的 Entity Framework 对象,因此我将不涵盖许多功能。请随意下载完整的源代码,并查看添加、编辑和删除功能,以及使用 jQueryUI 的 show 和 hide 函数使界面更具交互性,这我将在本教程中不进行介绍。

使用 jQuery AJAX 和 Web Methods 获取可用列表并显示它们

为了获取已创建的列表,我将使用一个 jQuery AJAX 调用到一个名为 GetSortableListsWebMethod。我包含了 pageNumberpageSize 参数,这些参数可以用于列表分页,但在此代码中尚未实现分页。这些参数只是用来展示数据如何传递到 Web 方法。成功状态会调用 drawListofLists 函数,但我尚未介绍该函数,但我在下一节中介绍了列表的绘制,这与该函数类似。

function getLists() {
   $.ajax({
       type: "POST",
       url: "AjaxMethods/ListMethods.aspx/GetSortableLists",
       data: "{'pageNumber':'0','pageSize':'0'}",
       contentType: "application/json; charset=utf-8",
       dataType: "json",
       success: function (data) {
          drawListofLists(data.d); 
       },
       error: function (xhr, ajaxOptions, thrownError) {
          showVoodooJavaScriptError(xhr, ajaxOptions, thrownError); 
   }
});

此 AJAX 调用指向以下 Web 方法

[System.Web.Services.WebMethod]
public static List<Data.Model.SortableList> GetSortableLists(int pageNumber, int pageSize)
{
     Data.Repository.SortableListRepository SLR = 
                          new Data.Repository.SortableListRepository();
     return SLR.GetSortableLists(true);
}

这是通往以下存储库方法的桥梁

public Data.Model.SortableList GetSortableList(int sortableListId, bool detach = false)
{
    var results = DataContext.SortableLists.Where
			(l => l.SortableListId == sortableListId);
    if (results.Count() > 0) 
    { 
        var result = results.First(); 
        if (detach) 
            DataContext.Detach(result);
 
        return result;
    }
    else
        return null;
}

请注意,存储库方法有一个可选的可分离参数,默认值为 false。这允许同一个存储库类返回具有导航属性的 Entity 对象以及可以 JSON 编码的仅对象。如果您尝试返回已启用延迟加载的 Entity Framework 对象而不将其分离并删除导航属性,您将收到以下错误

{"Message":"A circular reference was detected while serializing an object 
of type \u0027DragAndDropSortableList.Data.Model.SortableList\u0027.", "StackTrace":" 
at System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, 
StringBuilder sb, Int32 depth, Hashtable objectsInUse, 
SerializationFormat serializationFormat)\r\n at 
System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, 
StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat 
serializationFormat)\r\n...(PART OF THE STACK TRACE OMITED...)",
"ExceptionType":"System.InvalidOperationException"}

绘制排序列表项界面和具有各种样式表的列表预览

请注意下图,当您将项目拖到列表中的某个位置时,会出现一个红色的虚线框,表示可以在该位置放下它。

要检索数据,我们将向 Web 方法发出一个简单的 AJAX 调用,并从服务器接收一个 JSON 编码的项目列表。

function getListItems(sortableListId) {
     $.ajax({
    type: "POST",
    url: "../AjaxMethods/ListMethods.aspx/GetSortableListItems",
    data: "{'sortableListId':'" + sortableListId + "'}",
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (data) {
        drawListItems(data.d);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        showVoodooJavaScriptError(xhr, ajaxOptions, thrownError);
    }
     });
}

这是用于 jQuery 的 AJAX 调用与从数据存储库检索数据之间的桥梁的 Web 方法。请记住,在创建 Web 方法时,函数必须标记为 static

[System.Web.Services.WebMethod]
public static List<Data.Model.SortableListItem> GetSortableListItems(int sortableListId)
{
    Data.Repository.SortableListRepository SLR =
    new Data.Repository.SortableListRepository();
    return SLR.GetSortableListItems(sortableListId);
}

用于从数据模型返回排序列表项的存储库函数

请注意,在下面的函数中,结果始终与数据上下文分离。在其余需要实体附加且导航属性存在的情况下,分离是可选的。对于此函数,如果您希望导航属性存在,您可以简单地检索列表并使用列表的 SortableListItems 导航属性。

public List<Data.Model.SortableListItem> GetSortableListItems(int sortableListId)
{
    var results = DataContext.SortableListItems
                .Where(i => i.SortableListId == sortableListId);
    if (results.Count() > 0)
    {
        List<Model.SortableListItem> returnValue =
        results.OrderBy(i => i.ItemOrder).ToList();
        foreach (var item in returnValue)
            DataContext.Detach(item);
 
        return returnValue;
    }
    else
        return new List<Model.SortableListItem>();
}

获取列表项的 AJAX 调用的成功状态会调用 drawListItems 函数,并将返回的 JSON 编码的项目列表传递进去。此函数创建的项目列表用于通过拖放来移动项目位置以及使用各种样式表预览列表。这是绘制列表项的 JavaScript 代码。在演示中,有用于编辑和删除项目以及添加新项目的按钮。为了简洁起见,我已经将这些代码省略了。

function drawListItems(listItems) {
    sortableList = "<div class=\"SortableList\">";
    for (var index in listItems) {
        sortableList += "<div class=\"SortableListItem\" id=\"" + 
                     listItems[index].SortableListItemId + "\"
        sortableList += style=\"margin-bottom: 5px;\">";
        sortableList += "<div class=\"SortableListHeader\" 
                      style=\"padding: 5px; position:    relative; \">";
        sortableList += listItems[index].Headline;
        sortableList += "</div>";
        sortableList += "<div class=\"SortableListContent\" 
                      style=\"padding: 5px;\">";
        sortableList += "Desc: " + listItems[index].Description + "<br />";
        sortableList += "Link: " + listItems[index].LinkUrl + "<br />";
        sortableList += "</div>";
        sortableList += "</div>";
    }
    sortableList += '</div>'; 
 
    previewList = "<div id=\"sidebar\">";
    previewList += "<div id=\"listPreviewStyle\">";
    previewList += "<ul>";
    for (var indexP in listItems) {
        previewList += " <li>";
        previewList += " <a href='" + listItems[indexP].LinkUrl + "' 
                    target='_blank'>"
        previewList += listItems[indexP].Headline + "</a>";
        previewList += "<span>" + listItems[indexP].Description + "</span>";
        previewList += "</li>";
    }
    previewList += "</ul>";
    previewList += "</div>";
    previewList += "</div>";
 
    $("#PreviewListContent").html(previewList);
    $("#EditList").html(sortableList);
    $(".listbutton").button();
 
    // apply style to the list preview for
    $("#listPreviewStyle").addClass($('#uiCssClass option:selected').text());
    var $StartIndex; 
    // Make the list that was created above Sortable
    $(".SortableList").sortable({
        start: function (event, ui) {
        // Get the start index so no database call 
        //is made if item is dropped in the same order
        $StartIndex = ui.item.index() + 1;
    },
    stop: function (event, ui) {
        // At the end of the drag, if item is in different ordinal position, 
        // update the database using the moveListItem function
        idListItem = ui.item[0].id;
        newListIndex = ui.item.index() + 1;
    if ($StartIndex != newListIndex) {
        moveListItem(idListItem, newListIndex);
        } 
            } 
    });
 
    // Add Jquery UI Classses for the sortable list - This also creates 
    // a box with a red dotted line to show
    // visually where the item will be placed on drop - see next block 
    //of code for the Styles used
    $(".SortableListItem").addClass("ui-widget ui-widget-content ui-helper-clearfix 
                            ui-corner-all")
                    .find(".SortableListHeader") 
                    .addClass("ui-widget-header ui-corner-all")
                    .end()
                    .find(".SortableListContent"); 
    $(".SortableList").disableSelection();
}

以下是需要为上述函数添加到页面的附加样式类。请注意红色虚线边框,这是放置项目位置的轮廓样式。

<style type="text/css">
.ui-sortable-placeholder { border: 3pxdottedred;
       visibility: visible!important;
       height: 50px!important; }
.ui-sortable-placeholder* { visibility: hidden; }
</style>

上面的 drawListItems 函数实际上会创建以下 HTML 输出。首先是排序列表(上图左侧)编辑界面的输出,其次是列表预览(上图右侧)的输出。

排序列表输出(上图左侧)

<div class="SortableList">
    <div class="SortableListItem"id="7" style="margin-bottom: 5px;">
        <div class="SortableListHeader" style="padding: 5px; position: relative;">
            Code Project
        </div>
        <div class="SortableListContent" style="padding: 5px;">
            Desc: programming resource <br/>
            Link: https://codeproject.org.cn/ <br/>
        </div>
    </div>
    <div class="SortableListItem"id="5" style="margin-bottom: 5px;">
        <div class="SortableListHeader" style="padding: 5px; position: relative;">
            wesgrant.com
        </div>
        <div class="SortableListContent" style="padding: 5px;">
            Desc: my personal website <br/>
            Link: http://www.wesgrant.com/ <br/>
        </div>
    </div>
</div>

列表预览输出(上图右侧)

<div id="listPreviewStyle">
    <ul>
        <li>
            <a href='https://codeproject.org.cn/' target='_blank'>Code Project</a>
            <span>programming resource</span>
        </li>
        <li>
            <a href='http://www.wesgrant.com/' target='_blank'>wesgrant.com</a>
            <span>my personal website</span>
        </li>
    </ul>
</div>

请注意设置排序列表的代码,“stop”状态会在项目位置改变时调用 moveListItem。此代码会调用一个 Web 方法将移动持久化到数据库并相应地重新排列项目列表。这是 moveListItem 函数的代码

function moveListItem(sortableListItemId, newPosition) {
    $.ajax({
        type: "POST",
        url: "../AjaxMethods/ListMethods.aspx/SaveListPosition",
        data: "{'sortableListItemId':'" + sortableListItemId + "',"
                + "'newPosition':'" + newPosition + "'}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (data) {
        },
        error: function (xhr, ajaxOptions, thrownError) {
            showVoodooJavaScriptError(xhr, ajaxOptions, thrownError);
        }
     });
}

该函数传递被移动项目的 ID,以及项目将被放置的新位置。这将被传递到一个 Web 方法,列表项的移动最终将在数据存储库中进行。这是中间 Web 方法的代码

[System.Web.Services.WebMethod]
public static void SaveListPosition(int sortableListItemId, int newPosition)
{
     Data.Repository.SortableListRepository SLR = new
    Data.Repository.SortableListRepository();
    SLR.MoveSortableListItem(sortableListItemId, newPosition);
}

最后是完成所有工作。这将从数据模型中检索项目,以便它拥有项目的当前位置,以及项目所属列表的 ID。为了执行移动,它会向下移动列表中的所有项目,这些项目位于项目当前位置之上,然后在该移动之后,将大于或等于新位置的所有项目向上移动一个位置,然后将要移动的项目的位置设置为其新位置。

public void MoveSortableListItem(int sortableListItemId, int newPosition)
{
    var results = from i in DataContext.SortableListItems
    where i.SortableListItemId == sortableListItemId
    select i;
    if (results.Count() > 0)
    {
        Data.Model.SortableListItem li = results.First();

        // move items above current Item position down
        var items = DataContext.SortableListItems
            .Where(i => (i.SortableListId == li.SortableListId) && 
                  (i.ItemOrder > li.ItemOrder) &&
                   (i.SortableListItemId != sortableListItemId));
        foreach (var item in items)
            item.ItemOrder--;
        DataContext.SaveChanges();

        // move items above new position up
        items = DataContext.SortableListItems
            .Where(i => (i.SortableListId == li.SortableListId) && 
                (i.ItemOrder >= newPosition) &&
                (i.SortableListItemId != sortableListItemId));    
    
        foreach (var item in items)
            item.ItemOrder--;

        DataContext.SaveChanges();

        // move items above new position up
        items = DataContext.SortableListItems
            .Where(i => (i.SortableListId == li.SortableListId) && 
                (i.ItemOrder >= newPosition) &&
                (i.SortableListItemId != sortableListItemId));
        foreach (var item in items)
            item.ItemOrder++;

        // set the position of the item being dragged
        li.ItemOrder = newPosition;
        DataContext.SaveChanges();
        
        foreach (var item in items)
            item.ItemOrder++;

        // set the position of the item being dragged
        li.ItemOrder = newPosition;
        DataContext.SaveChanges();
    }
}

历史

  • 2011 年 7 月 20 日:首次发布
© . All rights reserved.