Search code examples
c#asp.net-coremodel-view-controllerrazor

How do I use a combo-box to change the sort order?


In My view I have a collection of the user uploads and I have added a combo box with action links to refresh the view to select the new sort order:

@model IEnumerable<FunPetPics.Models.PetPhotoModel>

@{
    ViewData["Title"] = "Index";
}

<p>
    <a asp-action="UploadPhoto">Upload Photo</a>
</p>
@if (Model == null || !Model.Any())
{
    <p>Your uploads will be displayed here</p>

}
else
{

    <div class="row">
        <div class="col-md-4">
            <h6>Order by:</h6>
            <div class="form-group">
                <input type=text list=sortOrder class="list">
                <datalist id=sortOrder>
                    <option>@Html.ActionLink("Newest", "Index", new { sortOrder = "Newest" })</option>
                    <option>@Html.ActionLink("Oldest", "Index", new { sortOrder = "Oldest" })</option>
                    <option>@Html.ActionLink("Cutest", "Index", new { sortOrder = "Cutest" })</option>
                    <option>@Html.ActionLink("Funiest", "Index", new { sortOrder = "Funiest" })</option>
                    <option>@Html.ActionLink("Most Awsome", "Index", new { sortOrder = "Most Awsome" })</option>
                </datalist>
            </div>
        </div>
    </div>
    <br />

    <div class="card-columns">

        @foreach (var upload in Model)
        {
            <div class="card">
                <img class="card-img-top" src="@("~/UploadedPhotos/"+  upload.ImageName)" asp-append-version="true">
                <div class="card-body">
                    <h4 class="card-title">@upload.Title</h4>
                    <h6 class="card-subtitle mb-2 text-muted">@upload.PetName</h6>
                    <p class="card-text">@upload.DateUploaded</p>
                    <p class="card-text">@upload.Description</p>
                    <p class="card-subtitle">Ratings:</p>
                    <p class="card-text">
                        Cute:@upload.AverageCutenessRating/5<br />
                        Funny:@upload.AverageFunnynessRating/5<br />
                        Awsome:@upload.AverageAwsomnessRating/5<br />
                    </p>
                </div>
            </div>
        }
    </div>

}

In my controller I get the user's list of uploads with a switch statement which changes the order based on the sortOrder parameter, then return the view with the model:

 public IActionResult Index(string sortOrder)
        {
            //get the current logged in user and their collection of uploads from the database context
            var uploads = GetLoggedInUser().Uploads;
           
            if (uploads == null || !uploads.Any())
            {
                return View();
            }

            switch (sortOrder)
            {
                case "Newest":
                    uploads.OrderByDescending(u => u.DateUploaded);
                    break;

                case "Oldest":
                    uploads.OrderBy(u => u.DateUploaded);
                    break;

                case "Cutest":
                    uploads.OrderByDescending(u => u.AverageCutenessRating)
                        .ThenByDescending(u => u.DateUploaded);
                    break;

                case "Funniest":
                    uploads.OrderByDescending(u => u.AverageFunnynessRating)
                        .ThenByDescending(u => u.DateUploaded);
                    break;

                case "Most Awsome":
                    uploads.OrderByDescending(u => u.AverageAwsomnessRating)
                        .ThenByDescending(u => u.DateUploaded);
                    break;

                default:
                    sortOrder = "Newest";
                    uploads.OrderByDescending(u => u.DateUploaded);
                    break;
            }

            return View(uploads.ToList());
        }

First problem: Selecting an item from the menu does not click in the hyperlink, it just removes other items from the list enter image description here

Second Problem: When trying to navigate manually using localhost:44316/UserUploads?sortOrder="Oldest" in the URL and refresh the page the sortOrder in the controller is null so the order does not change


Solution

  • Selecting an item from the menu does not click in the hyperlink

    datalist doesn't support forms or links. they just supply a dropdown list for an input. So the ActionLink can not work here, you should use js or jquery to refresh the page.

    it just removes other items from the list

    Actually, they are not removed. datalist has a built-in autocomplete attribute , so after you select an option to the input, the others are invisiable. You need to clear the input first.

    When trying to navigate manually using localhost:44316/UserUploads?sortOrder="Oldest" in the URL and refresh the page the sortOrder in the controller is null so the order does not change

    No need to add double quotes in querystring.

    localhost:44316/UserUploads?sortOrder=Oldest
    

    I made a demo according to your needs, you can refer to it.

    Index.cshtml:

    @{
        ViewData["Title"] = "Index";
    }
    
    <h1>Index</h1>
    
    <p>
        <a asp-action="UploadPhoto">Upload Photo</a>
    </p>
    
    
    <div class="row">
        <div class="col-md-4">
            <h6>Order by:</h6>
            <div class="form-group">
                <input type="text" id="sortOrder" name="sortOrder" list="orderList" value="@ViewBag.sortOrder">
                <datalist id="orderList">
                    <option>@Html.ActionLink("Newest", "Index", new { sortOrder = "Newest" })</option>
                    <option>@Html.ActionLink("Oldest", "Index", new { sortOrder = "Oldest" })</option>
                    <option>@Html.ActionLink("Cutest", "Index", new { sortOrder = "Cutest" })</option>
                    <option>@Html.ActionLink("Funiest", "Index", new { sortOrder = "Funiest" })</option>
                    <option>@Html.ActionLink("Most Awsome", "Index", new { sortOrder = "Most Awsome" })</option>
                </datalist>
            </div>
        </div>
    </div>
    <br />
    
    @section scripts{
        <script>
            $("#sortOrder").bind('input', function () {
                if (checkExists($('#sortOrder').val()) === true) {
                    var url = '@Url.Action("Index", "UserUploads")';
                    var text = $('#sortOrder').val();
                    switch (text) {
                        case "Newest":
                            url = url +"?sortOrder=Newest";
                            break;
                        case "Oldest":
                            url = url +"?sortOrder=Oldest";
                            break;
                        case "Cutest":
                            url = url +"?sortOrder=Cutest";
                            break;
                        case "Funiest":
                            url = url +"?sortOrder=Funiest";
                            break;
                        case "Most Awsome":
                            url = url +"?sortOrder=Most Awsome";
                            break;
                        default:
                            url = '@Url.Action("Index")';
                    }
                    window.location = url;
                }
            });
    
            function checkExists(inputValue) {
                console.log(inputValue);
                var x = document.getElementById("orderList");
                var i;
                var flag;
                for (i = 0; i < x.options.length; i++) {
                    if (inputValue == x.options[i].value) {
                        flag = true;
                    }
                }
                return flag;
            }
        </script>
    }
    

    Controller:

    public class UserUploadsController : Controller
    {
        public IActionResult Index(string sortOrder)
        {
            ViewBag.sortOrder = sortOrder;
            return View();
        }
    }
    

    Result:

    enter image description here