icebreaker icebreaker - 1 year ago 54
AngularJS Question

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.

Answer Source

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