Bernard
Bernard

Reputation: 49

Applying multiple search criteria to razor page list with dropdowns

In a Visual Studio 2019 Web application EF Core, I have a razor page with a list of projects. Each project has several columns with resource names. I would like to be able to filter with multiple drop-downs. I have managed to build the page and the filters, but they only work if I select an entry in both drop-downs. I do not get any error if I use them independently. The two search strings are displayed correctly in the URL /Projects?fosearchString=1&dsmsearchString=0, but the result does not update if I only use one of the drop-down entries. I want the drop-down filters to work independently and together (e.g. FO only, DSM only, FO and DSM together). How can I achieve this?

I have the resource model:

    public class Resource
    {
        [Display(Name = "ID")]
        public int Id { get; set; }

        [Display(Name = "First Name")]
        public string FirstName { get; set; }

        [Display(Name = "Last Name")]
        public string LastName { get; set; }

        [Display(Name = "Long Name")]
        public string LongName { get; set; }

        [Display(Name = "Active")]
        public bool IsActive { get; set; }
        public ICollection<ProjectResource> ProjectResources { get; set; }
    }

The project model:

    public class Project
    {
        [Display(Name = "ID")]
        public int Id { get; set; }

        [Display(Name = "PID")]
        public int PID { get; set; }

        [Display(Name = "Name")]
        public string ProjectName { get; set; }

        [Display(Name = "Forecast Owner")]
        public int FOId { get; set; }
        public Resource Resource { get; set; }

        [Display(Name = "DSM")]
        public int DSMId { get; set; }
        public Resource DSMResource { get; set; }

        public ICollection<ProjectResource> ProjectResources { get; set; }

    }

The index.cshtml.cs:

        public IList<Project> Project { get; set; }
        public IList<Resource> Resources { get; set; }
        public SelectList FOOptions { get; set; }
        public string CurrentFOFilter { get; set; }
        public SelectList Options { get; set; }
        public string CurrentDSMFilter { get; set; }

        public async Task<IActionResult> OnGetAsync(List<int> fosearchString, List<int> dsmsearchString)
        {
            FOOptions = new SelectList(_context.Resource, nameof(Resource.Id), nameof(Resource.LongName));
            List<int> CurrentFOFilter = fosearchString;
            Options = new SelectList(_context.Resource, nameof(Resource.Id), nameof(Resource.LongName));
            List<int> CurrentDSMFilter = dsmsearchString;
            if (fosearchString.Count == 0 || fosearchString[0] == 0 && dsmsearchString.Count == 0 || dsmsearchString[0] == 0)
            {
                Resources = await _context.Resource.ToListAsync();
                Project = await _context.Project
                .Include(p => p.Resource).ToListAsync();
            }
            else
            {
                Resources = await _context.Resource.ToListAsync();
                Project = await _context.Project
                    .Include(p => p.Resource)
                    .Where(s => fosearchString.Contains(s.FOId) && dsmsearchString.Contains(s.DSMId))
                .ToListAsync();

            }
            return Page();
        }

the index.cshtml:

<form asp-page="./Index" method="get">
    <div class="dropdown col-4 no-gutters">
        <div class="input-group mb-3">
            <select class="custom-select" name="fosearchString" value="@Model.CurrentFOFilter" asp-items="Model.FOOptions" selected="selected"><option value="0">Filter by FO...</option></select><text>&nbsp;</text>
            <select class="custom-select" name="dsmsearchString" value="@Model.CurrentDSMFilter" asp-items="Model.Options" selected="selected"><option value="0">Filter by DSM...</option></select><text>&nbsp;</text>
        </div>
        <input type="submit" value="Filter" class="btn btn-primary" /><text>&nbsp;</text><input type="submit" action="/Projects/Index" value="Back to full List" class="btn btn-primary" />
    </div>
</form>
<br />
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].PID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].ProjectName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].FOId)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].DSMId)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Project)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.PID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ProjectName)
                </td>
                <td>
                    @{
                        var foresource = Model.Resources.FirstOrDefault(r => r.Id == item.FOId);
                    }
                    @foresource.LongName
                </td>
                <td>
                    @{
                        var dsmresource = Model.Resources.FirstOrDefault(r => r.Id == item.DSMId);
                    }
                    @dsmresource.LongName
                </td>

I use two view models called ProjectVM and ResourceVM to bring the two models together and also have many-to-many relationships between the two models setup via a ProjectResource class.

Upvotes: 0

Views: 1459

Answers (1)

Ryan
Ryan

Reputation: 20116

The problem lies in your if and else conditions, besides, you need to add brackets since && is prior to ||.

You could always add breakpoints to check which condition it comes into when you filter.

A workable solution below is to set the first option value as "" instead of 0 which will be more convenient to search all:

Index.cshtml:

<div class="input-group mb-3">
        <select class="custom-select" name="fosearchString" value="@Model.CurrentFOFilter" asp-items="Model.FOOptions" selected="selected"><option value="">Filter by FO...</option></select><text>&nbsp;</text>
        <select class="custom-select" name="dsmsearchString" value="@Model.CurrentDSMFilter" asp-items="Model.Options" selected="selected"><option value="">Filter by DSM...</option></select><text>&nbsp;</text>
    </div>

Index.cshtml.cs:

public async Task<IActionResult> OnGetAsync(List<int> fosearchString, List<int> dsmsearchString)
    {
        FOOptions = new SelectList(_context.Resource, nameof(Resource.ID), nameof(Resource.LongName));
        List<int> CurrentFOFilter = fosearchString;
        Options = new SelectList(_context.Resource, nameof(Resource.ID), nameof(Resource.LongName));
        List<int> CurrentDSMFilter = dsmsearchString;

        if (fosearchString.Count == 0 && dsmsearchString.Count == 0)
        {
            Resources = await _context.Resource.ToListAsync();
            Project = await _context.Project.Include(p => p.Resource).ToListAsync();
        }
        else if(fosearchString.Count != 0 && dsmsearchString.Count == 0)
        {
            Resources = await _context.Resource.ToListAsync();
            Project = await _context.Project
                .Include(p => p.Resource)
                .Where(s => fosearchString.Contains(s.FOId))
            .ToListAsync();
        }
        else if (fosearchString.Count == 0 && dsmsearchString.Count != 0)
        {
            Resources = await _context.Resource.ToListAsync();
            Project = await _context.Project
                .Include(p => p.Resource)
                .Where(s => dsmsearchString.Contains(s.DSMId))
            .ToListAsync();
        }
        else
        {
            Resources = await _context.Resource.ToListAsync();
            Project = await _context.Project
                .Include(p => p.Resource)
                .Where(s => fosearchString.Contains(s.FOId) && dsmsearchString.Contains(s.DSMId))
            .ToListAsync();

        }
        return Page();
    }

Upvotes: 1

Related Questions