Published: 2015-09-27

DropDown Select In Angular

Table of Contents

1 Summary

I am using angularjs as mobile web framework in my project, and when I use dropdown select control I find interesting things.

Enviroment : angularjs 1.4.6, odoo 8.0(server side), pycharm, chrome, ubuntu 14.04

2 Single dropdown select

Firstly, if I want to make a single dropdown select, I using as follows:

<select name="select1" ng-model="model1" ng-options="option1.id as option1.name for option1 in options1">
      <option value="">---Pick One---</option>
 </select>

In this case,options1 is a array containing objects, such as [{id:1,name:'apple'},{id:2,name:'pear'}]. When I choose one option1.name, the model1 is binding to option1.id. Also, the options1 can be a object, or option1.id as option1.name can write in other ways depend on needs, you can learn more refer to Angularjs doc.

Back to example, When I want to generate the options, data from server is like this: [{id:1,name:'apple'},{id:2,name:'pear'}]., of course remember dump to json. And when I console.info() in the browser, It adds $$hashkey to each object, the data becomes this: [{id:1,name:'apple',$$hashKey: "object:123"},{id:2,name:'pear',$$hashKey: "object:124"}],But $$hashkey doesn't affect others. If I want the options selected when the page loaded, I also afferent the id from server side, so I can set the model1 to id to make it selected.

When option is selected, just post the data of model1 to server,so the server knows which one is selected.

3 Multle-select dropdown select

Secondly, I want to multle-select dropdown select. that's simple, just add multiple"multiple"= to HTML conrol like following:

<select name="select2" ng-model="model2" ng-options="option2.id as option2.name for option2 in options2" multiple="multiple">
    <option value="">---Pick Many---</option>
</select>

From the browser side,simply,the Get data(from server) is like this format: [{id:1,name:'apple'},{id:2,name:'pear'},{id:3,name:'orange'}],(also,angular will add $$hashkey in each object) and if you want pre-selected, also set model2 to id list ,the format like this [1,3]. The Post data is like {ids:"[1,3]"}, rember to tranfer to json using: JSON.stringify() before post to server.

4 Cascading dropdown seletion

Finnally, I want make a cascading dropdown seletion, for example, I want to pick country, state, area in turn, here I make up two ways.

4.1 First solution:

<!-- HTML -->
<select ng-model="selectedCountry" ng-change="selectedUnit=''" ng-options="country as country.name for country in countries" >
    <option value="">---Pick Country---</option>
</select>
<select ng-model="selectedState" ng-change="selectedArea=''" ng-options="state as state.name for state in selectedCountry.state">
    <option value="">---Pick State---</option>
</select>
<select ng-model="selectedArea" ng-options="area as area.name for area in selectedState.area">
    <option value="">---Pick Area---</option>
</select>
#Get data from server like this
[
{id:1,name:"country1",state:[
    {id:101,name:"state1",area:[{id:10101,name:"area1"},{id:10102,name:"area2"},{id:10103,name:"area3"}]},
    {id:102,name:"state2",area:[{id:10201,name:"area4"},{id:10202,name:"area5"},{id:10203,name:"area6"}]},
    {id:103,name:"state3",area:[{id:10301,name:"area7"},{id:10302,name:"area8"},{id:10303,name:"area9"}]}
                            ]
},
{id:2,name:"country2",state:[
    {id:201,name:"state4",area:[{id:20101,name:"area11"},{id:20102,name:"area12"},{id:20103,name:"area13"}]},
    {id:202,name:"state5",area:[{id:20201,name:"area14"},{id:20202,name:"area15"},{id:20203,name:"area16"}]},
    {id:203,name:"state6",area:[{id:20301,name:"area17"},{id:20302,name:"area18"},{id:20303,name:"area19"}]}
                            ]
},
{id:3,name:"country3",state:[
    {id:301,name:"state7",area:[{id:30101,name:"area31"},{id:30102,name:"area32"},{id:30103,name:"area33"}]},
    {id:302,name:"state8",area:[{id:30201,name:"area34"},{id:30202,name:"area35"},{id:30203,name:"area36"}]},
    {id:303,name:"state9",area:[{id:30301,name:"area37"},{id:30302,name:"area38"},{id:30303,name:"area39"}]}
                            ]
}
]

#Also,when angularjs get this data(of json format) from server,it tanfer it, and add $$haskkey to each object,including object in object.

So,if i want to pre-select options, I need also get the country id,state id,and area id from the server, and in angularjs controller, use the given id to get object from transfered data(in order to get $$haskkey also, I didn't do this on server because in server data don't contain `$$hashkey`),so the three object may look like this:

//selectedCountry
{$$hashkey:"objectxx",id:2,name:"country2",state:[
    {$$hashkey:"objectxx",id:201,name:"state4",area:[{$$hashkey:"objectxx",id:20101,name:"area11"},{$$hashkey:"objectxx",id:20102,name:"area12"},{$$hashkey:"objectxx",id:20103,name:"area13"}]},
    {$$hashkey:"objectxx",id:202,name:"state5",area:[{$$hashkey:"objectxx",id:20201,name:"area14"},{$$hashkey:"objectxx",id:20202,name:"area15"},{$$hashkey:"objectxx",id:20203,name:"area16"}]},
    {$$hashkey:"objectxx",id:203,name:"state6",area:[{$$hashkey:"objectxx",id:20301,name:"area17"},{$$hashkey:"objectxx",id:20302,name:"area18"},{$$hashkey:"objectxx",id:20303,name:"area19"}]}
                            ]
}

//selectedState
{$$hashkey:"objectxx",id:202,name:"state5",area:[{$$hashkey:"objectxx",id:20201,name:"area14"},{$$hashkey:"objectxx",id:20202,name:"area15"},{$$hashkey:"objectxx",id:20203,name:"area16"}]}

//selectedArea
{$$hashkey:"objectxx",id:20202,name:"area15"}

In this case,it can work. If you set selectedCountry or selectedState or selectedArea without $$hashkey, It don't show pre-selected data I want.

The problem of this method is:when the data gets more, each time I load this page, the server will caculate to generate this format data(or you can store it depend on your need), and the client side will caculate also to get the hash key.It is slow and waste performance because in most case, the data is the same(also depend on your situation).

What is $$hashkey? I find this answer from stackoverflow: >Angular adds this to keep track of your changes, so it knows when it needs to update the DOM.If you use angular.toJson(obj) instead of JSON.stringify(obj) then Angular will strip out these internal-use values for you.

Also, if you change your repeat expression to use the track by {uniqueProperty} suffix, Angular won't have to add $$hashKey at all. For example

<ul>
    <li ng-repeat="link in navLinks track by link.href">
        <a ng-href="link.href">{{link.title}}</a>
    </li>
</ul>

Just always remember you need the "link." part of the expression - I always tend to forget that. Just track by href will surely not work.

Another helpful: >In my use case (feeding the resulting object to X2JS) the recommended approach

data = angular.toJson(source); help to remove the $$hashKey properties, but the result could then no longer be processed by X2JS.

data = angular.copy(source); removed the $$hashKey properties as well, but the result remained usable as a parameter for X2JS.

4.1.1 Problem left

Then I wonder if I remove $$hashkey all, would it work and be simpler for data processing?

4.2 Second Solution

However, I choose the second solution:

<!--HTML-->
<select ng-model="vm.newCountryId" ng-change="vm.getState()" ng-options="country.id as country.name for country in vm.countrys" >
    <option value="">--select country--</option>
</select>
<select ng-model="vm.newStateId" ng-change="vm.getArea()" ng-options="state.id as state.name for state in vm.states" >
    <option value="">--select state--</option>
</select>
<select ng-model="vm.newAreaId" ng-options="area.id as area.name for area in vm.areas" >
    <option value="">--select area--</option>
</select>

And function (also getCountry()),getState(),vm.getArea() I write in angular controller to get data list from server. So each time when i select a value, it trigger a funtion to get data from server.Also in the funtion I use the Id to point what data I want get from server.The code is some like this:

//  In controller
vm.getArea = function(){

            //Needs attention here
            if(vm.newStateId==null){
                vm.newAreaId=null;
                return;
            }

            dataService.getArea(vm.newStateId)
            .then(function (data)
            {
                vm.areas = data;
                $timeout(function () {
                }, 1000);
            },
            function (error) {
                toaster.pop('error', "errormsg", "errormsg...");
            });
        };
//process pre-select data from server. Just get 3 id from the server, and the get one by one from server
if(data['country_id']){
    vm.newCountryId = data['country_id'];
    vm.getState();
    if(data['state_id']){
        vm.newStateId = data['state_id'];
        vm.getArea();
        if(data['area_id']){
            vm.newAreaId = data['area_id'];
        }
    }
}

In this method, Post data is just 3 ids. This is simple, and speed fast comparing the former solution.And the data and logic is also simple and clear.

This is my first blog, maybe it looks verbose and not clearly, I am sorry for that, I will keep trying to make it better.:)

Author: Nisen

Email: imnisenATgmailDOTcom

Emacs 25.2.1 (Org mode 8.2.10)