Warm tip: This article is reproduced from serverfault.com, please click

How do I add a sorting feature to my MVC asp.net Core app?

发布于 2018-11-06 16:57:44

I have an MVC app where users can log their create entries and see them in a table on their screen. I would like to add a feature to where the users can click on on of the Name header and the table sort by the name column. I am using this docs.Microsoft tutorial to help me but I don't seem to be getting anywhere. Here's what I have so far:

Controller:

public async Task<IActionResult> Index(string sortOrder)
    {
        ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
        var logs = from l in _context.Log
                        select l;
        switch (sortOrder)
        {
            case "name_desc":
                logs = logs.OrderByDescending(l => l.Name);
                break;
            case "Date":
                logs = logs.OrderBy(l => l.Date);
                break;
            case "date_desc":
                logs = logs.OrderByDescending(l => l.Date);
                break;
            case "relation":
                logs = logs.OrderByDescending(l => l.Relation);
                break;
            case "type":
                logs = logs.OrderByDescending(l => l.Type);
                break;
            default:
                logs = logs.OrderBy(l => l.Date);
                break;
        }
        return View(await logs.AsNoTracking().ToListAsync());
    }

    public async Task OnGetAsync(string parentComment, string searchString)
    {
        // Use LINQ to get list of dates.
        IQueryable<DateTime> dateQuery = from m in _context.Log
                                        // orderby m.Date
                                        select m.Date;

        var logs = from l in _context.Log
                     select l;

        if (!String.IsNullOrEmpty(searchString))
        {
            logs = logs.Where(s => s.Name.Contains(searchString));
        }

        if (!String.IsNullOrEmpty(parentComment))
        {
            logs = logs.Where(x => x.Type == parentComment);
        }
        Dates = new SelectList(await dateQuery.Distinct().ToListAsync());
        Log = await logs.ToListAsync();
    }

View

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Date)
            </th>
            <th>
            <a asp-action="Index" asp-route-sortOrder='@ViewData["NameSortParm"]'>
                @Html.DisplayNameFor(model => model.Log[0].Name)
            </a>
        </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Relation)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Type)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Comment)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Log)
        {
            <tr>
                <th>
                    @Html.DisplayFor(modelItem => item.Date)
                </th>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Relation)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Type)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Comment)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

As you can see, I have it set up for the Name column in the view. When I click on it, the URL changes to let me know that the Index() method fired (https://localhost:5001/HSLogs?action=Index) and the page reloads. No sorting happens. Hardly anything happens. Thoughts?

Questioner
athomas
Viewed
0
athomas 2018-11-07 21:32:09

Xueli Chen's suggestion worked. Thank you.

I got rid of the Task<T> Index method in the controller. I moved the switch statement to the Task OnGetAsync() method and updated the syntax.

Controller:

public string NameSort { get; set; }
public string DateSort { get; set; }    

public async Task OnGetAsync(string sortOrder)
    {
        NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        DateSort = sortOrder == "Date" ? "date_desc" : "Date";

        IQueryable<Log> logsEntry = from s in _context.Log
                                        select s;
        switch (sortOrder)
        {
            case "name_desc":
                logsEntry = logsEntry.OrderByDescending(l => l.Name);
                break;
            case "Date":
                logsEntry = logsEntry.OrderBy(l => l.Date);
                break;
            case "date_desc":
                logsEntry = logsEntry.OrderByDescending(l => l.Date);
                break;
            default:
                logsEntry = logsEntry.OrderBy(l => l.Date);
                break;
        }

        Log = await logsEntry.AsNoTracking().ToListAsync();
    }

In the View, I updated the header links with the appropriate tag-helpers for the Date column and the Name column.

View

<table class="table">
    <thead>
        <tr>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.DateSort">
                    @Html.DisplayNameFor(model => model.Log[0].Date)
                </a>
            </th>
            <th>
                <a asp-page="./Index" asp-route-sortOrder="@Model.NameSort">
                    @Html.DisplayNameFor(model => model.Log[0].Name)
                </a>
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Relation)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Type)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Log[0].Comment)
            </th>
            <th></th>
        </tr>
    </thead>
         ...
</table>