Pages

Friday, July 3, 2020

ASP.NET MVC - Simple 5 Step JQuery Filter.

Using this 5 step process you can add a client-side JQuery filter to your MVC Index pages.   

  1. Set up your layout page so that the JQuery pages load BEFORE the page loads.
  2. Add a filter bar.
  3. Add a <thead> and <tbody> section to your table (so we don't inadvertently filter the header row).
  4. Add data- tags to your <tr>s.
  5. Add jQuery to respond to the keyup event in your filters.

Layout page

In your Views/Shared/_Layout page, there is a block of code at the end that looks like this:

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)

Move these to the line just after

    @Scripts.Render("~/bundles/modernizr")

...at around line 8.  The JQuery library needs to be loaded before the @RenderBody line.  This step only needs to happen once for your whole app, unless you're using multiple layout pages.

Filter Bar

Next we'll need a filter bar to the index page.

Let's say we want to filter by three fields:

I put this just before the <table>.

<div class="icon-bar">
    <span style="font-weight:bold;">Filters</span>&nbsp;&nbsp;
    State: <input name="State" id="state" type="text" style="width:35px;" maxlength="2" class="filter" />&nbsp;&nbsp;
    County: <input name="County" id="county" type="text" style="width:90px;" class="filter" />&nbsp;&nbsp;
    Town: <input name="Town" id="town" type="text" style="width:90px;" class="filter" />&nbsp;&nbsp;
    <span id="recordCount" style="font-weight:bold;">@Model.Count().ToString() records.</span>
</div>

class="icon-bar": use this class to style the bar how you like it: I used this in my style sheet, but you do as you like:

div.icon-bar{
    background:linear-gradient(#44f, #88F, #44F, #008, #000);
    padding:5px;
    margin-bottom:12px;
    border:1px solid black;
    border-radius:15px;
    color:white;
}

This makes the text in the input boxes white on white, so I added this:

input.filter{
    background-color:white;
    color:black;
}

thead and tbody

The first row in your table is the headers and we DON'T want to ever filter that row out.  So add a <thead> tag around the first <tr>

Now add a <tbody id="fbody"> around your data loop.

I use the id tag to find the tbody and access its rows in the jQuery.

Data- tags

Rather than use some wizardry to find what's in the <td> cells, I add custom tags to the <tr>s to help me hide/show them as a group.

@foreach (var item in Model){<tr data-state="@item.state" data-county="@item.county" data-town="@item.town">    

This adds tags for all the fields I want to filter for.

jQuery

This is easy enough.  Add a keyup function to all 3 filter fields.

<script type="text/javascript">
    $(".filter").keyup(function () { //this matches the class=filter on all 3 input boxes.
        //hide all trs in the tbody
        var rows = $("#fbody").find("tr").hide(); //this hides every row in the table body
        var workingList = rows;  //makes a list of all the table rows to work on
        //grab the text from the search box
        var state = $("#state").val().toLowerCase();
        var county = $("#county").val().toLowerCase();
        var town = $("#town").val().toLowerCase();

        //now show the ones where all values match

        if (state.length > 0) { //if there is any text in the first filter...
            //filter just returns a filtered list
            workingList = workingList.filter(function () {  //spawns a tiny inline function to test each row
                //return true if data-state starts with var state
                if ($(this).data("state").toLowerCase().indexOf(state) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }

        if (county.length > 0) {
            workingList = workingList.filter(function () {
                //return true if data-state starts with var state
                if ($(this).data("county").toLowerCase().indexOf(county) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }

        if (town.length > 0) {
            workingList = workingList.filter(function () {
                //return true if data-state starts with var state
                if ($(this).data("town").toLowerCase().indexOf(town) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }
        $(workingList).show();  //show the rows remaining in the working list.
        $("#recordCount").text(workingList.length+" records.") //update the record count in the filter bar.
    });
</script>


That's it! You can see how I
  1. hide all the rows in the <tbody>
  2. grab the strings from the filter boxes.
  3. copy the list of all rows to workingList.
  4. Loop through the filters...
    1. check to see if there is any text in the filter strings.  (if not, skip that filter.)
    2. enact a function on each <tr> that looks for the data- values to match the string entered by the user.
    3. this filters out all the <tr>s that do not match.
    4. the 3 filters all work together successively eliminating more rows from workingList until we are done.
  5. we issue a show() to all the rows that are left in the list.
  6. we update the count in the filter bar.

Also, you C# programmers like me: Don't freak out that there are returns in the middle of the function.  Those returns are in the middle of a small inline function, and return to the main function, not from it.

Matching

I chose a "startsWith" match, that's why my functions have a .indexof(state)==0.  Here's some different ways to match.

exact match

if ($(this).data("town").toLowerCase() == town) {

contains match

if ($(this).data("town").toLowerCase().indexOf(town) >= 0) {      This should be a good start to filtering for any ASP.NET MVC index page using jQuery.                

No comments:

Share This!

Contact Us

Name

Email *

Message *