How to limit a Kendo UI combobox drop down to valid items using ASP.NET MVC Wrappers

restrict-combo-kendo-ui

A client I work with needed to have a combobox that would support both a drop down list & the ability to type in text which would filter the drop down results to allow for quick identification of the correct value they were looking for. This is very helpful when dealing with account numbers & any data that is difficult to visually identify when in a large list. The catch is that any manually entered value needs to be a valid value contained in the list (we don’t want any bogus account numbers being entered).

The following solution leverages the Kendo UI Complete ASP.NET MVC Wrappers & extends it’s functionality via custom client side script which is registered with the “Change” event

<div class="form-group">
    @Html.LabelFor(model => model.GlAccount, new { @class = "control-label col-md-4" })
    <div class="col-md-6">
        @(Html.Kendo<OrderRequest>().ComboBoxFor(m => m.GlAccount).DataValueField("Number").DataTextField("Description").Filter(FilterType.Contains).HighlightFirst(true)
                        .DataSource(src => src.Read(read => read.Action("GetGlAccounts", "Lookup", new { area = "" }))).Events(events => events.Change("app.common.onChangeRestrictValues")))
        @Html.ValidationMessageFor(model => model.GlAccount)
    </div>
</div>
_PO_ReferenceInformation.cshtmlview rawview file on GitHub

The “app.common.onChangeRestrictValues” function determines if the value is present in the drop down list by checking the selectedIndex. Only valid values from the drop down list will have a non -1 value. If the entered text is considered invalid then we empty the input box to let the user know this.

    /* Restrict entering/selecting values that are not defined for Kendo ComboBox  */
    onChangeRestrictValues: function () {
        if (this.value() && this.selectedIndex == -1) {
            var dt = this.dataSource._data[0];
            this.text('');
        }
    }

The “onChangeRestrictValues” function is contained in the javascript file “app.common.js”, which provides common/generic functionality which can be reused in any Kendo UI project. Please feel free to plug & play. Just make sure you load the file correctly as shown below

@model FacilitiesManagement.Models.OrderRequest

@{
    ViewBag.Title = "PlaceOrder";
}


@section head_section{
    <link href="~/Content/Onokumus.css" rel="stylesheet" />
    <link href="~/Content/FontAwesome.css" rel="stylesheet" />
    <script src="~/Scripts/app/app.common.js"></script>
}

<h2>PlaceOrder</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

If you’re interested in the plumbing for this solution and analyze the code you’ll notice we’re using an ajax datasource to populate the values of the combobox. You can use either this method or a client side list passed by using a view model. Below you’ll see the pieces of server code that generate the list data for us. First is the controller class that leverages the IGeneralLedgerService interface.

    public class LookupController : Controller
    {
        private readonly IGeneralLedgerService _glService;
        public LookupController(IGeneralLedgerService glService)
        {
            _glService = glService;
        }

        //
        // GET: /Lookup/
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetGlAccounts()
        {
            var glAccounts = _glService.GetAllGlAccounts();
            return Json(glAccounts, JsonRequestBehavior.AllowGet);
        }

    }
LookupController.csview rawview file on GitHub

Next is a mock implementation that produces a list of items for us to use during prototyping and testing.

    public class MockGeneralLedgerService : IGeneralLedgerService
    {

        public IQueryable<Account> GetAllGlAccounts()
        {
            var list = new List<Account>
            {
                new Account {Number = "8800420", Name = "Fire Protection"},
                new Account {Number = "8800422", Name = "Purchased Parts"},
                new Account {Number = "8800424", Name = "Outside Maintenance"},
                new Account {Number = "8800426", Name = "Maintenance: Lands & Buildings"},
                new Account {Number = "8800430", Name = "Janitorial Services"},
                new Account {Number = "8800434", Name = "Supplies: Office"},
                new Account {Number = "8800436", Name = "Publications & Subscriptions"},
                new Account {Number = "8800438", Name = "Supplies: Production"},
                new Account {Number = "8800440", Name = "Supplies: Maintenance"},
                new Account {Number = "8800442", Name = "Supplies: Computer"},
                new Account {Number = "8800444", Name = "IT Solutions: Software"},
                new Account {Number = "8800446", Name = "Vehicle Operating Expense"},
                new Account {Number = "8800448", Name = "Advertising"},
                new Account {Number = "8800452", Name = "Furniture"},
                new Account {Number = "8800454", Name = "Rent: Systems Equipment"},
                new Account {Number = "8800456", Name = "Rent: Machinery & Equipment"},
                new Account {Number = "8800458", Name = "Electricity"},
                new Account {Number = "8800460", Name = "Gas and Fuel"},
                new Account {Number = "8800462", Name = "Water and Waste Water"},
                new Account {Number = "8800464", Name = "Asbestos Abatement Act"}

            };
            return list.AsQueryable();
        }
    }
MockGeneralLedgerService.csview rawview file on GitHub

We’re using Autfoac as the IoC container in our ASP.NET MVC project and wiring things up in a static method called during Application_Start in our global.asax file.

    public class AutofacConfig
    {
        public static void RegisterDependencies()
        {
            var builder = new ContainerBuilder();
            builder.RegisterControllers(typeof(MvcApplication).Assembly);
            builder.RegisterFilterProvider(); //Inject Properties Into FilterAttributes section of MvcIntegration
            builder.RegisterSource(new ViewRegistrationSource());
            builder.RegisterType<MockGeneralLedgerService>().As<IGeneralLedgerService>();
            var container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        }

    }

The following links helped in putting together a solution that met our goals

Limiting ComboBox to “Valid” Items
Avoid ComboBox sending values that aren’t on the list
Combobox Change Event
Combobox Value Method
Combobox Text Method
Combobox Select Method

Hope this helps anyone out who has run into the same issue.

Leave A Response