Bind ng-options to nested array within JSON object, within ng-repeat

3.6k views Asked by At

This is my first question, so please be gentle on me! I hope you can help.

I am trying to define a select box (dropdown) within my HTML view using ng-repeat, so that I can create a select for each item in an array. I have successfully achieved this, but my problems come when I try to get the options I need into each of the selects.

The array I am using{ "field1": 1, "field2": "Value01", "field3": "Value02", "refFields": [ { "fieldId": 100, "fieldValue": "fieldValueA" }, { "fieldId": 101, "fieldValue": "fieldValueB" } ] }, etc...

I need a select per item in the 'top level' of the array, and in each select box the values that should appear should be the fieldValue from the refFields nested array.

My current (broken) code products some pretty rubbish results. Here's the code:

<div ng-repeat="fk in fks">
        <ng-form name="fksForm">
            <select name="fkselect" ng-model="fk" ng-options="item as item.refFields for item in fks">                
            </select>
        </ng-form>
    </div>  

Could anybody provide me with some insight into whether this is possible? I don't think that flattening the data is an option for this particular application. I've tried quite a few different things including passing an index to the ng-options, which as you might expect produced some pretty dodgy results. I also tried this, which doesn't give me any useful output.

ng-options="item as item.refFields.fieldValue for item in fks"

Here's my JSFiddle: ng-option nested JSON Array within ng-repeat

Many thanks in advance for any suggestions!

2

There are 2 answers

1
AWolf On BEST ANSWER

With Underscore.js you could prepare your data for ng-options.

For me it looks pretty complicated. I would re-think if you could modify your data structure. I can't recommend a structure because I don't understand how the final result will look like.

I've created two arrays one with the field names that's used for the ng-repeat and the second array holds the refFields for the options.

With $index you can get the current index of the ng-repeat to access the refFields.

I'm not sure if I've created this correctly because I don't know what you're doing with the field1, field2, field3 values.

Please have a look at this jsfiddle or the demo below (same code).

var myApp = angular.module('myApp', []);

myApp.controller('DetailsCtrl', ['$scope', function($scope) {
    var data = [
  {
    "field1": 1,
    "field2": "Value01",
    "field3": "Value02",
    "refFields": [
      {
        "fieldId": 100,
        "fieldValue": "fieldValueA"
      },
      {
        "fieldId": 101,
        "fieldValue": "fieldValueB"
      }
    ]
  },
  {
    "field1": 2,
    "field2": "Value03",
    "field3": "Value04",
    "refFields": [
      {
        "fieldId": 102,
        "fieldValue": "fieldValueA"
      },
      {
        "fieldId": 103,
        "fieldValue": "fieldValueB"
      },
      {
        "fieldId": 104,
        "fieldValue": "fieldValueC"
      },
      {
        "fieldId": 105,
        "fieldValue": "fieldValueD"
      }
    ]
  },
  {
    "field1": 3,
    "field2": "Value05",
    "field3": "Value06",
    "refFields": [
      {
        "fieldId": 106,
        "fieldValue": "fieldValueA"
      },
      {
        "fieldId": 107,
        "fieldValue": "fieldValueB"
      }
    ]
  }
];
    $scope.fks = _.map(data, function(field) {
        return _.omit(field, ['field1', 'field2', 'field3']).refFields; // only refFields
    });
    console.log($scope.fks);
    $scope.fields = _.map(data, 
    function(field) {
        console.log(field);
        return [field.field1, field.field2, field.field3];
    }
    );
    //console.log(results)
    $scope.dropdown = {};
    
    $scope.changedValue = function(index) {
        console.log(index, $scope.dropdown);
        console.log($scope.dropdown[index]);
    }
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="DetailsCtrl">
<form novalidate class="form-vertical" ng-show="!loading" name="detailsForm" ng-controller="DetailsCtrl">
    <!--<pre>{{fks |json}}</pre>-->
      <div ng-repeat="fk in fields">
          <ng-form name="fksForm">
              <label>{{fk}}</label>
                <select name="fkselect" ng-model="dropdown[$index]" ng-options="item.fieldValue for item in fks[$index]" ng-change="changedValue($index)">
                </select>
            </ng-form>
        </div>  
</form>
</div>

0
monty On

Your data is fine, and there is a simpler way: make use of that fk and get the refFields directly from that, so ng-options="item as item.fieldValue for item in fk.refFields".

For the full code:

<div ng-app="myApp" ng-controller="myCtrl">
    <div ng-repeat="fk in fks">
        <label>{{fk.field2}}</label>
        <select ng-model="fk.selectedItem" ng-options="item as item.fieldValue for item in fk.refFields">              
        </select>      
    </div>  
</div>

And the data that you can just reference directly:

angular.module('myApp',[])
.controller('myCtrl',function($scope,$timeout) {
    $scope.fks = [
      {
        "field1": 1,
        "field2": "Value01",
        "field3": "Value02",
        "refFields": [
          { "fieldId": 100,  "fieldValue": "1fieldValueA" },
          { "fieldId": 101, "fieldValue": "1fieldValueB" }
        ]
      },
      {
        "field1": 2,
        "field2": "Value03",
        "field3": "Value04",
        "refFields": [
          { "fieldId": 102, "fieldValue": "2fieldValueA" },
          { "fieldId": 103, "fieldValue": "2fieldValueB" },
          { "fieldId": 104, "fieldValue": "2fieldValueC" },
          { "fieldId": 105, "fieldValue": "2fieldValueD" }
        ]
      },
      {
        "field1": 3,
        "field2": "Value05",
        "field3": "Value06",
        "refFields": [
          { "fieldId": 106, "fieldValue": "3fieldValueA" },
          { "fieldId": 107, "fieldValue": "3fieldValueB" }
        ]
      }
    ];
});

And, of course, the fiddle.

Now just don't ask me how to initialise the select of a particular item in such lists: That's what I was here looking for.