Rendering tree data in Angular JS by level is a great way to organize and

Being new to AngularJS, I find the task of grouping data by tree level and tree id quite challenging. I would appreciate some assistance or guidance on this. What I aim to accomplish is grouping the data in a tree-like structure based on their level and id.

Here is my current HTML code:

<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

<body>

  <div ng-app="myApp" ng-controller="myCtrl">
      <ul>
          <li ng-repeat="item in list">
              [[ item.code ]]

          </li>
      </ul>
  </div>

</body>

</html>

And here is my JavaScript code:

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

app.controller('myCtrl', function($scope) {

    $scope.list = [
        {
            "id": 1,
            "code": "TANK",
            "tree_id": 1,
            "level": 0,
            "parent": null
        },
        {
            "id": 2,
            "code": "OIL",
            "tree_id": 1,
            "level": 1,
            "parent": 1
        },
        {
            "id": 3,
            "code": "CHEM",
            "tree_id": 1,
            "level": 1,
            "parent": 1
        },
        {
            "id": 6,
            "code": "PROD",
            "tree_id": 1,
            "level": 2,
            "parent": 3
        },
        {
            "id": 4,
            "code": "BULK",
            "tree_id": 2,
            "level": 0,
            "parent": null
        },
        {
            "id": 5,
            "code": "LOG",
            "tree_id": 2,
            "level": 1,
            "parent": 4
        }
    ],
});

Upon reviewing the $scope.list, you will notice the tree_id, level, and parent values. My goal is to group the objects by tree id, where the top level (level 0) will be the visible section and the lower levels (level 1 and up) will be collapsible content. The tree id acts as a grouping mechanism, the level determines the hierarchy, and the parent integer value defines the root of the hierarchy. It's important to note that this structure can have multiple branches and be flexible and unlimited in its depth.

The desired visual representation should resemble the following:

  • TANK
    • CHEM
    • OIL
      • PROD
  • BULK
    • LOG

Answer №1

This topic has been addressed on StackOverflow. For more information, you can refer to the following link:

How can I group data with an Angular filter?

By utilizing the groupBy filter, you can create a tree structure.

Answer №2

You also have the option to utilize custom directives for managing nested levels:

UPDATE to include properties réf in directives and adjust your new array structure

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

app.directive('collection', function () {
return {
restrict : "E",
replace : true,
        transclude: true,
scope : {
collection : '=',
list : '=',
coldisplay : '=',
colfrom : '=',
colto : '='
},
template : "<ul><member ng-repeat='member in collection track by $index' member='member' list='list' coldisplay='coldisplay' colfrom='colfrom' colto='colto'></member></ul>"
};
});

app.directive('member', function ($compile) {
return {
restrict : "E",
replace : true,
        transclude: true,
scope : {
member : '=',
list : '=',
coldisplay : '=',
colfrom : '=',
colto : '='
},
template : "<li>{{member[coldisplay]}}</li>",
link : function (scope, element, attrs) {
scope.children = [];
angular.forEach(scope.list, function (value, key) {
if (value[scope.colfrom] === scope.member[scope.colto]) {
this.push(value);
}
}, scope.children);

if (scope.children.length > 0) {
element.append("<collection collection='children' list='list' coldisplay='coldisplay' colfrom='colfrom' colto='colto'></collection>");
$compile(element.contents())(scope);
}
}
}
});

app.controller('myCtrl', function ($scope) {

$scope.list = [{
"id" : 1,
"code" : "TANK",
"tree_id" : 1,
"level" : 0,
"parent" : null
}, {
"id" : 2,
"code" : "OIL",
"tree_id" : 1,
"level" : 1,
"parent" : 1
}, {
"id" : 3,
"code" : "CHEM",
"tree_id" : 1,
"level" : 1,
"parent" : 1
}, {
"id" : 6,
"code" : "PROD",
"tree_id" : 1,
"level" : 2,
"parent" : 3
}, {
"id" : 4,
"code" : "BULK",
"tree_id" : 2,
"level" : 0,
"parent" : null
}
];

$scope.rootList = [];

angular.forEach($scope.list, function (value, key) {
if (value.parent == null) {
this.push(value);
}
}, $scope.rootList);

})
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

<body>
  <div ng-app="myApp" ng-controller="myCtrl">
  <collection collection='rootList' list='list' coldisplay='"code"' colfrom='"parent"' colto='"id"'></collection>
  </div>
</body>
</html>

Inspired by

Answer №3

In Angular, the groupBy function works for objects and not arrays. Therefore, you will need to generate the desired functionality in your controller.

If you are interested, I can provide a step-by-step process for achieving the desired outcome.

Once you retrieve your list from the API in your controller, you can call a function to manipulate it.

$scope.newList = {parents:[],childs:[]};

var generateNav = function(parent) {
  $scope.newList.parents.push(parent);
  angular.forEach($scope.list, function(item) {
    if(item.tree_id === parent.tree_id) {
      $scope.newList.childs.push(item);
    }
  })
};

var generateNewList = function(list) {
  angular.forEach(list, function(item) {
    if(item.level === 0) {
      generateNav(item);
    }
    });
};

// Call this function after retrieving data from the API
generateNewList($scope.list);

In your HTML, use newList to display the data.

<ul>
  <li ng-repeat="item in newList.parents">
    {{ item.code }}
    <ul>
      <li ng-repeat="child in newList.childs" ng-if="child.tree_id === item.tree_id && item.id!==child.id">{{child.code}}</li>
    </ul>
  </li>
</ul>

For a demonstration, you can visit the PLUNKER DEMO LINK.

Answer №4

To address the issue at hand, I recommend implementing a few modifications. Firstly, given that the data is sourced from an API, it's crucial to adjust the format for a more coherent structure. Consider structuring it as follows:

[{
  id: 1,
  code: "TANK",
  tree_id: 1,
  parent: null,
  children: [{
    id: 3,
    code: "CHEM",
    tree_id: 1,
    parent: 1,
    children: [{
        id: 9,
        code: "PROD",
        tree_id: 1,
        parent: 3
    }]
  }, {
    id: 2,
    code: "OIL",
    tree_id: 1,
    parent: 1,
  }]
}, {
  id: 4,
  code: "BULK",
  tree_id: 2,
  parent: null,
  children: [{
    id: 5,
    code: "LOG",
    tree_id: 2,
    parent: 4,
  }]
}]

To achieve this, we need to define the following function:

var createObject = function(json) {
    return {
        id: json.id,
        code: json.code,
        tree_id: json.tree_id,
        parent: json.parent,
        children: []
    }
};

var processList = function(temporaryObj) {

    for (var i = 0; i < list.length; i++) {
        if (list[i].parent === temporaryObj.id) {
            temporaryObj.children.push(processList(createObject(list[i])));
        }
    }

    return temporaryObj;
};

var convertList = function(list) {

    var temp = [];
    for (var i = 0; i < list.length; i++) {

        if (list[i].level === 0) {
            temp.push(processList(createObject(list[i])));
        }

    }

    return temp;
};

var newList = convertList(list_from_api);

Subsequently, it's essential to develop a recursive directive to display the content on the UI:

app.directive("tree", ['$compile', '$templateCache', function($compile, $templateCache) {
  return {
    type: "E",
    scope: {
      list: "="
    },
    link: function(scope, element, attrs) {
      element.replaceWith( $compile( $templateCache.get('tree.html'))(scope));
    }
  }
}]);

The corresponding tree.html template file should look like this:

<ul ng-if="list.length > 0">
   <li ng-repeat="item in list">
      <span>{{ item.code }}</span>
      <tree list="item.children"></tree>
   </li>
</ul>

For further insights, you can check out this plunkr link: https://plnkr.co/edit/8TtgFfgUQd73Du47b7XB?p=preview

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Demonstrate a array of values at varying angles within a circle using the functionalities of HTML5 canvas

Looking to utilize HTML5 Canvas and Javascript for a project where I need to showcase various values (depicted by dots possibly) at different angles within a circle. For example, data could include: val 34% @ 0°, val 54% @ 12°, val 23% @ 70°, a ...

Include form data into an array of objects within an Angular data source

I am struggling to add the edited form data from edit-customers-dialog.ts into an array of objects in my datasource. The form.data.value is returning correctly, but I'm facing issues with inserting it properly into the array. As a beginner in Angular ...

How to show the current week using React Native

Looking to show the current week of the month in a specific format using react-native: (Week 2: 05.10 - 11.10) (for example, displaying week 2 of the current month) Any ideas on how to make this happen? I'm aware of packages like momentjs that can ...

A Vue computed property is returning the entire function instead of the expected value

One of my computed properties is set up like this: methods: { url_refresh: function (id) { return `${this.url_base}?start=${Date.now()}` } } However, when I attempt to print the value on mount: mounted() { console.log(this.url_refresh) ...

How can I pass arguments from a Python command line program (compiled to an EXE) to a JavaScript file?

As I work on developing a node program, I've come across certain abilities that Python possesses which JavaScript lacks, such as utilizing Python-specific modules. To bridge this gap, I made the decision to compile Python files into EXE and then invok ...

Creating an object in AngularJS by merging data from three separate API calls

I am looking to develop a Jenkins dashboard using AngularJS. I am considering combining data from three different API calls to create an object that can be used in the HTML file with ng-repeat, but I'm not sure if this is the best approach. The desir ...

Tips for capturing a screenshot of the ESRI Map using Angular

Is there a way to capture a screenshot of the Esri map in its current state on the UI and then convert it into a PDF for download using Angular? Below is my current .ts code, but I am open to any additional suggestions. esri-map.component.html <!-- Map ...

Angular's observable function is not providing a complete response

In my Angular component, I have a function that is called from a template. This function returns an Observable of type string, but unfortunately it only returns the `data` variable. How can I modify it to return `dateNew[0] + " de " + data + " de "+ dateNe ...

Refresh the page to change the section using vue.js

I am currently working on a website using Laravel and Vue.js. I require two separate sections for the site: Site: https://www.example.com Admin: https://www.example.com/admin Within the resource/js/app.js file, I have included the main components as fo ...

The function Router.use is looking for a middleware function, but instead received an object in node.js /

I encountered an issue while trying to setup routing in my application. Whenever I attempt to initialize a route using app.use() from my routes directory, I receive an error stating that Router.use() requires a middleware function but received an Object in ...

Material UI - Radio buttons do not properly reflect the current state of the value

I'm diving into the world of full stack development and I'm working on a project to enhance my understanding of frontend programming with React JS and Material UI. Specifically, I've created a Dialog component to send data to the backend, bu ...

Determining the Location of a Drag and Drop Item

I have been utilizing the code found at for implementing Drag & Drop functionality. My inquiry is: How can I retrieve the exact position (x,y) of a group once it has been dragged and dropped? ...

Module is absent in JavaScript but present in TypeScript

As I delve into coding a vscode extension by following a tutorial, I encountered an issue with importing in my server.ts file. The directory structure looks like this: ...

The callback functions, such as afterMove, are not being executed

This code snippet is copied from Owl Carousel's official website. I am having trouble getting the callback functions like afterMove to work. Can anyone help me figure out why the afterMove function is not being called? It seems that none of the callba ...

Tips for avoiding $state refresh in Components unaffected by changes to $state.var?

We are currently utilizing Angular-ui-router for managing the state of our application. One issue we are facing is that every component refreshes when there is a change in the $state, even if it's a variable that has been updated and is not used or d ...

Error occurring when attempting to pass messages within an iframe on a sandboxed page in a Chrome extension

Whenever I try to utilize my popup page for a chromium extension as a conduit for communication between the background page and a page shown within an iframe on the popup, I encounter the following error. I required a sandboxed environment to execute Vue.j ...

Adjust the color of the active link on the page using Angular and CSS

I have a project that I need to modify by adding a sub menu that appears on every page but is only coded once. My goal is to highlight the link for the current page, all within one HTML snippet. Although the list renders correctly, I'm struggling to g ...

Tips for creating a functional null option using the select ng-options feature

While there are a few questions and answers on this topic, I have yet to find a solution that works for my specific case. Imagine having an object like this: $scope.person = {name: 'Peter', category1: null, category2: null}; Another variable r ...

How can I pass a variable name as an argument in Vue?

I am exploring ways to create a method that can efficiently load and assign JSON files to dynamic variables. Despite my efforts, varA and varB are not being populated as expected: data() { return { varA: Array, varB: Array } }, ...

Obtain the string value for the template variable

Trying to display a string literal if element.elementId is null or undefined. <div>{{String(element.elementId)}}</div> Encountering an error: TableBasicExample.html:6 ERROR TypeError: _co.String is not a function at Object.eval [as updat ...