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!