Search code examples
javascriptangularjsangularjs-ng-repeat

Make a list with ng-repeat, separated by "custom categories" (AngularJS)


I need a create a list with values that people will enter in a list, and I need to display them separated by Category, only problem is that I don't know which Category they will use for each element, so categories are custom values. Here's a example of how I want to display the list:

Category 1:
- Element 1
- Element 2
- Element 3

Category 2:
- Element 1
- Element 2
- Element 3

Category invented by me:
- Element 1
- Element 2

I tried making an ng-repeat of the elements with a filter to avoid all the duplicated Categories. Then I show the title of the category and below the title another ng-repeat now to show the elements of that category but with an ng-if to filter only the ones that match the category of the previous ng-repeat:

<div ng-repeat="x in elements | unique:'Category'">
     <h2>{{x.Category}}</h2>
     <div class="element" ng-repeat="y in elements" ng-if="y.Category === {{x.Category}}">
        <p class="click-text">{{y.Title}}</p>
     </div>
</div>

I know this is a mess... I need help on searching an actual solution.


Here a example of the array of elements:

[{
  "Category": "Category 1",
  "Title": "Title example",
  "Comments": "Example comments"
},
{
  "Category": "Category 1",
  "Title": "My cat is named George",
  "Comments": "Example comments"
},
{
  "Category": "Category 1",
  "Title": "Hocus pokus",
  "Comments": "Example comments"
},
{
  "Category": "Category 2",
  "Title": "7 projects going LIVE now",
  "Comments": "Example comments"
},
{
  "Category": "Category 2",
  "Title": "Batman vs Superman was a good movie",
  "Comments": "Example comments"
},
{
  "Category": "Category 2",
  "Title": "projects (more)",
  "Comments": "Example comments"
},
{
  "Category": "Category invented by me",
  "Title": "Remember, remember the fifth of november",
  "Comments": "Hello there!"
},
{
  "Category": "Category invented by me",
  "Title": "It's night, electric night",
  "Comments": "General Kenobi"
}]

Solution

  • You can group elements by category in an object and iterate by object properties. See "Iterating over object properties" in the ngRepeat documentation.

    When you add a new element, you can add it to the object too (in the array next to the new element's category).

    You can see a working demo below (note how the mapping from categories to corresponding elements is created in the vm.init function):

    angular
      .module('elementsApp', [])
      .controller('elements', function() {
        var vm = this;
        vm.elements = [{
            "Category": "Category 1",
            "Title": "Title example",
            "Comments": "Example comments"
          },
          {
            "Category": "Category 1",
            "Title": "My cat is named George",
            "Comments": "Example comments"
          },
          {
            "Category": "Category 1",
            "Title": "Hocus pokus",
            "Comments": "Example comments"
          },
          {
            "Category": "Category 2",
            "Title": "7 projects going LIVE now",
            "Comments": "Example comments"
          },
          {
            "Category": "Category 2",
            "Title": "Batman vs Superman was a good movie",
            "Comments": "Example comments"
          },
          {
            "Category": "Category 2",
            "Title": "projects (more)",
            "Comments": "Example comments"
          },
          {
            "Category": "Category invented by me",
            "Title": "Remember, remember the fifth of november",
            "Comments": "Hello there!"
          },
          {
            "Category": "Category invented by me",
            "Title": "It's night, electric night",
            "Comments": "General Kenobi"
          }
        ];
        vm.newElement = {};
        vm.elementGroups = {};
    
        vm.addElement = function(e) {
          vm.elements.push(e);
          vm.elementGroups[e.Category] = vm.elementGroups[e.Category] || [];
          vm.elementGroups[e.Category].push(e);
          vm.newElement = {};
        };
    
        vm.init = function() {
          vm.elements.forEach(e => {
            vm.elementGroups[e.Category] = vm.elementGroups[e.Category] || [];
            vm.elementGroups[e.Category].push(e);
          });
        };
    
    
      });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
    
    <div ng-app="elementsApp">
      <div ng-controller="elements as ctrl" ng-init="ctrl.init()">
        <form ng-submit='ctrl.addElement(ctrl.newElement)'>
          <input ng-model="ctrl.newElement.Title" placeholder='Title' required>
          <input ng-model="ctrl.newElement.Category" placeholder='Category' required>
          <button type='submit'>Add</button>
        </form>
    
        <div ng-repeat="(category, elements) in ctrl.elementGroups">
          <h2>{{category}}</h2>
          <div ng-repeat="element in elements">
            {{element.Title}}
          </div>
        </div>
      </div>
    </div>