Search code examples
angularjsng-options

What would be angular ng-option equivalent of this select?


I am struggling to get this into an ng-option. Is it even possible?

<select ng-model="detail_type_id">
   <optgroup ng-repeat="type in data.detailTypes" label="{{type.name}}">
      <option ng-repeat="t in type.children" value="{{t.id}}">{{t.name}}</option>
   </optgroup>
</select>

DetailTypes looks like this:

[
 {"id":7, 
  "parent_id":null, 
  "name":"Contact", 
  "children":[
     {"id":8,
      "parent_id":7,
      "name":"Address",
      "children":[]
     },
     {"id":12,
      "parent_id":7,
      "name":"Something else",
      "children":[]
     }
   ]},
 {"id":16,
  "parent_id":null,
  "name":"Other",
  "children":[
     {"id":10,
      "parent_id":16,
      "name":"Remarks",
      "children":[]}
   ]
 }
]

Child id needs to be selected. Nesting cannot be deeper.


Solution

  • The ngOptions directive does not work with multidimensional objects. So you need to flatten your array to use it.

    I wrote a filter for that:

    app.filter('flatten' , function(){
      return function(array){
        return array.reduce(function(flatten, group){
          group.children.forEach(function(child){
            child.groupName = group.name;
            flatten.push(child)
          })
          return flatten;
        },[]);
      }
    })
    

    And the HTML part would be like this:

    <select ng-model="detail_type_id"
            ng-options="item.id as item.name 
                        group by item.groupName for item 
                        in data.detailTypes | flatten track by item.id">
    </select>
    

    Plunker (version #1 with filter): https://plnkr.co/edit/dxi7j8oxInv2VRJ1aL7F

    I also modified your object to be like this:

    [{
      "id": 7,
      "parent_id": null,
      "name": "Contact",
      "children": [{
        "id": 8,
        "parent_id": 7,
        "name": "Address",
        "children": []
      }, {
        "id": 12,
        "parent_id": 7,
        "name": "Something else",
        "children": []
      }]
    }, {
      "id": 16,
      "parent_id": null,
      "name": "Other",
      "children": [{
        "id": 10,
        "parent_id": 16,
        "name": "Remarks",
        "children": []
      }]
    }]
    

    EDIT:

    After suggestion I wrote another version without the filter, but flattening the array inside the controller.

    Additional Controller JS:

    $scope.flattenDetailTypes = flattenDetailTypes($scope.data.detailTypes);
    
      function flattenDetailTypes(array){
        return array.reduce(function(flatten, group){
          group.children.forEach(function(child){
            child.groupName = group.name;
            flatten.push(child)
          })
          return flatten;
        },[]);
      }
    

    Markup:

    <select ng-model="detail_type_id"
            ng-options="item.id as item.name group by item.groupName for item in flattenDetailTypes track by item.id"></select>
    

    Plunker (version #2 without filter): https://plnkr.co/edit/D4APZ6