1

I am building an MVC-App and I am looking for an efficient way to do paging in my view.

So far I have installed the following paging system. However, if this paging system does work well, it has a flaw: the list shown in the view is not the full list, but rather what portion of the list the user is viewing based on the page number. The flaw being that if I use a jQuery system to sort the items, it will only sort the items in the current page, and not the total list.

Allow me to demonstrate my meaning. Say that I have this view:

@using MyApp.Models
@model PagedList.IPagedList<MyApp.Utilities.CardDisplay>

<h2>
    Cards Display Results
</h2>

<script type="text/javascript">
    $(document).ready(function(){
        $('#cardRarity').change(function () {
            var showCardRarity = $(this).val();
            if (showCardRarity == "All") {
                $(".cardRarity").show();
            } else {
                $(".cardRarity").hide();
                $(".cardRarity-" + showCardRarity).each(function() {
                    $(this).show();
                });
            }
        });
        $('#cardType').change(function() {
            var showCardType = $(this).val();
            if (showCardType == "All") {
                $(".cardType").show();
            } else {
                $(".cardType").hide();
                $(".cardType-" + showCardType).each(function() {
                    $(this).show();
                });
            }
        });
        $('#cardColor').change(function() {
            var showCardColor = $(this).val();
            if (showCardColor == "All") {
                $(".cardColor").show();
            } else {
                $(".cardColor").hide();
                $(".cardColor-" + showCardColor).each(function() {
                    $(this).show();
                });
            }
        });
    });
</script>

@using (Html.BeginForm())
{
    <p>Filter by rarity: <select name="cardRarity" id="cardRarity" tabindex="1">
                             <option value="All">All</option>
                             <option value="Land">Land</option>
                             <option value="Common">Common</option>
                             <option value="Uncommon">Uncommon</option>
                             <option value="Rare">Rare</option>
                             <option value="Mythic Rare">Mythic Rare</option>
                             <option value="Special">Special</option>
                         </select>
        Filter by type: <select name="cardType" id="cardType" tabindex="2">
                            <option value="All">All</option>
                            <option value="Artifact">Artifact</option>
                            <option value="Instant">Instant</option>
                            <option value="Creature">Creature</option>
                            <option value="Land">Land</option>
                            <option value="Planeswalker">Planeswalker</option>
                            <option value="Enchantment">Enchantment</option>
                            <option value="Sorcery">Sorcery</option>
                            <option value="Tribal">Tribal</option>
                        </select>
        Filter by color: <select name="cardColor" id="cardColor" tabindex="3">
                            <option value="All">All</option>
                            <option value="White">White</option>
                            <option value="Red">Red</option>
                            <option value="Blue">Blue</option>
                            <option value="Green">Green</option>
                            <option value="Black">Black</option>
                            <option value="Gold">Gold</option>
                            <option value="Colorless">Colorless</option>
                        </select>
    </p>
}
@if (Model.Count > 0)
{
    if (Model.PageCount > 1)
    {
        <div class="center">
            Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
            of @Model.PageCount

            @if (Model.HasPreviousPage)
            {
                @Html.ActionLink("<<", "DisplayCardsResults", new { _page = 1, _sortOrder = ViewBag._currentSort })
                @Html.Raw(" ")
                @Html.ActionLink("< Prev", "DisplayCardsResults", new { _page = Model.PageNumber - 1, _sortOrder = ViewBag._currentSort })
            }
            else
            {
                @:<<
                @Html.Raw(" ");
                @:< Prev
            }

            @if (Model.HasNextPage)
            {
                @Html.ActionLink("Next >", "DisplayCardsResults", new { _page = Model.PageNumber + 1, _sortOrder = ViewBag._currentSort })
                @Html.Raw(" ")
                @Html.ActionLink(">>", "DisplayCardsResults", new { _page = Model.PageCount, _sortOrder = ViewBag._currentSort })
            }
            else
            {
                @:Next >
                @Html.Raw(" ")
                @:>>
            }
        </div>
    }
    <table id="resultTable">
        <tr>
            <th>Item number</th>
            <th>Card Name</th>
            <th>Number</th>
            <th>Color</th>
            <th>Mana Cost</th>
            <th>Mana Converted</th>
            <th>Card Type</th>
            <th>Power</th>
            <th>Toughness</th>
            <th>Rarity</th>
            <th>Card Set</th>
            <th>Artist Name </th>
            <th>Actions </th>
        </tr>
        @for (int i = 0; i < Model.Count; i++)
        {
            var className = i % 2 == 0 ? "even" : "odd";

            var row = ((i + 1) + ((Model.PageNumber - 1) * 50));

            <tr class="@className cardRarity cardRarity-@(Model[i].mCardRarity.Replace(" ", "-")) 
                cardType cardType-@(Model[i].mStrippedCardType)
                cardColor cardColor-@(Model[i].mCardColor)">
                <td>@row</td>
                <td class="center">
                    @(Model[i].mCardFlagFace == CardInfo.FlagFaceValue.Normal ?
                    Html.ActionLink(Model[i].mCardName, "CardDetails", new {_cardId = Model[i].mCardID}) :
                    Html.ActionLink(Model[i].mCardName + " // " + Model[i].mChildCard.mCardName, "CardDetails", new {_cardId = Model[i].mCardID}))
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardNumber)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardColor)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardManaCost)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardManaConverted)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardType)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardPower)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardToughness)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardRarity)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardSet.mCardSetName)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardArtistName)
                </td>
                <td>
                    @Html.ActionLink("Details", "Details", new {@_cardId = Model[i].mCardID})
                    @Html.ActionLink("Edit", "Edit", new {@_cardId = Model[i].mCardID})
                    @Html.ActionLink("Delete", "Delete", new {@_cardId = Model[i].mCardID})
                </td>
            </tr>
        }
    </table>
    if (Model.PageCount > 1)
    {
        <div class="center">
            Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
            of @Model.PageCount

            @if (Model.HasPreviousPage)
            {
                @Html.ActionLink("<<", "DisplayCardsResults", new { _page = 1, _sortOrder = ViewBag._currentSort })
                @Html.Raw(" ")
                @Html.ActionLink("< Prev", "DisplayCardsResults", new { _page = Model.PageNumber - 1, _sortOrder = ViewBag._currentSort })
            }
            else
            {
                @:<<
                @Html.Raw(" ");
                @:< Prev
            }

            @if (Model.HasNextPage)
            {
                @Html.ActionLink("Next >", "DisplayCardsResults", new { _page = Model.PageNumber + 1, _sortOrder = ViewBag._currentSort })
                @Html.Raw(" ")
                @Html.ActionLink(">>", "DisplayCardsResults", new { _page = Model.PageCount, _sortOrder = ViewBag._currentSort })
            }
            else
            {
                @:Next >
                @Html.Raw(" ")
                @:>>
            }
        </div>
    }
}

Say that I have a list of 50 cards to display, but the page number of cards allowed is 20. Based on this view, if I change the selected option Card Type to Creature, the view will indeed sort and display all the creatures, but only for the 20 cards displayed and not for the whole list.

That is why I need help: I need to figure a way either to either load the entire list to the view, and then sort out some paging way, or to redisplay only the wanted item, and I'd love to do it without having to rewrite my controller method, though I do not mind adding new features. I've heard that JSon might help, but I don't know how it works. Can anyone help me out?

EDIT

Here's the working code!

First, I have modified the javascript function like this:

<script type="text/javascript">
    $(document).ready(function () {
        $('#cardRarity').change(function () {
            var showCardRarity = $(this).val();
            refreshResults(($(this).attr("id")), showCardRarity);
        });
        $('#cardType').change(function () {
            alert("Type changed");
            var showCardType = $(this).val();
            refreshResults(($(this).attr("id")), showCardType);
        });
        $('#cardColor').change(function () {
            alert("Color changed");
            var showCardColor = $(this).val();
            refreshResults(($(this).attr("id")), showCardColor);
        });

        function refreshResults(filter, value) {
            alert(filter);
            $.get("@Url.Action("DisplayCardsResults", "Card")", {
                _page: 1,
                _sortOrder: "@ViewBag._sortOrder",
                _filter: filter,
                _searchValue: value
            }, function(data) {
                $("#resultTable").html(data);
            });
        }
    });
</script>

The function refreshResults was a bit tricky because I had to figure how to call a controller method from a javascript function and pass data through it. I love stackOverflow!

Then I removed everything beyond the @if(Model.Count > 0) and put that in a partial view that I was calling like this:

<div id="resultsDiv">
    @{
        Html.RenderPartial("_ResultsTable", @Model);
    }
</div>

And then, in my controller, I had to sort things through:

public ActionResult DisplayCardsResults(int? _page, string _sortOrder, string _filter = "", string _searchValue = "")
{
    ViewBag._searchValue = _searchValue;
    ViewBag._filter = _filter;

    if (Request.HttpMethod != "GET")
    {
        _page = 1;
    }

    int pageNumber = (_page ?? 1);

    if (Request.IsAjaxRequest())
    {
        switch (_filter)
        {

            // The mListCardsToShow is an inner list I keep and it is different from the mListCards because of the where clause which flush the rest of the data.

            case "cardRarity":
                if (_searchValue == "All")
                {
                    mListCardsToShow = mListCards.ToList();
                }
                else
                {
                    mListCardsToShow.AddRange(mListCards.Where(_item => _item.mMasterCard.mCardRarity == _searchValue));
                }
                break;
            case "cardType":
                if (_searchValue == "All")
                {
                    mListCardsToShow = mListCards.ToList();
                }
                else
                {
                    mListCardsToShow.AddRange(mListCards.Where(_item => _item.mMasterCard.mCardType == _searchValue));
                }
                break;
            case "cardColor":
                if (_searchValue == "All")
                {
                    mListCardsToShow = mListCards.ToList();
                }
                else
                {
                    mListCardsToShow.AddRange(mListCards.Where(_item => _item.mMasterCard.mCardColor == _searchValue));
                }
                break;
            default:
                mListCardsToShow = mListCards.ToList();
                break;
        }

        return PartialView("_ResultsTable", mListCardsToShow.ToPagedList(pageNumber, ValueDomain.PAGE_SIZE));
    }

    return View(mListCards.ToPagedList(pageNumber, ValueDomain.PAGE_SIZE));
}

Now, I must admit that the sorting is not perfect, like, I would need to take in account each three of the filter, but that's another story. Things works, and I'm happy for that!

4

3 回答 3

3

这听起来确实像是 AJAX 可以帮助解决您的问题并使事情变得更优雅的东西 - 值得做一些研究。现在,每当用户单击其中一个链接时,都需要重新加载整个页面才能简单地获取表中的下一页数据。您可以在需要时仅动态更新表。

查看jQuery.ajax的文档- 在您传递给服务器的 URL 中,您可以设置过滤器(分页和排序),并在成功处理程序中更新表中的值。也不要被术语 JSON 吓倒,这只是 js 主要使用的数据存储术语,您可以在控制器上设置一个操作,该操作将返回在 AJAX 成功处理程序中处理的 JSON 信息。

PS:耶MtG,祝你好运:)

于 2013-07-23T17:55:40.133 回答
2

如果您有很多值,加载整个列表通常不是一个好主意。在您的情况下,虽然 50 条记录并不多,但通常不是一个有效的设计。看看下面的文章。

http://demo.aspnetawesome.com/GridDemo/CustomQuerying

http://kevww.wordpress.com/2011/12/20/how-to-implement-paging-sorting-in-mvc-3-part-2-how-to-use-it-for-paging-sorting/

http://www.4guysfromrolla.com/articles/012611-1.aspx

Mvc 也有一个开箱即用的 WebGrid,通常适用于这种类型的场景。可能会适合您的需求。

http://stick2basic.wordpress.com/2013/03/18/efficient-paging-and-sorting-with-webgrid-web-helper-asp-net-mvc/

http://msdn.microsoft.com/en-us/magazine/hh288075.aspx

希望能帮助到你。

于 2013-07-23T17:50:53.273 回答
1

为泰勒 +1。AJAX 是我能想到的唯一优雅的方式。每次更改下拉列表时,您都必须将页面提交到服务器,并且您将获得数据,但每次没有 AJAX 的情况下您的页面都会刷新。

编辑:

这就是使用 ajax 的方式。

这是你的看法

@using MyApp.Models
@model PagedList.IPagedList<MyApp.Utilities.CardDisplay>

<h2>
    Cards Display Results
</h2>

<script type="text/javascript">
    $(document).ready(function(){
        $('#cardRarity').change(function () {
            var showCardRarity = $(this).val();
            refreshResults(($(this).attr("id")),showCardRarity );
        });
        $('#cardType').change(function() {
            var showCardType = $(this).val();
            refreshResults(($(this).attr("id")),showCardRarity );
        });
        $('#cardColor').change(function() {
            var showCardColor = $(this).val();
            refreshResults(($(this).attr("id")),showCardRarity );
        });

        function refreshResults(filter, value){
          $.get("YourController/DisplayCardsResults", 
            { 
                _page = 1,   
                _sortOrder = ViewBag._currentSort,
                _filter = filter,
                _searchValue =  value 
            },
            function (data{
              $("#resultsDiv").html(data);
            }
    });
</script>

@using (Html.BeginForm())
{
    <p>Filter by rarity: <select name="cardRarity" id="cardRarity" tabindex="1">
                             <option value="All">All</option>
                             <option value="Land">Land</option>
                             <option value="Common">Common</option>
                             <option value="Uncommon">Uncommon</option>
                             <option value="Rare">Rare</option>
                             <option value="Mythic Rare">Mythic Rare</option>
                             <option value="Special">Special</option>
                         </select>
        Filter by type: <select name="cardType" id="cardType" tabindex="2">
                            <option value="All">All</option>
                            <option value="Artifact">Artifact</option>
                            <option value="Instant">Instant</option>
                            <option value="Creature">Creature</option>
                            <option value="Land">Land</option>
                            <option value="Planeswalker">Planeswalker</option>
                            <option value="Enchantment">Enchantment</option>
                            <option value="Sorcery">Sorcery</option>
                            <option value="Tribal">Tribal</option>
                        </select>
        Filter by color: <select name="cardColor" id="cardColor" tabindex="3">
                            <option value="All">All</option>
                            <option value="White">White</option>
                            <option value="Red">Red</option>
                            <option value="Blue">Blue</option>
                            <option value="Green">Green</option>
                            <option value="Black">Black</option>
                            <option value="Gold">Gold</option>
                            <option value="Colorless">Colorless</option>
                        </select>
    </p>
}

<div id="resultsDiv">
    @Html.RenderPartial("_ResultsTable",@Model)
<div>

把它放在局部视图 _ResultsTable

@using MyApp.Models
@model PagedList.IPagedList<MyApp.Utilities.CardDisplay>

@if (Model.Count > 0)
    {
        if (Model.PageCount > 1)
        {
            <div class="center">
                Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
                of @Model.PageCount

            @if (Model.HasPreviousPage)
            {
                @Html.ActionLink("<<", "DisplayCardsResults", new { _page = 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
                @Html.Raw(" ")
                @Html.ActionLink("< Prev", "DisplayCardsResults", new { _page = Model.PageNumber - 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
            }
            else
            {
                @:<<
                @Html.Raw(" ");
                @:< Prev
            }

            @if (Model.HasNextPage)
            {
                @Html.ActionLink("Next >", "DisplayCardsResults", new { _page = Model.PageNumber + 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
                @Html.Raw(" ")
                @Html.ActionLink(">>", "DisplayCardsResults", new { _page = Model.PageCount, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
            }
            else
            {
                @:Next >
                @Html.Raw(" ")
                @:>>
            }
        </div>
    }
    <table id="resultTable">
        <tr>
            <th>Item number</th>
            <th>Card Name</th>
            <th>Number</th>
            <th>Color</th>
            <th>Mana Cost</th>
            <th>Mana Converted</th>
            <th>Card Type</th>
            <th>Power</th>
            <th>Toughness</th>
            <th>Rarity</th>
            <th>Card Set</th>
            <th>Artist Name </th>
            <th>Actions </th>
        </tr>
        @for (int i = 0; i < Model.Count; i++)
        {
            var className = i % 2 == 0 ? "even" : "odd";

            var row = ((i + 1) + ((Model.PageNumber - 1) * 50));

            <tr class="@className cardRarity cardRarity-@(Model[i].mCardRarity.Replace(" ", "-")) 
                cardType cardType-@(Model[i].mStrippedCardType)
                cardColor cardColor-@(Model[i].mCardColor)">
                <td>@row</td>
                <td class="center">
                    @(Model[i].mCardFlagFace == CardInfo.FlagFaceValue.Normal ?
                    Html.ActionLink(Model[i].mCardName, "CardDetails", new {_cardId = Model[i].mCardID}) :
                    Html.ActionLink(Model[i].mCardName + " // " + Model[i].mChildCard.mCardName, "CardDetails", new {_cardId = Model[i].mCardID}))
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardNumber)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardColor)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardManaCost)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardManaConverted)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardType)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardPower)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardToughness)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardRarity)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardSet.mCardSetName)
                </td>
                <td>
                    @Html.DisplayFor(_item => _item[i].mCardArtistName)
                </td>
                <td>
                    @Html.ActionLink("Details", "Details", new {@_cardId = Model[i].mCardID})
                    @Html.ActionLink("Edit", "Edit", new {@_cardId = Model[i].mCardID})
                    @Html.ActionLink("Delete", "Delete", new {@_cardId = Model[i].mCardID})
                </td>
            </tr>
        }
    </table>
    if (Model.PageCount > 1)
    {
        <div class="center">
            Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
            of @Model.PageCount

            @if (Model.HasPreviousPage)
            {
                @Html.ActionLink("<<", "DisplayCardsResults", new { _page = 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
                @Html.Raw(" ")
                @Html.ActionLink("< Prev", "DisplayCardsResults", new { _page = Model.PageNumber - 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
            }
            else
            {
                @:<<
                @Html.Raw(" ");
                @:< Prev
            }

            @if (Model.HasNextPage)
            {
                @Html.ActionLink("Next >", "DisplayCardsResults", new { _page = Model.PageNumber + 1, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
                @Html.Raw(" ")
                @Html.ActionLink(">>", "DisplayCardsResults", new { _page = Model.PageCount, _sortOrder = ViewBag._currentSort, _filter = ViewBag._filter, _searchValue = ViewBag._searchValue })
            }
            else
            {
                @:Next >
                @Html.Raw(" ")
                @:>>
            }
        </div>
    }
}

在控制器中执行以下操作:

public ActionResult DisplayCardsResults(int _page, string _sortOrder, string _filter = "", string _searchValue = "")
{
  ViewBag._filter = _filter;
  ViewBag._searchValue= _searchValue;

  //Do whatever you are doing to get the cards but to the resultant collection add the following where condition, suppose the collection is in var cards


 switch (_filter)
            {
                case "cardRarity":
                    cards = cards.Where(s => s.CardRarity == _searchValue);
                    break;
                case "cardType":
                    cards = cards.Where(s => s.CardType == _searchValue);
                    break;
                case "cardColor":
                    cards = cards.Where(s => s.CardColor == _searchValue);
                    break;
                default:
                    // no filter
                    break;
            }
   if(Request.isAjaxRequest)
      return PartialView("_ResultsTable",cards);
  return View(cards);
}
于 2013-07-23T19:44:24.810 回答