Click here to Skip to main content
15,867,308 members
Articles / Web Development / HTML

Indexed Search Filter Using ASP.NET MVC, jQuery and Ajax Requests

Rate me:
Please Sign up or sign in to vote.
4.95/5 (27 votes)
16 Dec 2016CPOL2 min read 35.4K   569   15   13
In this article we'll discuss an approach of using jQuery's Ajax requests to implement an indexed search filter based on an example of creating a simple phone book web application with ASP.NET MVC.

Introduction

In the following article, we'll demonstate an approach on how to implement a indexed search filter for a simple phone book web application developed using ASP.NET MVC. During the discussion we'll learn how to use jQuery's AJAX requests to dynamically update the contents of the phone book list making it responsive to the user input. The web application being discussed retrieves the list of phone numbers from the database and based on the data being retrieved generates a web page containing the list of persons which phone numbers are arranged into a table as well as a textbox in which user can what particular person or phone number he'd like to find. The following phone book application allows to find a person or phone number by a partitial match. When user inputs in the textbox field what person or number he's about to find, the table containing the list of person's phone numbers is modified by displaying the phone numbers or names of only those persons that exatcly match the search criteria.

Using the code

PhoneBookController.cs: The following fragment of code implements PhoneBookController controller that contains the following actions: the Search action method retrieves the data by connecting to the local database by executing an SQL-query being constructed during the following action method execution. It stores the data being retrived to the list defined within the data model. Another action method Modify is used to insert phone book entries into the database by performing the specific SQL-query:

C#
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace IndexedSearch.Controllers
{
    public class PhoneBookController : Controller
    {
        private Models.SearchModel SearchModel = new Models.SearchModel();
        public ActionResult Index()
        {
            return View();
        }
        string SqlGetConnectionString(string ConfigPath, string ConnectionStringName)
        {
            System.Configuration.Configuration rootWebConfig =
                            System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(ConfigPath);
            System.Configuration.ConnectionStringSettings connectionString =
                rootWebConfig.ConnectionStrings.ConnectionStrings[ConnectionStringName];
            if (connectionString == null || string.IsNullOrEmpty(connectionString.ConnectionString))
                throw new Exception("Fatal error: Connection string is missing from web.config file");

            return connectionString.ConnectionString;
        }
        public ActionResult Search(string text)
        {
            using (SqlConnection connection =
                       new SqlConnection(this.SqlGetConnectionString("/Web.Config", "PhoneBookDB")))
            {
                try
                {
                    string SqlQuery = @"SELECT dbo.Contacts.* FROM dbo.Contacts";
                    if (Request.IsAjaxRequest() && text != "")
                        SqlQuery += " WHERE dbo.Contacts.ContactName LIKE @text OR dbo.Contacts.Phone LIKE @text";

                    SqlCommand command = new SqlCommand(SqlQuery, connection);
                    command.Parameters.AddWithValue("@text", String.Format("%{0}%", text));

                    connection.Open();

                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read() && reader.HasRows != false)
                        {
                            Models.PhoneBookEntry PhoneEntry = new Models.PhoneBookEntry();
                            PhoneEntry.ContactID = Int32.Parse(reader["ContactID"].ToString());
                            PhoneEntry.ContactName = reader["ContactName"].ToString();
                            PhoneEntry.Phone = reader["Phone"].ToString();

                            if ((!PhoneEntry.ContactID.Equals("")) &&
                                (!PhoneEntry.ContactName.Equals("")) && (!PhoneEntry.Phone.Equals("")))
                                SearchModel.PhoneList.Add(PhoneEntry);
                        }

                        reader.Close();
                    }
                }

                catch (Exception ex) { Console.WriteLine(ex.Message); }
            }

            return PartialView(SearchModel.PhoneList);
        }
        public ActionResult Create(string person, string phone)
        {
            using (SqlConnection connection =
                       new SqlConnection(this.SqlGetConnectionString("/Web.Config", "PhoneBookDB")))
            {
                try
                {
                    string SqlQuery = @"INSERT INTO dbo.Contacts VALUES (@person, @phone)";
                    SqlCommand command = new SqlCommand(SqlQuery, connection);
                    command.Parameters.AddWithValue("@person", person);
                    command.Parameters.AddWithValue("@phone", phone);

                    connection.Open();
                    command.ExecuteNonQuery();
                    connection.Close();
                }

                catch (Exception ex) { Console.WriteLine(ex.Message); }
            }

            return RedirectToAction("Index");
        }
    }
}

SearchModel.cs: SearchModel class instantinates the generic List<T> class used to store the set of phone book entries. As the parameter type T we use another class _PhoneBookEntry which represents an entry to the phone book list. SearchModel is used as the main data model of the following ASP.NET MVC project:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace IndexedSearch.Models
{
    public class PhoneBookEntry
    {
        private int contact_id;
        private string contact_name;
        private string phone;

        public int ContactID {
            get { return (!contact_id.Equals(-1)) ? contact_id : -1; }
            set { contact_id = (!value.Equals(-1)) ? value : -1; } }
        public string ContactName {
            get { return (!contact_name.Equals("")) ? contact_name : ""; }
            set { contact_name = (!value.Equals("")) ? value : ""; } }
        public string Phone
        {
            get { return (!phone.Equals("")) ? phone : ""; }
            set { phone = (!value.Equals("")) ? value : ""; }
        }
    }

    public class SearchModel
    {
        private List<PhoneBookEntry> phone_list = null;
        public SearchModel()
        {
            if (phone_list == null)
                phone_list = new List<PhoneBookEntry>();
        }

        public List<PhoneBookEntry> PhoneList
            { get { return (phone_list != null) ? phone_list : null; } }
    }
}

Index.cshtml: The following code snippet is a web-page contaning the two forms. The first form is the SearchForm that contains the editable textbox area in which user inputs the text to find a phone book entry that partitially matches the creteria of search. In the Scripts section of the following web-page we normally define jQuery code that implement the textbox area event handling. When user modifies the textbox area the paste/keyup event is fired the following code normally dispatches an AJAX-request to the specific controller's action that retrieves the data from the database and generates the HTML contents. After that another jQuery clause dynamically modifies the phone book's main page.

ASP.NET
@{
    ViewBag.Title = "Index";
}

<table border="1" align="center" width="500" cellpadding="0" cellspacing="0">
    @using (Html.BeginForm("SearchForm", "PhoneBookController", FormMethod.Get))
    {
        @Html.ValidationSummary(true);
        <tr><td>
          <table align="center" width="300" cellpadding="0" cellspacing="0">
               <tr><td><p><b>Find a contact:</b><br />@Html.TextBox("SearchBox",
                     null, new { @class = "search_box", @id = "search" })</p></td></tr></table>
        </td></tr>
        <tr><td>
          <div id="search_results">@{Html.RenderAction("Search", new { Text = "Empty" });}</div>
        </td></tr>
    }
    @{Html.EndForm();}

    <tr><td><br />
     <form method="get" action=@Url.Action("Create", "PhoneBook") id="createform" autocomplete="off">
      <table border="1" align="center">
       <tr>
        <td><b>Person:</b></td>
        <td>
          @Html.TextBox("Person",
            null, new { @class = "person_box", @id = "person" })
        </td>
       </tr>
        <tr><td colspan="2"><div id="invalid_person" style="color: red; visibility: hidden">Specify the correct person's name 3-31 characters</div></td></tr>
        <tr>
         <td><b>Phone:</b></td>
         <td>
           @Html.TextBox("Phone",
             null, new { @class = "phone_box", @id = "phone" })
         </td>
        </tr>
        <tr><td colspan="2"><div id="invalid_phone" style="color: red; visibility: hidden">Specify the correct phone number e.g. +x(xxx)xxx-xxxx</div></td></tr>
        <tr><td colspan="2"><input type="button" id="submit_btn" value="Submit>" /></td></tr>
      </table>
    </form>
</td></tr>
</table>

@section Scripts {
    <script type="text/javascript">
        $("#person").on("keyup paste", function () {
          var person_match = $("#person").val().match(/^[a-zA-Z\.\s]*$/);
          var is_visible = (person_match == null) && ($("#person").val()) ? "visible" : "hidden";
          $("#invalid_person").css({ 'visibility': is_visible });
        });

        $("#phone").on("keyup paste", function () {
          var is_visible = ($("#phone").val()) && ($("#phone").val().length > 15) ||
                ($("#phone").val().match(/^[a-zA-Z\.\s]*$/) != null) ? "visible" : "hidden";
          $("#invalid_phone").css({ 'visibility': is_visible });
        });

        $("#submit_btn").click(function () {
            var person_match = $("#person").val().match(/^[a-zA-Z\.\s]*$/);
            var phone_match = $("#phone").val.length > 0 && $("#phone").val().match(/^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/);
            var is_invalid_phone = (phone_match == null) || (!$("#phone").val()) || ($("#phone").val().length < 10) ? "visible" : "hidden";
            var is_invalid_person = (person_match == null) && ($("#person").val()) ? "visible" : "hidden";
            $("#invalid_phone").css({ 'visibility': is_invalid_phone });
            if (person_match != null && phone_match != null) $("#createform").submit();
        });

        $("#search").on("keyup paste", function () {
            $.ajax({
                type: "GET",
                url: '@Url.Action("Search","PhoneBook")'+"/?text="+$("#search").val(),
                dataType: 'html',
                contentType: 'application/html; charset=utf-8',
                success: function (content, ajaxObj) {
                    $("#search_results").html(content);
                },
            });
        })
    </script>
}

Search.cshtml: The following fragment of code implements a web-page, in which we're using ASP.NET script language to generate and display the list of phone book entries obtained from the data model.

ASP.NET
@model List<IndexedSearch.Models.PhoneBookEntry>
<table border="0" align="center" width="500" cellpadding="0" cellspacing="0">
    <tr>
        <td style="text-align: center; background-color: black; color: white; width: 90px"><b>Contact ID</b></td>
        <td style="text-align: left;   background-color: black; color: white; width: 250px"><b>Contact Name</b></td>
        <td style="text-align: center; background-color: black; color: white; width: 150px"><b>Phone</b></td>
    </tr>

    @{ int line_id = 0; string bgcolor = ""; }
    @foreach (var PhoneBookEntry in Model)
    {
        <tr>
            @{ bgcolor = (line_id++ % 2) == 0 ? "white" : "lightgrey"; }
            <td style="background-color: @bgcolor; text-align: center; width: 90px">@line_id</td>
            <td style="background-color: @bgcolor; text-align: left;   width: 250px">@PhoneBookEntry.ContactName</td>
            <td style="background-color: @bgcolor; text-align: center; width: 150px">@PhoneBookEntry.Phone</td>
        </tr>
    }
</table>

History

  • December 13, 2016 - The first version of the article has been published.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) EpsilonDev
Ukraine Ukraine
I’m software developer, system analyst and network engineer, with over 20 years experience, graduated from L’viv State Polytechnic University and earned my computer science and information technology master’s degree in January 2004. My professional career began as a financial and accounting software developer in EpsilonDev company, located at L’viv, Ukraine. My favorite programming languages - C/C++, C#.NET, Java, ASP.NET, Node.js/JavaScript, PHP, Perl, Python, SQL, HTML5, etc. While developing applications, I basically use various of IDE’s and development tools, including Microsoft Visual Studio/Code, Eclipse IDE for Linux, IntelliJ/IDEA for writing code in Java. My professional interests basically include data processing and analysis algorithms, artificial intelligence and data mining, system analysis, modern high-performance computing (HPC), development of client-server web-applications using various of libraries, frameworks and tools. I’m also interested in cloud-computing, system security audit, IoT, networking architecture design, hardware engineering, technical writing, etc. Besides of software development, I also admire to write and compose technical articles, walkthroughs and reviews about the new IT- technological trends and industrial content. I published my first article at CodeProject in June 2015.

Comments and Discussions

 
QuestionNice article , however is that all validation required in controllers Pin
deepankur.singh260715-Dec-16 11:40
deepankur.singh260715-Dec-16 11:40 
AnswerRe: Nice article , however is that all validation required in controllers Pin
Arthur V. Ratz15-Dec-16 16:15
professionalArthur V. Ratz15-Dec-16 16:15 
AnswerRe: Nice article , however is that all validation required in controllers Pin
Arthur V. Ratz16-Dec-16 2:37
professionalArthur V. Ratz16-Dec-16 2:37 
PraiseNice article Pin
Jyoti Kumari9614-Dec-16 7:27
Jyoti Kumari9614-Dec-16 7:27 
GeneralRe: Nice article Pin
Arthur V. Ratz14-Dec-16 16:29
professionalArthur V. Ratz14-Dec-16 16:29 
GeneralMember prefix m_ Pin
Rene Balvert14-Dec-16 5:00
Rene Balvert14-Dec-16 5:00 
GeneralRe: Member prefix m_ Pin
Arthur V. Ratz14-Dec-16 5:32
professionalArthur V. Ratz14-Dec-16 5:32 
GeneralRe: Member prefix m_ Pin
Arthur V. Ratz14-Dec-16 5:58
professionalArthur V. Ratz14-Dec-16 5:58 
GeneralRe: Member prefix m_ Pin
Rene Balvert14-Dec-16 23:39
Rene Balvert14-Dec-16 23:39 
GeneralRe: Member prefix m_ Pin
Arthur V. Ratz14-Dec-16 23:51
professionalArthur V. Ratz14-Dec-16 23:51 
GeneralRe: Member prefix m_ Pin
Jacques Fournier20-Feb-17 12:14
Jacques Fournier20-Feb-17 12:14 
GeneralRe: Member prefix m_ Pin
Rene Balvert20-Feb-17 23:03
Rene Balvert20-Feb-17 23:03 
Sorry to correct you, they are not using a _ for instance variables, but for private members and probably backing store for public properties.

Using a _ for private members is an common adopted coding style and in fact a bit more safe than using camel case backing stores, but others prefer camel case and using this.* to be sure the instance member is taken. It depends on the team or project, but Microsoft recommends the camel case style.

And using this. makes any prefix obsolete as you guaranteed referencing a member instance, with a prefix this is not guaranteed (void Foo(int _someparamater));

Using a _ for public instance members is a no-go at all.
BugVulnerable to SQL Injection Pin
stooboo14-Dec-16 1:55
stooboo14-Dec-16 1:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.