What is the best way to showcase a collapsible tree using AngularJS and Bootstrap?

I'm currently working on a web application that requires the display of a tree structure using lists. Here is the basic outline:

* Node 1
    * Node 1.1
        * Node 1.1.1
            * Node 1.1.1.1
        * Node 1.1.2
    * Node 1.2

http://jsfiddle.net/QffFm/1/

My goal is to find a solution in Angular or Bootstrap so that:

  • The list initially shows up to the third layer, displaying Node 1, Node 1.1, Node 1.1.1, Node 1.1.2, and Node 1.2 (excluding Node 1.1.1.1)
  • Clicking on the list-style icon toggles the node to collapse or expand
  • Preferably, I would like the icon to change based on whether the item is expanded - a right arrow for items with children, a down arrow for expanded items, and a regular list item for those without children

I am new to AngularJS and still learning Bootstrap. While Angular's accordion function doesn't meet all my requirements, I believe there must be existing solutions for this common issue. Any guidance on the best approach would be greatly appreciated before I implement complex logic into my application.

HTML code:

<div ng-app="myApp" ng-controller="controller">
    <my-directive></my-directive>
    <table style="width: 100%"><tbody><td>
        <tree items="tree"></tree>
    </td></tbody></table>
</div>

Angular code:

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

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

    $scope.tree=[{"name":"Node 1","items":[{"name":"Node 1.1","items":[{"name":"Node 1.1.1","items":[{"name":"Node 1.1.1.1","items":[]}]},{"name":"Node 1.1.2","items":[]}]},{"name":"Node 1.2","items":[]}]}];

});
app.directive('tree', function() {
    return {
        template: '<ul><tree-node ng-repeat="item in items"></tree-node></ul>',
        restrict: 'E',
        replace: true,
        scope: {
            items: '=items',
        }
    };
});

app.directive('treeNode', function($compile) {
    return { 
        restrict: 'E',
        template: '<li >{{item.name}}</li>',
        link: function(scope, elm, attrs) {
        if (scope.item.items.length > 0) {
            var children = $compile('<tree items="item.items"></tree>')(scope);
            elm.append(children);
        }
    }
    };
});

Answer №1

For the example below, I incorporated:

  • bootstrap
  • AngularJS recursive ng-include or (see second example) recursive directives
  • jQuery (will try to avoid in the future)

Demo 1 (ng-include) Plunker

From this model:

 $scope.displayTree =
            [{
            "name": "Root",
            "type_name": "Node",
            "show": true,
            "nodes": [{
                "name": "Loose",
                "group_name": "Node-1",
                "show": true,
                "nodes": [{
                    "name": "Node-1-1",
                    "device_name": "Node-1-1",
                    "show": true,
                    "nodes": []
                }, {
                    "name": "Node-1-2",
                    "device_name": "Node-1-2",
                    "show": true,
                    "nodes": []
                }, {
                    "name": "Node-1-3",
                    "device_name": "Node-1-3",
                    "show": true,
                    "nodes": []
                }]
            }, {
                "name": "God",
                "group_name": "Node-2",
                "show": true,
                "nodes": [{
                    "name": "Vadar",
                    "device_name": "Node-2-1",
                    "show": true,
                    "nodes": []
                }]
            }, {
                "name": "Borg",
                "group_name": "Node-3",
                "show": true,
                "nodes": []
            }, {
                "name": "Fess",
                "group_name": "Node-4",
                "show": true,
                "nodes": []
            }]
        }];
        [{
            "name": "Android",
            "type_name": "Android",
            "icon": "icon-android icon-3",
            "show": true,
            "nodes": []
        }];
    }

The 2nd example utilizes 2 directives:

app.directive('nodeTree', function() {
      return {
        template: '<node ng-repeat="node in tree"></node>',
        replace: true,
        transclude: true,
        restrict: 'E',
        scope: {
          tree: '=ngModel'
        }
      };
});

app.directive('node', function($compile) {
  return { 
    restrict: 'E',
    replace:true,
     templateUrl: 'the-tree.html',
    link: function(scope, elm, attrs) {
    
      // ....     
     
      if (scope.node.children.length > 0) {
        var childNode = $compile('<ul ><node-tree ng-model="node.children"></node-tree></ul>')(scope)
        elm.append(childNode);
      }
    }
  };
}); 

(Added some checkboxes as well :))

Demo 2 Plunker

How it looks:

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

Utilize the UseQuery graphql function in conjunction with the useState hook within useEffect, allowing for the execution of an additional useEffect to update additional state

In my NextJS project, I am utilizing Apollo for graphQL queries and encountering an issue with the stateData.allProducts section. Despite setting the state in the useEffect and including data as a dependency in the array, the error claims it is null when d ...

What causes the consistency in output when various encodings are applied in Node.js with fs.readFileSync()?

I've been puzzled by why using the readFileSync method with different encodings (such as utf-8, hex, ascii) always gives me the same output on the console. It's also strange that when I don't specify any encoding, I still get the output in u ...

What is the best way to place two stacked buttons side by side in an inline format?

I am trying to rearrange the layout of a bootstrap modal that includes two multiple select elements and two buttons. My goal is to have all controls inline, with the buttons stacked on top of each other. Here's an example of what I'm aiming for: ...

The Chrome extension I created using JQuery to trigger 'keyup' events is not updating the value of the 'input' field on a website that utilizes knockout JS

I am currently working on developing a Google Chrome extension for a website that appears to be utilizing 'knockout JS'. Let's take a look at the HTML element below: <input type="text" class="form-control text-right" id="amount" autoco ...

Tips for retrieving the posted object in angularJS

My current challenge involves creating an object with a defined name, posting it into a database, and then immediately finding its position and obtaining its id. However, I have noticed that using "get" right after "post" retrieves the data before it' ...

Highcharts is experiencing a duplication issue with the Y-Axis Series

This is a snippet from my code: $.get('https://dl.dropboxusercontent.com/u/75734877/data.csv', function (data) { var lines = data.split('\n'); $.each(lines, function (lineNo, line) { var items = line.split(',& ...

Creating JSON arrays for multiple elements using the same model name

I am using Angular to generate JSON data for multiple elements. Currently, the output looks like this: {"field":{"name":{"0":"a","1":"b"},"length":{"0":10,"1":20}}} However, I would like the JSON data to be more sophisticated and in array form, like this ...

Navigating the process of returning a list from an AJAX method to a view through a controller in ASP.NET MVC

I need to retrieve a list from my controller and pass it to the view. Currently, I have the following script: function fetchNames(_id, _name) { $.ajax({ type: 'Post', url: 'GetNames/', data: { id: _id, name: _name}, su ...

Creating an adjustable fixed footer using Bootstrap 3

Struggling with the application of bootstrap styling. I have a footer with columns set as col-md-3, 3, 2, 4 which sums up to a total of 12. The issue lies in the differing content within each column. My goal is to achieve equal spacing between each div in ...

Using JavaScript to consume ASP.net Script Services

My ASP.NET script service needs to be accessed from JavaScript using JSONP due to cross-domain restrictions. When dealing with a complex input structure, I have to construct the client-side input structure myself. How does the matching between the client ...

Using jQuery to combine a variable with the image source in order to replace the image attribute

I am trying to create a script that will display a greeting message to the user based on their local time. I found a script on Stack Overflow, but I am struggling with updating the image source. When I try to replace the image source, I encounter the follo ...

When utilizing Route.get() with an express app, an error occurred as a callback function was expected, but instead, an [

My health.js file contains the following code: var health = function(req, res, done) {}; health.prototype.endpoint = function(req, res, done) { let http_status = 200; console.log("--- Sending health endpoint status of %s", http_status); res.se ...

Angular- Product Details

I'm currently expanding my knowledge of angular (utilizing the ionic framework with phone gap included) and I'm working on developing a single application that displays a list of data. When a user clicks on an item in the list, I want to show the ...

Tips for displaying user-entered information from a form after submitting it with PHP

How can I display the email in the email text input field with this code? It doesn't seem to work correctly when there is a failed login attempt. value= "if(isset($_POST['submit'])){ ?PHP echo $_POST[''email']?>"; ...

Having difficulty with my async custom react hooks. Is there a way to wait for the result of one hook before using it in another hook?

Having some difficulty working with custom react hooks. I've created 2 custom hooks - one for fetching an ID and another for fetching a profile using the previously fetched ID. Since the second hook is dependent on the ID, I need to await the promise ...

Manage the border around the image by incorporating a timer countdown - starting from a complete circle, transitioning to a partial arc, and finally disappearing completely

My expertise lies in html, css, and angularjs for front-end development. I have an image that is initially surrounded by a thick border forming a full circle. As a countdown of one minute begins, I want the border to gradually disappear as time progresses. ...

Information does not seem to be linking properly in the angular dropdown menu

I am struggling with trying to create a dropdown using angularjs and bootstrap. I want the dropdown to open a link when any value is clicked, but I can't seem to get the data to populate in the dropdown list. If you can offer any help or advice, it wo ...

What is the best way to display the friends of the current user in a MeanJS application?

How can I show the Authenticated user's friends on an angularjs page? // Retrieve the list of Friends $scope.find = function() { $scope.friends = Authentication.user.friends; $scope.firstFriendName = Authentication.user.friends; ...

Progress Circles on Canvas in FireFox

Currently, I am experimenting with this codepen demo that utilizes canvas to generate progress circles: The functionality functions perfectly in Chrome and Safari; however, it only displays black circles in FireFox. My knowledge of canvas is limited, so I ...

How can I change the color of a designated column in a Google stacked bar chart by clicking a button?

I am in the process of creating a website that compares incoming students at my university across different academic years. We have successfully implemented a pie chart to display data for the selected year. Now, we aim to include a stacked bar chart next ...