Cascading Parent Child Fields from Dynamic JSON Data

I created several chains with selected cells dynamically from the JSON data that I will receive from the server. Chaining / cascading works in such a way that each select-box is a named object with the following properties:

  • Parent attribute: name of the object that is the parent of this select-box object.
  • Parameters: an array of options objects, where each object contains: (a) the parameter value (b) the parent value of the parameter is the parent selection field with which the current value is mapped. (c) The identifier of the option.

  • Selected option: object with two properties: (a) selected current value (b) identifier of the currently selected value.

I create select-boxes using ng-repeat in the option tag, or using ng-option in the select tag, and then I use a custom filter where I filter the resulting parameters (2) by matching (2> b) the values options (2> a) with the "currently selected value" (3> a) of its parent. Basically does a multi-valued mapping from optional child values ​​to the selected parent value using a custom filter.

enter image description here

I can correctly display the parent-child selection fields, but the problem is that when I change the value of the parent selection, the value of the “selected parameter value” of its child object is not updated (the child selected item does not capture the first item in the filtered list, resulting in a dropdown the list of grandchildren is not updated.) [1]

Is there a way to change the parent value, is the child select block (and subsequent children / grandchildren) initialized with the value of the first option instead of the current empty value?

Here is a working plunker. (implementation of ng-repeat). We will be very grateful for any help.

Here is another plunk with ng-options implementation.

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: "Afganistan", 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: "Afganistan", 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 } } }, }]; }); 

Literature:

+5
source share
1 answer

After struggling for a while, I came up with a solution (although I'm not sure if it fits the best practices). In my custom filter, which returns filtered parameters based on the selected parameter value of the parent object, I additionally send the current object of the selection window. Then,

  • First, the filter checks if the parent exists or not, otherwise it returns all the parameters.
  • If the parent exists, it iterates over all available parameters of the current select-box object.
  • If the current value of the parent value of the select-box options matches the selected parameter of the parent object, then it pops the values ​​of the filtered parameters in the result array , only if the selected object of the parent object is not equal to zero (the option of the parent object can be empty if the grandparent value is changed, and the parent does not receive the selected filtering options, which leads to an empty first option in the parent select-box). This temporarily causes the current-select field to be completely empty, because the selected parent is null.
  • It then checks to see if the selected object is the current select-box null. If so, it assigns the first element (object) of the results array to the selected object of the object and returns an array of results. Since the filter will be launched for all selective boxes (grandparent, parent and child), it will set the first element in the parent filtered array as the selected parent. Now, when the selected parent is no longer zero, the current select-box will display the filtered parameters (from the result array), and the first element of the result array will be assigned to the selected option of the current selection block.

Working demo here. Please share if there is a better solution. Below is a custom filter:

 myApp.filter('optionFilter', function() { return function(items, parent, self) { var result = []; if (parent) { for (var i = 0; i < items.length; i++) { if (parent.selectedOption !== null && items[i].parentValue === parent.selectedOption.value) { result.push(items[i]); } } if (self.selectedOption === null) { self.selectedOption = result[0]; } return result; } else { return items; } } }); 

HTML:

 <div ng-repeat="(key, item) in data"> <span>{{key}}</span> <select ng-model="item.selectedOption" ng-options="option.value for option in (item.availableOptions | optionFilter : data[item.parent] : item) track by option.id"> </select> </div> 

Data:

 this.data = { Country: { parent: "None", availableOptions: [{ value: "United States", parentValue: "None", id: 1 }, { value: "China", parentValue: "None", id: 2 }, { value: "India", parentValue: "None", id: 3 }], selectedOption: { value: "United States", parentValue: "None", id: 1 } }, State: { parent: "Country", availableOptions: [{ value: "California", parentValue: "United States", id: 1 }, { value: "Shanghai", parentValue: "China", id: 2 }, { value: "Delhi", parentValue: "India", id: 3 }], selectedOption: { value: "California", parentValue: "United States", id: 1 } }, City: { parent: "State", availableOptions: [{ value: "Greenfield", parentValue: "California", id: 1 }, { value: "Shanghai", parentValue: "Shanghai", id: 2 }, { value: "New Delhi", parentValue: "Delhi", id: 3 }], selectedOption: { value: "Greenfield", parentValue: "California", id: 1 } } }; 
+2
source

All Articles