filterasp.net-core-mvchtml-select.net-9.0

Category dropdown filter in ASP.NET Core 9 MVC


Can anyone help me with this category filter?

<div class="d-flex align-items-center">
    <label for="category-select" class="me-2">Category</label>
    <select name="categoryFilter" class="form-select" id="category-select">
        <option value="">All Categories</option>
        @foreach (var category in Model.Categories)
        {
            var isSelected = Model.CategoryFilter == category.Value ? "selected" : "";
            <option value="@category.Value" selected="" @isSelected>@category.Text</option>
        }
    </select>
</div>

I get this error:

Error (active) RZ1031
The tag helper 'option' must not have C# in the element's attribute declaration area.
C:\Users\zaida\Desktop\Clinic Management Software\ClinicManagementSystem\Views\ProductManagement\Index.cshtml 55

Controller:

[HttpGet]
public async Task<IActionResult> Index(string searchTerm, string categoryFilter, string sortBy, int page = 1)
{
    var productsQuery = _context.Products
        .Include(p => p.Category)
        .Include(p => p.Inventory)
        .Where(p => p.DeletedAt == null);

    // Filtering
    if (!string.IsNullOrEmpty(searchTerm))
        productsQuery = productsQuery.Where(p => p.Name.Contains(searchTerm) || p.SKU.Contains(searchTerm));

    if (!string.IsNullOrEmpty(categoryFilter))
        productsQuery = productsQuery.Where(p => p.Category.Name == categoryFilter);

    // Sorting
    productsQuery = sortBy switch
    {
        "price_asc" => productsQuery.OrderBy(p => p.Price),
        "price_desc" => productsQuery.OrderByDescending(p => p.Price),
        "name_asc" => productsQuery.OrderBy(p => p.Name),
        "name_desc" => productsQuery.OrderByDescending(p => p.Name),
        _ => productsQuery.OrderBy(p => p.CreatedAt)
    };

    // Pagination
    var totalItems = await productsQuery.CountAsync();
    var products = await productsQuery
        .Skip((page - 1) * PageSize)
        .Take(PageSize)
        .ToListAsync();

    var categories = await _context.Product_Category
        .Select(c => new SelectListItem { Text = c.Name, Value = c.Name })
        .ToListAsync();

    var viewModel = new ProductIndexViewModel
    {
        Products = products,
        CurrentPage = page,
        TotalPages = (int)Math.Ceiling((double)totalItems / PageSize),
        SearchTerm = searchTerm,
        CategoryFilter = categoryFilter,
        SortBy = sortBy,
        Categories = categories
    };

    return View(viewModel);
}

[HttpPost]
public async Task<IActionResult> IndexPost(string searchTerm, string categoryFilter, string sortBy, int page = 1)
{
    return await Index(searchTerm, categoryFilter, sortBy, page);
}

[HttpGet]
public async Task<IActionResult> AddProduct()
{
    ViewBag.Categories = await _context.Product_Category.ToListAsync();
    ViewBag.Discounts = await _context.Product_Discount.ToListAsync();
    return View();
}

I tried

<option value="">All Categories</option>
@foreach (var category in Model.Categories)
{
    var isSelected = Model.CategoryFilter == category.Value ? "selected" : "";
    <option value="@category.Value" selected="@isSelected" >@category.Text</option>
}

but I have to go search bar and press enter to make it work


Solution

  • Error (active) RZ1031 The tag helper 'option' must not have C# in the element's attribute declaration area.

    For this issue, you seem to have found the cause (<option value="@category.Value" selected="" @isSelected>), the issue relates the selected property, you need to put the @isSelected inside the "", after modified, the code should like this <option value="@category.Value" selected="@isSelected">.

    According to your code, it seems that you want to set the default value for the select tag, using your code, you will find it doesn't set the correct default value. If use F12 developer tools to check the elements, you can see every option has the selected attribute and it will select the last option as the default value.

    result1

    To solve this issue, you can modify your code as below: use asp-for and asp-item to bind select tag and set default value.

    <select asp-for="CategoryFilter" name="categoryFilter" class="form-select" id="category-select" asp-items="Model.Categories">
        <option value="">All Categories</option> 
    </select> 
    

    The output as below:

    result2

    but I have to go search bar and press enter to make it work

    Do you mean you want to filter data after change the selected options, if that is the case, you can use the select tag onchange event to submit the form.

    Try to use the following code:

    Index.cshtml: in the form tag, we can use the method attribute to specify how the form is submitted (get or post) and use the asp-action attribute to specify which action method will be submitted to.

    @model Net9MVCSample.Models.ProductIndexViewModel
    @{
        ViewData["Title"] = "Home Page";
    }
    
    <form id="myform" asp-action="Index" method="get">
    
        <div class="d-flex align-items-center">
            <label for="category-select" class="me-2">Category</label>
            <select asp-for="CategoryFilter" name="categoryFilter" class="form-select" id="category-select"
                    onchange="Submitform()"
                asp-items="Model.Categories">
                <option value="">All Categories</option> 
            </select> 
        </div>
    
        <div>
        Output: @ViewData["selectvalue"]
        </div>
    </form> 
    
    @section Scripts {
        <script>
            function Submitform(){
                document.getElementById("myform").submit();
            }
        </script>
    }
    

    Home controller:

    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
    
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
    
        [HttpGet]
        public async Task<IActionResult> Index(string searchTerm, string categoryFilter, string sortBy, int page = 1)
        {
            var categories = new List<SelectListItem>()
             {
                 new SelectListItem { Text = "C1", Value="101" },
                 new SelectListItem { Text = "C2", Value="102" },
                 new SelectListItem { Text = "C3", Value="103" },
                 new SelectListItem { Text = "C4", Value="104" },
                 new SelectListItem { Text = "C5", Value="105" },
             };
    
            categoryFilter = categoryFilter ?? "103";
            var viewModel = new ProductIndexViewModel
            { 
                CategoryFilter = categoryFilter, 
                Categories = categories
            };
    
            ViewData["selectvalue"] = categoryFilter;
    
            return View(viewModel);
        }
    

    Models:

    public class ProductIndexViewModel
    {
        public string CategoryFilter { get; set; }
        public List<SelectListItem> Categories { get; set; }
    }
    

    More detail information, see Tag Helpers in forms in ASP.NET Core.

    The result as below: after changing the selected options, it will submit form to Index action method and then return the filtered data to page.

    result3