A dynamic creation of chained select-boxes has been implemented based on JSON data fetched from the server. In this cascading process, each select-box is an object with specific properties:
- Parent Attribute: The name of the parent object for the current select-box.
Options: An array of option objects comprising: (a) Option Value (b) Parent Option Value - mapping the value of the parent select-box to the current one. (c) Option ID.
Selected Option: An object containing two properties: (a) Currently selected value (b) ID of the currently selected value.
The select-boxes are generated using ng-repeat in the "option" tag or ng-option in the "select" tag, followed by the application of a custom filter. This custom filter matches the parent option value (2 > b) of the option values (2 > a) with the "currently selected value" (3 > a) of its parent object, essentially establishing a many-to-one mapping from child option values to the selected parent value.
https://i.sstatic.net/ym2R2.jpg
While successful in mapping parent-child select-box relationships, an issue arises when changing the parent select-box value results in the child object's "selected option value" not updating (failure to grab the first item in the filtered list, causing the grandchild dropdown to remain unchanged)[1].
Is there a way to initialize the child select-box and subsequent children/grandchildren with the first option value when the parent value changes instead of maintaining the blank value?
Here is the working example on Plunker (ng-repeat implementation). Any assistance would be highly appreciated.
Another version can be found on Plunker, demonstrating the ng-options implementation: Plunker Example.
HTML (ng-repeat):
<div ng-repeat="selection in vm.selectionData">
<div ng-repeat="(key, attribute) in selection.attributes">
<span>{{key}}</span>
<select class="form-control" ng-model="attribute.selectedOption.name">
<option ng-repeat="option in attribute.options | optionFilter : selection.attributes[attribute.parentAttr]">{{option.name}}</option>
</select>
</div>
</div>
HTML (ng-options):
<div ng-repeat="selection in vm.selectionData">
<div ng-repeat="(key, attribute) in selection.attributes">
<span>{{key}}</span>
<select ng-model="attribute.selectedOption" ng-options="attribute.name for attribute in (attribute.options | optionFilter : selection.attributes[attribute.parentAttr]) track by attribute.id">
</select>
</div>
</div>
JS:
myApp.filter('optionFilter', function() {
return function(items, parent) {
var result = [];
if (parent) {
for (var i = 0; i < items.length; i++) {
console.log(items[0].parent, parent.selectedOption.name);
if (items[i].parent === parent.selectedOption.name) {
result.push(items[i]);
}
}
return result;
} else {
return items;
}
}
});
myApp.controller("MyCtrl", function($scope) {
this.selectionData = [{
selectionType: "Geography",
attributes: {
country: {
parentAttr: "none",
options: [{
name: "India",
parent: "None",
id: 1
}, {
name: "Bangladesh",
parent: "None",
id: 2
}, {
name: "Afghanistan",
parent: "None",
id: 3
}],
selectedOption: {
name: "India",
id: 1
}
},
state: {
parentAttr: "country",
options: [{
name: "Rajasthan",
parent: "India",
id: 1
}, {
name: "Haryana",
parent: "India",
id: 2
}, {
name: "Dhaka",
parent: "Bangladesh",
id: 3
}, {
name: "Kabul",
parent: "Afghanistan",
id: 4
}],
selectedOption: {
name: "Rajasthan",
id: 1
}
},
city: {
parentAttr: "state",
options: [{
name: "Kota",
parent: "Rajasthan",
id: 1
}, {
name: "Sirsa",
parent: "Haryana",
id: 2
}, {
name: "Alwar",
parent: "Rajasthan",
id: 3
}, {
name: "Gurgaon",
parent: "Haryana",
id: 4
}, {
name: "Kabul",
parent: "Kabul",
id: 5
},{
name: "Dhaka",
parent: "Dhaka",
id: 6
}
],
selectedOption: {
name: "Kota",
id: 1
}
}
},
}];
});
References:
- Cascading select/dropdowns