Pages

Sunday, September 20, 2015

Cascading select list using custom directive of angular JS that takes json inputs.

I was trying my hand with Angular JS as it is becoming very famous front end framework. For more details about Angular JS, you may visit https://angularjs.org/.

One of the problem that I stuck was to implement a cascading select list. A cascading select is actually a set of select lists; those are interrelated to each other. Its like a map of entries where selection of first select list, filters result of second select list and so on. One of the useful example is - assume you have three select lists one for country another one is for state and last one is for city. Selecting country filters record of state and selecting state filters record of city. Now without talking much let's see to tackle this.-

The first page is index.html where you would like to show the select lists. Here is list of thing we should notice about this file.
  • The head tag has angular JS dependency which all required to build and AngularJS app.
  • cascading.js is our js file that we will explain in a while
  • first div under body tag has ng-app attribute. This informs AngularJS that this is the div that will contain angular JS related stuff. That simply means that if we write any AngularJS specific code outside this; it will be skipped.
  • another div is stating about controller of angular JS.
  • The most important part of this html is cascading tag. This is not a html tag but it is created as part of angular JS custom directive. This custom directive is taking three JSON city-state-map, country-map and state-country-map al used to supply data to custom directive. 

<!doctype html>
<meta charset="utf-8">
<html>
<head>
    <title>Adnan Try</title>
    <script type="text/javascript" src="../js/1.4.5/angular.min.js"></script>
    <script type="text/javascript" src="cascading.js"></script>
</head>
<body>
<div ng-app="cascading">
    <div ng-controller="CascadingCtrl">
        <cascading city-state-map = '{
        "cityStateMap" : [
                   {
                   "stateId": 111, "cities": [
                                            {id: 123, "label": "Akbar-pur"},
                                            {id: 124, "label": "Lucknow"}
                                            ]
                    },
                    {
                    "stateId": 112, "cities": [
                                                {id: 124, "label": "Lucknow"}]
                    }
        ]
        }'
                   state-country-map = '{
                   "stateCountryMap":[
                    {
                    "countryId" : 106, "states" : [{"id" : 111, "label" : "Argentina"},
                                                   {"id" : 112, "label" : "Delhi"}]
                    },
                    {
                    "countryId" : 107, "states" : [{"id" : 112, "label" : "Delhi"}]
                    }
        ]}'

                   country-map =  '{
    "countryMap": [
        {
        "id": 106,
        "label": "US"
        },
        {
        "id": 107,
        "label": "INDIA"
        },
        {
        "id": 110,
        "label": "UP"
        }
        ]
        }'

                >

        </cascading>
    </div>
</div>
</body>
</html>
Another file cascading.js file. It actually has all AngularJS specific logic to play around. it creates directive and handles changing value of one select list using another. The code is as follows-
angular.module('cascading', [])
    .controller('CascadingCtrl', ['$scope', function($scope) {

    }])
    .directive('cascading', function() {
        return {
            restrict : "E",
            scope : {
                countryMap : '@',
                stateCountryMap : '@',
                cityStateMap : '@'
            },
            templateUrl: 'cascading.html',
            link: function($scope, $element, $attribute) {
                var newVal = $scope.$eval('(' + $scope.countryMap + ')');
                $scope.countries = newVal.countryMap;
                $scope.countryChange = function(val){
                    $scope.cityMap = null;
                    var stateArray = $scope.$eval('(' + $scope.stateCountryMap + ')');
                    var filtered = stateArray.stateCountryMap.filter(function (data) {
                        return data.countryId == val;
                    })[0];
                    $scope.stateMap = typeof filtered !== 'undefined' ? filtered.states : filtered;
                };

                $scope.stateChange = function(val){
                    var cityArray = $scope.$eval('(' + $scope.cityStateMap + ')');
                    var filtered = cityArray.cityStateMap.filter(function (data) {
                        return data.stateId == val;
                    })[0];
                    $scope.cityMap = typeof filtered !== 'undefined' ? filtered.cities : filtered;
                }

            }
        };
    });
The final and V of MVC of AngularJS is the html file. here is cascading.html file-
<div>
    Country:
    <select id="country" data-ng-Model="country"  ng-change="countryChange(country)"
            data-ng-options="country.id as country.label for country in countries">
        <option value=''>Select</option></select>
</div>
<div>
    States: <select id="state" data-ng-disabled="!stateMap" ng-change="stateChange(state)"
                    data-ng-model="state"
                    data-ng-options="state.id as state.label for state in stateMap">
    <option value=''>Select</option></select>
</div>
<div>
    City: <select id="city" data-ng-disabled="!cityMap" data-ng-model="city"
                    data-ng-options="city.id as city.label for city in cityMap">
    <option value=''>Select</option></select>
</div>

So this is the complete demonstration of three select lists with cascading behavior implemented in angularJS custom directive approach. Place these files in one folder and open index.html. It should work well. Please feel free to add comments below if you have any questions concern or you think the approach can be better. Happy Coding...