Ответ 1
Основываясь на вашем коде JSFiddle, я думаю, что я работал так, как вы хотите:
var app = angular.module('myApp', []);
app.controller('BoxController', ['$scope', 'BoxService', function($scope, BoxService) {
$scope.currentBox = {};
$scope.currentSelection = [];
$scope.currentOptions = [];
$scope.defaultOptions = [{
"id": 1,
"type": "black"
}, {
"id": 8,
"type": "red"
}, {
"id": 15,
"type": "green"
}, {
"id": 10,
"type": "yellow"
}, {
"id": 3,
"type": "blue"
}];
// This object maps each box ID to its length. For example,
// `boxLengths['1'] = 2` means that box with ID '1' contains 2 boxes.
$scope.boxLengths = {};
$scope.setCurrentBox = function(id) {
BoxService.getBoxItem(id, function(box) {
$scope.currentBox = box;
// Convert the box from a tree structure into a flat array `data`
BoxService.getBoxesAsTab(box, function(data) {
$scope.currentSelection = data;
$scope.currentOptions = [];
// We now know the current box contains `data.length - 1` boxes
// (subtract 1 so we don't count the first box in the `data` array)
$scope.boxLengths[id] = data.length - 1;
angular.forEach(data, function(item, index) {
BoxService.getBoxOptions(item.type, function(options) {
$scope.currentOptions[index] = options;
});
});
});
});
};
// This gets called whenever a `<select>` box changes value
$scope.updateSelection = function(index, choiceId) {
// Truncate the arrays down to the element at the specified `index`
// http://stackoverflow.com/a/6928247/5249519
$scope.currentSelection.length = index + 1;
$scope.currentOptions.length = index + 1;
// If the user selects "NO CHOICE", then `choiceId` will be `null`
if (choiceId === null) {
// Update the number of boxes that the current box contains
// (subtract 1 so we don't count the first box in the array).
// NOTE: If the user selects "NO CHOICE" for the 1st choice,
// then `$scope.currentBox.id` would be `null` at this point,
// but I'm not sure what you want to do in that case...
$scope.boxLengths[$scope.currentBox.id] = $scope.currentSelection.length - 1;
// Update the appropriate object reference in the chain
if (index === -1) {
$scope.currentBox = null;
} else {
$scope.currentSelection[index].box = null;
}
// Stop here and return
return;
}
// Otherwise, create the next item in the chain
var nextItem = {
id: choiceId,
type: '',
box: null
};
// Given the `id`, find the corresponding `type` name in the `defaultOptions` array
for (var i = 0; i < $scope.defaultOptions.length; i++) {
if ($scope.defaultOptions[i].id === nextItem.id) {
nextItem.type = $scope.defaultOptions[i].type;
break;
}
}
// Update the appropriate object reference in the chain
if (index === -1) {
$scope.currentBox = nextItem;
} else {
$scope.currentSelection[index].box = nextItem;
}
// Add the `nextItem` to the `currentSelection` array
$scope.currentSelection.push(nextItem);
// Get the options for the `nextItem` and add them to the `currentOptions` array
BoxService.getBoxOptions(nextItem.type, function(options) {
$scope.currentOptions.push(options);
});
// Update the number of boxes that the current box contains
// (subtract 1 so we don't count the first box in the array)
$scope.boxLengths[$scope.currentBox.id] = $scope.currentSelection.length - 1;
};
}]);
app.directive('editForm', function() {
return {
restrict: 'E',
template:
'1st choice : ' +
'<select ng-model="currentBox.id" ' +
' ng-options="obj.id as obj.type for obj in defaultOptions" ' +
' ng-change="updateSelection(-1, currentBox.id)"> ' +
' <option value="">NO CHOICE</option> ' +
'</select> ' +
'<div class="editor" ng-repeat="item in currentSelection"> ' +
' <br/><br/>Choice {{$index}} : ' +
' <div> Id : <label>{{item.id}}</label></div> ' +
' <div> Type : <label>{{item.type}}</label></div> ' +
' <div class="boxes" style="border:1px solid red;"> ' +
' Box : ' +
' <select ng-model="item.box.id" ' +
' ng-options="obj.id as obj.type for obj in currentOptions[$index]"' +
' ng-change="updateSelection($index, item.box.id)"> ' +
' <option value="">NO CHOICE</option> ' +
' </select> ' +
' </div> ' +
'</div> '
};
});
//This is the http service supposed to retrieve boxes data. HARDCODED for the example
app.factory('BoxService', ['$http', function($http) {
return {
getBoxItem: function(id, callback) {
callback({
"id": 1,
"type": "black",
"box": {
"id": 8,
"type": "red",
"box": {
"id": 15,
"type": "green",
"box": null
}
}
});
},
getBoxesAsTab: function(box, callback) {
var boxesArray = [];
var currentBox = box;
while (currentBox) {
boxesArray.push(currentBox);
currentBox = currentBox.box;
}
callback(boxesArray);
},
getBoxOptions: function(type, callback) {
if (type === 'black') {
callback([{
'id': 8,
'type': 'red'
}, {
'id': 3,
'type': 'blue'
}, {
'id': 15,
'type': 'green'
}]);
} else if (type === 'red') {
callback([{
'id': 15,
'type': 'green'
}, {
'id': 10,
'type': 'yellow'
}]);
} else if (type === 'blue') {
callback([{
'id': 1,
'type': 'black'
}, {
'id': 8,
'type': 'red'
}, {
'id': 15,
'type': 'green'
}, {
'id': 10,
'type': 'yellow'
}, {
'id': 3,
'type': 'blue'
}]);
} else if (type === 'green') {
callback([{
'id': 1,
'type': 'black'
}, {
'id': 8,
'type': 'red'
}, {
'id': 15,
'type': 'green'
}, {
'id': 10,
'type': 'yellow'
}, {
'id': 3,
'type': 'blue'
}]);
} else {
callback([]);
}
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="BoxController">
<p>Click on the table row (green line) to set "pre-defined" (hardcoded) data</p>
<table class='table'>
<thead>
<tr style="border:1px solid black;">
<td style="border:1px solid black;">id</td>
<td style="border:1px solid black;">type</td>
<td style="border:1px solid black;">contains</td>
</tr>
</thead>
<tbody>
<tr ng-click="setCurrentBox('1');" style="background-color:lightgreen;">
<td>1</td>
<td>Black</td>
<td ng-bind="boxLengths['1']"></td>
</tr>
</tbody>
</table>
<edit-form></edit-form>
<br/>
<br/>
<br/> CURRENT BOX : {{currentBox}}
<br/> CURRENT SELECTION : {{currentSelection}}
<br/> CURRENT OPTIONS : {{currentOptions}}
</div>