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


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'"-->
    ></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  the promise
         return dataRest.get({ userInput : e.userInput, pageNumber : e.pageNumber }, function(result) {
             vm.resultList = result.persons;
             vm.resultTotalRows = result.totalRows;
         });
    };

}]);


Sample application layer code:

using System.Web.Http;


using Dapper;

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 @pageLimit rows only", 
                        new { filterName = dto.UserInput ?? "", 
                              offset = pageLimit * (dto.PageNumber-1), pageLimit = pageLimit });


                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.Dto
{
    public class PagingDto
    {
        public string UserInput { get; set; }        
        public int PageNumber { get; set; }        
    }
}   



using System.Collections.Generic;

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



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

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


Happy Coding!

Thursday, October 30, 2014

AngularJS: Confusing and non-symmetrical callback when using expression binding

There are three ways to communicate values between the directive and the controller


  • For one way string binding, we can use the '@' character
  • For more binding flexibility, we can use bi-directional binding by using the '=' character
  • And then for expression binding, we can use the '&' character



There are two ways to do a callback on Angular, one is to use bi-directional binding, another way is to use expression binding. Expression binding is the recommended way to do a callback, however I find it confusing on some scenario



Here's the complete code:
<div ng-controller="TheController">
  
    <sample-directive the-callback="informParent(theParam)">
        Nice
    </sample-directive>
    
    Received Value: {{valueReceivedFromDirective}}
    
</div>


var theApp = angular.module('theApp',[]);


theApp.directive('sampleDirective', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope : {
            theCallback : '&'
        },
        template: 
        '<div>' + 
            '<div ng-transclude></div>' + 
            '<hr/>' +
            '<div>Comments: <input ng-model="comments" ng-init="comments=\'Great\'"/></div>' +            
            '<div><button ng-click="theCallback({theParam: comments})">Click</button></div>' + 
        '</div>'
    };
});


theApp.controller('TheController', ['$scope', function($scope) {

    $scope.valueReceivedFromDirective = '';
    
    
    // If in case we need to reference a controller's property with same name as directive's parameter 
    // in the callback, there's no way to reference that controller's property. 
    // That is, this can't be referenced in the-callback
    $scope.theParam = 'Blah'; 
    
    // The name must be different from the directive's parameter name:
    $scope.theParamX = 'Meh'; // this can now be referenced in the directive's callback
    
    
    $scope.informParent = function(valuePassed) {    
        // code below is symmetrical to the directive's callback. however, it won't work
        // $scope.valueFromDirective = valuePassed.theParam; 
        
        // so should use this:
        $scope.valueReceivedFromDirective = valuePassed;
    };
    
}]);


Live code: http://jsfiddle.net/jrxh3w1p/1/


As we can see on line 16, we passed an object to the callback parameter, however when it's received on parent callback (line 38), the value received is not the whole object, the parent callback only receives the object's property, hence there's no need to access it as valuePassed.theParam (line 41). The symmetry is broken here

Another oddity, on line 16 it's confusing what property the theParam name is referring to, is it from the directive, or is it from the controller? It could be from the controller, as long as the name doesn't conflict from the directive's parameter name; in our example above, informParent's theParam name is referring to directive instead since the directive has theParam name in its parameter theCallback({theParam: comments}). Try to change line 3 of the HTML, to informParent(theParamX), check it here: http://jsfiddle.net/k4qtL7ez/2/. With expression binding (line 3 of HTML), we cannot ascertain on which property is the parameter in the callback is referring to unless we see the code of the directive; callback by means of expression binding makes the code confusing to read


Another oddity is we cannot pass the comments directly, see: http://jsfiddle.net/tq7pLj8x/1/. Rule is, when using expression binding (uses '&'), the parameter(s) from the directive passed to to the parent's callback must be enclosed in an object. And in parent's callback, the value received is the object's property, not the object itself




If we want to make things more symmetrical, we can use the bi-directional binding by using the '=' sign. Here are the changes:

<div ng-controller="TheController">
  
    <sample-directive the-callback="informParent">
        Nice
    </sample-directive>
    
    Received Value: {{valueReceivedFromDirective}}
    
</div>


var theApp = angular.module('theApp',[]);


theApp.directive('sampleDirective', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope : {
            theCallback : '='
        },
        template: 
        '<div>' + 
            '<div ng-transclude></div>' + 
            '<hr/>' +
            '<div>Comments: <input ng-model="comments" ng-init="comments=\'Great\'"/></div>' +            
            '<div><button ng-click="theCallback({theParam: comments})">Click</button></div>' + 
        '</div>'
    };
});


theApp.controller('TheController', ['$scope', function($scope) {
    $scope.name = 'Superhero';
    
    $scope.valueReceivedFromDirective = '';
    
    $scope.informParent = function(valuePassed) {    
    
        // the parameter passed to this function is now symmetrical how the directive call this function.
        // The directive passed an object to this function, this function received an object too:
        $scope.valueReceivedFromDirective = valuePassed.theParam;
    };
    
}]);

Live code: http://jsfiddle.net/vhx4huk2/


The changes in HTML (line 3) is we didn't call the function directly, we just passed the reference of the controller's method to the directive's property binding (the '=' sign, line #9). With property binding, if we passed an object from the directive's callback (line 16), the parent callback receives an object too (line 31); so if we passed an scalar value from the directive, the parent callback receives scalar value too. See: http://jsfiddle.net/vhx4huk2/1/



If there are no cons against callback using the property binding approach (using '='), it's a lot better than expression binding (using '&'). With property binding approach, there will be no confusion on which property the callback (we only pass the object's method reference) since there is no parameter being passed to it (line 3 of HTML). Another advantage is the caller and callee relationship is very symmetrical, caller passed an object or scalar, the callee receives the same parameter format



Happy Coding!

Sunday, October 12, 2014

Changes to model done outside of AngularJS' callback doesn't reflect on UI

Callbacks made outside of AngularJS can't be monitored by AngularJS, hence when there are changes on model it will not take effect on UI. An example:
<div ng-app='theApp' ng-controller='SampleController as c'>
    
    <label>Search</label><p><input type='text' ng-model='c.topic'/>    
    
    <button ng-click='c.getTopMatch()'>Get Top Match</button>
    
    <p>
        <span ng-show='c.topMatch.length !=""'>Top Match: {{c.topMatch}}<span> 
    </p>
        
</div>


var app = angular.module('theApp', ['oitozero.ngSweetAlert']);

app.controller('SampleController',['$http', 'SweetAlert', function($http, SweetAlert) {
    var self = this;
    
    self.topic = 'angularjs';

    self.topMatch = '';

    
    self.getTopMatch = function() {
                        
        $http.jsonp('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&callback=JSON_CALLBACK',
                    {
                        params : { 'q' : self.topic }
                    })
        .success(function(data) {        

            self.topMatch = data.responseData.results[0].url;  
            
            SweetAlert.swal({
               title: "Clear Search?",
               text: "Everyone wants a clear textbox",
               type: "success",
               showCancelButton: true,
                cancelButtonText: 'No',
               confirmButtonText: "Yes!"
            }, 
            function(isConfirm){                        
     
                if (!isConfirm) return;
                  
                self.topic = '';
                
                
            });      
            
        });     
        
    };
}]);


We can solve that by wrapping our changes on model inside of AngularJS $q service:
var app = angular.module('theApp', ['oitozero.ngSweetAlert']);

app.controller('SampleController',['$http', '$q', 'SweetAlert', function($http, $q, SweetAlert) {
    var self = this;
    
    self.topic = 'angularjs';

    self.topMatch = '';

    
    self.getTopMatch = function() {
                        
        $http.jsonp('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&callback=JSON_CALLBACK',
                    {
                        params : { 'q' : self.topic }
                    })
        .success(function(data) {        

            self.topMatch = data.responseData.results[0].url;  
            
            SweetAlert.swal({
               title: "Clear Search?",
               text: "Everyone wants a clear textbox",
               type: "success",
               showCancelButton: true,
               cancelButtonText: 'No',
               confirmButtonText: "Yes!"
            }, 
            function(isConfirm){                        

                if (!isConfirm) return;                

                var deferred = $q.defer();
                deferred.promise.then(function() {
                    self.topic = '';
                });
                
                deferred.resolve();
                
            });      
            
        });     
        
    };
}]);


Note that we need to inject $q service to our controller. If that's a bit overkill, we can skip the use of $q service and use another approach. Another approach is to dynamically add a promise on existing $http's promise when a callback outside of AngularJS is made, an example:

var app = angular.module('theApp', ['oitozero.ngSweetAlert']);

app.controller('SampleController',['$http', 'SweetAlert', function($http, SweetAlert) {
    var self = this;
    
    self.topic = 'angularjs';

    self.topMatch = '';

    
    self.getTopMatch = function() {
                        
        var thePromise = 
            $http.jsonp('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&callback=JSON_CALLBACK',
                    {
                        params : { 'q' : self.topic }
                    })
            .success(function(data) {        
    
                self.topMatch = data.responseData.results[0].url;  
                
                SweetAlert.swal({
                   title: "Clear Search?",
                   text: "Everyone wants a clear textbox",
                   type: "success",
                   showCancelButton: true,
                    cancelButtonText: 'No',
                   confirmButtonText: "Yes!"
                }, 
                function(){                                        
                    
                    thePromise.then(function() {
                        self.topic = '';
                    });                                
                    
                });      
                
            });     
        
    };
}]);


UPDATE


Another good approach is to use $timeout, unlike $scope.$apply/$scope.$digest, $timeout is testable. Though it works, it is not advisable to use $scope.$apply/$scope.$digest in a controller. $timeout works and it is testable:

var app = angular.module('theApp', ['oitozero.ngSweetAlert']);

app.controller('SampleController',['$http', '$timeout', 'SweetAlert', function($http, $timeout, SweetAlert) {
    var self = this;
    
    self.topic = 'angularjs';

    self.topMatch = '';

    
    self.getTopMatch = function() {
                        
        $http.jsonp('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&callback=JSON_CALLBACK',
                    {
                        params : { 'q' : self.topic }
                    })
        .success(function(data) {        

            self.topMatch = data.responseData.results[0].url;  
            
            SweetAlert.swal({
               title: "Clear Search?",
               text: "Everyone wants a clear textbox",
               type: "success",
               showCancelButton: true,
                cancelButtonText: 'No',
               confirmButtonText: "Yes!"
            }, 
            function(isConfirm){                        

                if (!isConfirm) return;
                
                $timeout(function() {
                    self.topic = '';
                });
                
            });      
            
        });     
        
    };
}]);


Happy Coding!

Thursday, October 9, 2014

AngularJS Get Its POJO Back

Earlier AngularJS version (e.g., 0.9) has no $scope:

function GreetingCtrl(){
    this.greeting = 'Hello';
    this.name = 'Michael';
}    

<div ng:controller='GreetingCtrl'>
      <p>{{greeting}} {{name}}</p>
</div>    

Live example: http://jsfiddle.net/ywuwwgu5/


That's one of the things I find elegant when AngularJS was in its infancy. Aside from there's no special class to inherit from, there is no specialized object(e.g., $scope) the framework need to use in order to do model-binding, just a POJO would bind fine

So on later version of AngularJS when $scope was introduced, it felt like, 'oh, my javascript code would be tied to AngularJS!' I'm thinking why the newer AngularJS version can't do the good old magic where the older framework can bind directly to object's properties, to a POJO


Though porting the code to other javascript MV* frameworks it is not the main overriding concern developers has to face when deciding to use AngularJS or not, there's still a value in making a framework able to work with generic code as much as possible. If there's another javascirpt MV* framework that can bind directly to a POJO like the code above, we can just plug that framework to our generic javascript code and we don't have to rewrite a single line of code.

Code portability feels empowering, just like it feel empowering that your SQL knowledge(or at least a subset of) on one RDBMS is transferable to another RDBMS with little to no rewrite. Unlike with using specialized object like $scope, it feels $scope has many moving parts in it and code portability is just a pipe dream, your code is forever tied to AngularJS; nothing wrong with a code associated to high-quality framework like AngularJS, but still



Now that there is Controller as syntax, we could make our javascript code as generic as possible(and portable), again, yay!

With exactly the same generic AngularJS 0.9 controller code above, we can now re-use it on AngularJS 1.2 using Controller as syntax. We just have to adjust our view, we need to prefix the controller's alias on models we wanted to bind to. Neat!
<div ng-controller='GreetingCtrl as g'>
    <p>{{g.greeting}} {{g.name}}</p>
</div>    


Live code: http://jsfiddle.net/x8mwtox9/


A nice insight so AngularJS 1.2's Controller as won't feel too magical, in AngularJS 1.1 we can also use the this approach of AngularJS 1.2 controller above (albeit without the Controller as syntax) and it would still work. AngularJS 1.1 has no Controller as, but we can achieve the AngularJS 1.2 this code and view just by assigning this on a variable in $scope object:
function GreetingCtrl($scope){
    $scope.g = this;
    this.greeting = 'Hello';
    this.name = 'Michael';
}    

<div ng-controller='GreetingCtrl'>
   <p>{{g.greeting}} {{g.name}}</p>
</div>       

Live code: http://jsfiddle.net/hqfhutLo/


With AngularJS 1.2's Controller as feature, aside from it removes the need of assigning this to $scope manually, we don't have to use specialized object such as $scope anymore. Very generic code, neat! :-)



Happy Coding!

Sunday, October 5, 2014

Use citext on PostgreSQL

Case-insensitive text type is not installed by defaut on Postgres, to use, run this in pgAdmin:

CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;

Wednesday, October 1, 2014

404 when accessing a subdirectory from nginx

I deployed an ASP.NET MVC application on Ubuntu + nginx + fastcgi-mono-server4, this works:

http://www.example.com/


However this doesn't:

http://www.example.com/Companies/Search?q=softwaremint


The fix is apparently simple, instead of letting nginx manage the subdirectories, let ASP.NET MVC manage it by removing the following (configuration is in /etc/nginx/sites-available/default) :

try_files $uri $uri/ =404;

Failed reloading nginx configuration nginx

michael@buen:/etc/init.d$ sudo nginx -t &&  service nginx reload
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
 * Reloading nginx configuration nginx                                   [fail]
michael@buen:/etc/init.d$ sudo nginx -t && sudo service nginx reload
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
 * Reloading nginx configuration nginx                                   [ OK ]

Put sudo on service too