locked
Sorting MVC View Results (really more of an architectural issue) RRS feed

  • Question

  • User1434241939 posted

    My controller has an [HttpGet] index() action that displays a form with several inputs that are not bound to my database.  For example there is a drop-down containing all the years from 1960 to 2020.  When a user selects one, the form is posted and the [HttpPost] action method finds every item in the database from that year.  Works great!

    I would like a user to be able to sort the results by item name.  I would also eventually like to do pagination.

    1.  Is there a Javascript/CSS library that will do all this so I can just plug it in?

    2.  If not, I have attempted to code it.  However I can't get the data to persist when I do something like this:

    <a asp-action="Index" asp-route-sortorder="@ViewBag.NameSortParm">Sort by Airline / Bag Name</a>

    in order to trigger this

                switch (sortorder)
                {
                    case "airline_desc":
                        baglist = baglist.OrderByDescending(s => s.Airline).ToList();
                        break;
                    case "year":
                        baglist = baglist.OrderBy(s => s.Year).ToList();
                        break;
                    case "year_desc":
                        baglist = baglist.OrderByDescending(s => s.Year).ToList();
                        break;
                    default:
                        baglist = baglist.OrderBy(s => s.Airline).ToList();
                        break;
                }
    

    which calls the Index() get method, meaning I've lost the filtered data from the model.  It no longer has any idea that the data from the dropdown was say, 2005.  I'm guessing that the original form shouldn't post and should instead use a querystring.  is that a better way to go?  Even so, it still doesn't help me retain only the 2005 data when sorting.

    I suppose I could cache the results (now that I know how to do so) but it seems like there's a better, clearer way to do so.

    Tuesday, May 12, 2020 9:38 PM

All replies

  • User475983607 posted

    I recommend going through the following tutorial which illustrates sorting and paging in step 3-8.

    https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-3.1&tabs=visual-studio

    Tuesday, May 12, 2020 9:53 PM
  • User711641945 posted

    Hi stevebo,

    I would also eventually like to do pagination.

    1.  Is there a Javascript/CSS library that will do all this so I can just plug it in?

    You could try to use X.PagedList for pagination.

    Reference:

    https://stackoverflow.com/a/57881736/11398810

    https://github.com/dncuug/X.PagedList

    A similar tutorial about how to add sorting, filtering, and paging in asp.net core mvc you could refer to:

    https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/sort-filter-page?view=aspnetcore-3.1

    Best Regards,

    Rena

    Wednesday, May 13, 2020 8:18 AM
  • User1434241939 posted

    I have been working on this all week and while I've finally had success in sorting and pagination, I have run into a problem.  I would like my pagination to be a generic method since it does the exact same thing for each kind of page I display to the user.   The problem is that I keep track of pagination using static variables and I don't know how to manipulate them in the generic method.  Here's the code that works:

            private List<Peoplemvc> CreatePagination(List<Peoplemvc> people)
            {
                if (people != null)
                {
                    double stupid = Convert.ToDouble(people.Count) / PerPage;
                    NumPages = Convert.ToInt32(Math.Ceiling(stupid));
                    ViewBag.NumPages = NumPages;
                    return (people.GetRange((WhichPage - 1) * PerPage, Math.Min(PerPage, people.Count - ((WhichPage - 1) * PerPage))));
                }
                else
                {
                    return (null);
                }
            }

    Here is the declaration of NumPages and the way I call CreatePagination

            public static int WhichPage = 1;
            public static int PerPage = 5;
            public static int NumPages = 0;
    
    
    ...
    
                people = CreatePagination(people);
    

    But since other Views will need the same functionality, I created this method (that works)

            public List<T> CreatePagination<T>(List<T> items)
            {
                if (items != null)
                {
                    double stupid = Convert.ToDouble(items.Count) / PerPage;
                    NumPages = Convert.ToInt32(Math.Ceiling(stupid));
                    ViewBag.NumPages = NumPages;
                    return (items.GetRange((WhichPage - 1) * PerPage, Math.Min(PerPage, items.Count - ((WhichPage - 1) * PerPage))));
                }
                else
                {
                    return (null);
                }
            }

    However it only works in my controller class.  I want to move the method elsewhere and have other controllers call it but the static int NumPages is out of scope.  I tried passing a class type to the method but that doesn't seem to work.  I suppose a static is the wrong way to go, but I kind of like keeping the state of my result page.  How should I proceed?

    Sunday, May 17, 2020 11:24 AM
  • User475983607 posted

    Static fields are not a design option.  It seems to work because you are the only user.   A static field is a single memory location accessible by all application users.  All applicaiton users see the same value because there is only one. 

    The common design pattern is taking advantage of the URL to cache the pagination fields. The URL is specific to the current user making requests. 

    Sunday, May 17, 2020 11:54 AM
  • User1434241939 posted

    This is what I have in the View:

                @if (ViewBag.WhichPage != 1 && ViewBag.NumPages != 1)
                {
                    <a class="btn btn-link" asp-controller="People" asp-action="Index"
                       asp-route-whichpage="@(ViewBag.WhichPage - 1)">Previous @ViewBag.PerPage People</a>
                }
                -
                @if (ViewBag.WhichPage != ViewBag.NumPages && ViewBag.NumPages != 1)
                {
                    <a class="btn btn-link" asp-controller="People" asp-action="Index"
                       asp-route-whichpage="@(ViewBag.WhichPage + 1)">Next @ViewBag.PerPage People</a>
                }

    Are you saying is that I should include NumPages and PerPage in my tag helpers like this?

    <a class="btn btn-link" asp-controller="People" asp-action="Index"
       asp-route-numpages="@ViewBag.NumPage"    asp-route-perpage="@ViewBag.PerPage" asp-route-sortorder="@ViewBag.SortOrder"
       asp-route-whichpage="@(ViewBag.WhichPage + 1)">Next @ViewBag.PerPage People</a>

    That's fine, but there could be a lot of items in the tag and URL if I add more to the state of the page.  But if that's the way to do it, I will do so.

    Sunday, May 17, 2020 12:07 PM
  • User475983607 posted

    stevebo

    Are you saying is that I should include NumPages and PerPage in my tag helpers like this?

    I though I was clear.  Static is not an option because every application user will see the last changed values.   Paging is commonly cached in the URL because a URL can be bookmarked and it goes with the user.

    You can always create a tag helper and/or middleware that handler pagination or find an API on NuGet.  There are a lot of blogs out there on the Internet.

    https://www.reflectionit.nl/blog/2017/paging-in-asp-net-core-mvc-and-entityframework-core

    Sunday, May 17, 2020 12:18 PM