Search code examples
angularjsrecursionangularjs-ng-repeatangularjs-material

md-select with recursive options


I am trying to iterate through an array of categories with subcategories.

The problem. I have this array of categories:

$scope.categories = [{
  "_id": 1,
  "name": "Cat 1",
  "categories": {
    "_id": 11,
    "name": "Cat 11",
    "categories": {
      "_id": 111,
      "name": "Cat 111",
      "categories": null
    }
  }
}, {
  "_id": 2,
  "name": "Cat 2",
  "categories": null
}, {
  "_id": 3,
  "name": "Cat 3",
  "categories": null
}];

As you can see, is an array of objects with subcategories, so I know that I need a recursive solution.

I need to show all the categories into a md-select (it is not necesary group it, but it will be great) and I am trying this:

<md-input-container class="md-block">
  <label>Categories</label>
  <md-select ng-model="selectedCategory">
    <md-option ng-value="category._id" ng-repeat-start="category in categories">{{category.name}}</md-option>
    <span ng-repeat-end ng-include="'subcategories'" ng-if="category.categories"></span>
  </md-select>
</md-input-container>
<script type="text/ng-template" id="subcategories">
{{category.name}}
<md-option ng-value="category._id" ng-repeat-start="category in category.categories">{{category.name}}</md-option>
<span ng-repeat-end ng-include="'subcategories'" ng-if="category.categories"></span>

It works partially, but it is not the result expected.

What I want? Something like this

enter image description here

What I have? This code

Tell me if need more details.

Thanks


Solution

  • You can simply flatten your list and style elements by their nesting level.

    angular
      .module('app', ['ngMaterial'])
      .controller('AppController', function($scope) {
    
        $scope.categories = [{
          "_id": 1,
          "name": "Cat 1",
          "categories": [{
            "_id": 11,
            "name": "Cat 11",
            "categories": [{
              "_id": 111,
              "name": "Cat 111",
              "categories": null
            }]
          }]
        }, {
          "_id": 2,
          "name": "Cat 2",
          "categories": null
        }, {
          "_id": 3,
          "name": "Cat 3",
          "categories": null
        }];
    
        $scope.flattenCategories = flatten($scope.categories, 0);
    
        function flatten(categories, level) {
          var flat = [];
          for (var i = 0, n = categories.length, category; category = categories[i]; i++) {
            flat.push({
              _id: category._id,
              name: category.name,
              level: level
            });
            if (category.categories) {
              flat = flat.concat(flatten(category.categories, level + 1));
            }
          }
    
          return flat;
        }
    
      });
    .subcategory-0 .md-text {
      margin-left: 0;
    }
    
    .subcategory-1 .md-text {
      margin-left: 8px;
    }
    
    .subcategory-2 .md-text {
      margin-left: 16px;
    }
    
    .subcategory-3 .md-text {
      margin-left: 24px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular-animate.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular-aria.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular-messages.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.4/angular-material.min.js"></script>
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
    
    <div ng-app="app" ng-controller="AppController">
      <form name="myForm">
        <md-input-container class="md-block">
          <label>Categories</label>
          <md-select ng-model="selectedCategory" name="myCategory">
            <md-option ng-class="'subcategory-' + category.level" ng-value="category._id" ng-repeat="category in flattenCategories track by category._id">{{ category.name }}</md-option>
          </md-select>
          <!-- <pre ng-bind="flattenCategories | json"></pre> -->
        </md-input-container>
      </form>
    </div>