Подтвердить что ты не робот

Форма динамического редактирования для своего контейнера (ов) российских кукол с помощью AngularJS

Вот проблема,

Мне действительно нужно управлять объектами, которые могут содержать другие объекты, определенные в db. Так, например, у меня есть 5 видов ящиков. Красная коробка, зеленый ящик, синяя коробка, желтая коробка и черный ящик.

Каждый ящик может содержать одно поле, которое также может содержать поле и т.д.

То, что я получаю, является таким видом объекта:

{
    "id":1,
    "type":"black",
    "box":
    {
        "id":8,
        "type":"red",
        "box":
        {
            "id":15,
            "type":"green",
            "box":null
        }
    }
}

Итак, этот пример: черный ящик, содержащий красный ящик, содержащий пустой зеленый квадрат. (черный → красный → зеленый → пустой)

Существуют условия:

  • Черный ящик может содержать только синий, зеленый и красный,
  • Красная коробка может содержать только зеленый и желтый,
  • Желтый ящик не может содержать ничего,
  • Другие поля (зеленый и синий) могут содержать что угодно

Что мне нужно сделать, это какой-то "редактор набора ящиков", я получаю объект box, который является сложным или нет (это означает, что он может иметь только один уровень ящика или несколько). Я должен представлять его в списке ящиков выбора, поэтому в примере, который я написал, он будет показывать это:

<select name="LEVEL_1">
        <option value="0">NONE</option>
        <option selected value="1">black</option>
        <option value="8">red</option>
        <option value="15">green</option>
        <option value="3">blue</option>
        <option value="10">yellow</option>
    </select>
<br/>
    <select name="LEVEL_2">
        <option value="0">NONE</option>
        <option selected value="8">red</option>
        <option value="15">green</option>
        <option value="3">blue</option>
    </select>
<br/>
    <select name="LEVEL_3">
        <option value="0">NONE</option>
        <option selected value="15">green</option>
        <option value="10">yellow</option>
    </select>
<br/>
    <select name="LEVEL_4">
        <option selected value="0">NONE</option>
        <option value="15">green</option>
        <option value="8">red</option>
        <option value="3">blue</option>
        <option value="10">yellow</option>
        <option value="1">black</option>
    </select>
4b9b3361

Ответ 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>

Ответ 2

Попробуйте этот пример: http://jsfiddle.net/kevalbhatt18/0js7q638/1/

Используя функцию checkInnerObject, она вернет счетчик "окна" в примере

function checkInnerObject(obj) {
    var i = 0;
    var arg = Array.prototype.slice.call(arguments, 1);
    start: while (obj) {
        if (obj.hasOwnProperty(arg)) {
            obj = obj[arg];
            i = i + 1;
            continue start;
        }
     }
        return i - 1;
}

checkInnerObject(OBJECT,'key you want to find');

UPDATE:


Eample: http://jsfiddle.net/kevalbhatt18/0js7q638/5/

Ответ 3

Ваш вопрос довольно длинный, поэтому простите меня, если мой ответ ниже не удовлетворяет всем вашим условиям. Говоря это, я думаю, что если вам не нужно создавать такой объект (русская кукла), нам не придется много волноваться.

Сделайте это: -

var reallyLengthyBoxObj = {
"id":"1",
"type":"black",
"box":{
    "id":"8",
    "type":"red",
    "box":{
        "id":"15",
        "type":"green",
        "box":{
            "id":"15",
            "type":"green",
            "box":{
                "id":"15",
                "type":"green",
                "box":null
            }
        }
    }
}
}

$scope.boxObjArr = [],
    $scope.selectedBoxes = {};
    i = 0;
function recurseMe(boxObj){
   i++;
   $scope.selectedBoxes["level"+i] = null;
   var  obj = {};
   obj.id = boxObj.id;
   obj.type = boxObj.type;
   obj.level = i;
   try{
      var haskeys = Object.keys(boxObj.box);
      obj.isParent = true;
      $scope.boxObjArr.push(obj);
      recurseMe(boxObj.box);
   }catch(e){
      obj.isParent = false;
      $scope.boxObjArr.push(obj);
      return;
   }
}

recurseMe(reallyLengthyBoxObj);

Таким образом вы получите массив со всеми полями и их уровнями. Теперь я предполагаю, что дублирующие идентификаторы не будут (и не должны появляться) с вашего сервера. Иначе наша логика будет расти.

Теперь у вас есть 2 вещи - $scope.boxObjArr и $scope.selectedBoxes.

напишите это в html:

<div ng-repeat="(key,value) in selectedBoxes">
   <select ng-model="value" ng-if="key=='level1' || selectedBoxes[key.slice(0,key.length-1)+(key.slice(-1)-1)] != null">
     <option ng-repeat="box in boxObjArr" ng-show="key=="level1" || box.level < selectedBoxes[key.slice(0,key.length-1)+(key.slice(-1)-1)].level">
     </option>
   </select>
</div>

Часть javascript выполнена и работает. Не уверен, что я допустил ошибку в части HTML. Но я думаю, что у вас есть идея о том, как и зачем формировать $scope.selectedBoxes и $scope.boxObjArr.

Надеюсь, что он решает вашу проблему как можно короче.

Спасибо