Saturday, November 1, 2014

Angular Ajax Combobox

Angular Ajax Combobox component

Features:
  • On-demand loading of list
  • Paged list
  • Can use page up / page down shortcut when navigating the list
  • Can type on textbox anytime even when the dropdown list is displayed
  • Can use mouse scroll on the popup list
  • Uses AngularJS 1.3 ng-model debounce functionality to prevent fast typist from overwhelming the network
  • Keyboard shortcut to popup the list uses the conventional shortcut, Alt+Down, or F4
  • Aside from next page / previous page buttons, it has fast forward and fast reverse button, it partition the list by 100 instead of the usual ten for paging. Say we have 5,000 rows, a total of 500 pages, so from first page when we click the fast forward button it brings us to page 51
  • Can manually assign both ID/Code and Text for the combobox, by using ng-model and user-input attributes respectively. Think edit
  • When pressing escape key, it reverts back the old value. Likewise when pressing tab, yet the user didn't select an item, the combobox will revert to old ID/Code and Text values
  • Developer still has control on the requested ajax url's parameter names, and also with the result's list and total's property names. The component doesn't impose any names for the ajax's url parameter names and response property names
  • ng-change event, things like cascading combobox is possible
  • Combobox width can be overriden
  • The popup's width has the same width as the combobox
  • Uses bootstrap for the design
  • No jQuery
  • Page Size


Front-end code:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
 
    <script src="Scripts/angular.min.js"></script>
    <script src="Scripts/angular-resource.min.js"></script>
 
    <link rel="stylesheet" href="Content/bootstrap.min.css" />
 
    <link rel="stylesheet" href="Content/kel.ui.css" />
    <script src="Scripts/kel.ui.js"></script>
 
    <script src="Scripts/app.js"></script>
 
 
</head>
<body ng-app="TheApp" ng-controller="TheController as c">
 
 
    <h3>Angular Ajax Combobox Demo</h3>
 
 
    <div>PersonId chosen: {{c.personId}}</div>
    <br />
    <div style="float: left">Person name:&nbsp;</div>
    <kel-ajax-combobox ng-model="c.personId"
                       user-input="c.personName"
                       result-getter="c.resultGetter"
                       result-list="c.resultList"
                       result-total-rows="c.resultTotalRows"
                       selected-value="'id'"
                       selected-text="'fullName'"
         <!--width="'450px'"-->
         <!--page-size="20"-->
    ></kel-ajax-combobox>
 
</body>
</html>
Output:
app.js:
var app = angular.module('TheApp', ['ngResource', 'kel.ui']);


app.controller('TheController', ['$http', '$resource', function($http, $resource) {
    var vm = this;

    vm.personId = 5500;
    vm.personName = 'Aaron Butler';

    vm.resultList = [];
    vm.resultTotalRows = 0;

    var dataRest = $resource('http://localhost:63692/api/AdventureWorksPeople');

    vm.resultGetter = function(e) {      
         return dataRest.get({ userInput : e.userInput, pageNumber : e.pageNumber, pageSize : e.pageSize }, function(result) {
             vm.resultList = result.persons;
             vm.resultTotalRows = result.totalRows;
         });
    };

    //// This now works:

    //vm.resultGetter = function(e) {
    //        return dataRest.get({ userInput : e.userInput, pageNumber : e.pageNumber, pageSize : e.pageSize }).$promise.then(function(result) {
    //            vm.resultList = result.persons;
    //            vm.resultTotalRows = result.totalRows;
    //        });
    //};

}]);


Sample application layer code:

using System.Web.Http;

using Dapper;

using System.Web.Http;
using System.Collections.Generic;

using ReadyAspNetWebApiCors.Dtos;


namespace ReadyAspNetWebApiCors.Controllers
{
    
    [System.Web.Http.Cors.EnableCors(origins: "*", headers: "*", methods: "*")]
    public class AdventureWorksPeopleController : ApiController
    {
        // GET api/<controller>
        public PagedDtoResult Get([FromUri] PagingDto dto)
        {
            int pageLimit = 10;

            using (var con = new System.Data.SqlClient.SqlConnection(
                                 "Server=.; Database=AdventureWorks2012; Trusted_Connection=true;"))
            {
                var persons = con.Query<PersonDto>(
                    @"with x as (
                        select Id = BusinessEntityId, FullName = FirstName + ' ' + LastName 
                        from Person.Person 
                    )
                    select * 
                    from x
                    where FullName like @filterName + '%' or @filterName = ''
                    order by FullName 
                    offset @offset rows fetch next @pageSize rows only", 
                        new { filterName = dto.UserInput ?? "", 
                              offset = pageLimit * (dto.PageNumber-1), pageSize = dto.PageSize });


                var totalRows = con.ExecuteScalar<int>(
                    @"with x as (
                        select FullName = FirstName + ' ' + LastName 
                        from Person.Person 
                    )
                    select count(*)
                    from x
                    where FullName like @filterName + '%' or @filterName = ''", 
                        new { filterName = dto.UserInput ?? "" });


                return new PagedDtoResult
                {
                    Persons   = persons,
                    TotalRows = totalRows
                    
                };
            }
        }
    
    }
}




namespace ReadyAspNetWebApiCors.Dtos
{
    public class PersonDto
    {
        public int    Id        { get; set; }
        public string FullName  { get; set; }
    }
}

namespace ReadyAspNetWebApiCors.Dtos
{
    public class PagingDto
    {
        public string UserInput { get; set; }        
        public int    PageNumber { get; set; }
        public int    PageSize   { get; set; }
    }
}   





namespace ReadyAspNetWebApiCors.Dtos
{
    public class PagedDtoResult
    {
        public int TotalRows { get; set; }
        public IEnumerable<PersonDto> Persons { get; set; }
    }
}



// Add this on WebApiConfig.Register

config.EnableCors();


var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
settings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();



Download the library from: http://www.nuget.org/packages/kel.angular.ajax.combobox/

Git: https://github.com/MichaelBuen/AngularAjaxComboBox


Happy Coding!