Select2 with MetroUI CSS works well with AngularJS. But when I try using the same in dependent drop down lists I face the issue that view of child drop-down list doesn't get updated accordingly to change in parent drop-down list.
Here is a Stack Snippet demonstrating the issue I am seeing.
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
}
})();
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro-responsive.min.css" rel="stylesheet"/>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro.min.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/js/select2.min.js"></script>
<script src="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/js/metro.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
Steps to reproduce:
Hence my issue is that even though child select gets the correct options bound to it, it's option list view doesn't refreshes.
[I tried select2.val(") but still the option view of child dropdown did not refresh.]
Another issue is this: if I clear the selection in Parent select, it doesn't clear out the selection in Child select.
See this codepen.io to get the dependent drop-down list working.
Key points of solution are:
so it would be something like this in the ng-change event (childSelect2 is ID of child drop-down):
var ele = angular.element('#childSelect2');
ele.html("").val("").trigger("change");
Also note that on clicking the cross mark in parent drop-down, we are clearing the child drop-down as for any change we are calling ele.html("").val("").trigger("change");
and this is expected because if there is no selection in parent then it has no meaning to have selection in child. but with this we also need to clear out the ng-model of child drop-down so for that in ng-change function set the model of child drop-down to null (vm.selectedChild is ng-model for child drop-down)
vm.selectedChild = null;
though to have this any effect I found out that this setting of child model to null should be before calling ele.html("").val("").trigger("change");
hence I have made it the first statement in ng-change event. [I do not know why this placement-order is important, but it worked only after this change :)]
Hope the answer helps other and save their time.
quick reference: HTML Code:
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents" ng-change="vm.SetSelect2()">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}" id="childSelect2">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
JS Code (angular Module and controller):
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
vm.SetSelect2 = function () {
////debugger;
vm.selectedChild = null;
var ele = angular.element('#childSelect2');
//ele.html("");
//ele.val("").trigger("change");
ele.html("").val("").trigger("change");
}
}
})();