Top method for dynamically generating a recursive treeview from data fetched from an API

I am currently learning Angular 2 and working on creating an expandable tree-view that pulls data from a potentially large third-party API. The underlying structure of the API is structured like this:

- Home (id: 1053)
- - Rugby League (id: 1054)
- - - Super League (id: 1103)
- - - - Castleford Tigers (id: 1111)
- - - - Catalans Dragons (id: 1110)
- - - - Huddersfield Giants (id: 1116)
- - - - Hull FC (id: 1108)
- - - Championship (id: 1104)
- - - - Batley Bulldogs (id: 1120)
- - - - Bradford Bulls (id: 1118)
- - - - Dewsbury Rams (id: 1124)
- - - - Featherstone Rovers (id: 1121)
- - Football (id: 1056)
- - - Premier League (id: 1057)
- - - - AFC Bournemouth (id: 1059)
- - - - etc
- - - - etc

The API works by passing an ID and returning a JSON array containing only the children of that specific node. For example, if I call:

http://www.example.com/api/contentNodes/?parentId=1053
, it will return:

[
  {"Id":1054,"Name":"Rugby League","HasChildren":true},
  {"Id":1056,"Name":"Football","HasChildren":true}
]

(The value of HasChildren indicates whether the node has child nodes.)

Considering the dataset may grow in size, my goal is to fetch more data from the API progressively as users open different branches of the tree, rather than loading the entire dataset at once in the app.

I have developed an Angular 2 application which you can explore here: http://plnkr.co/edit/QQ1OKCbd4pDptpSVbWch?p=preview

The crucial component is located in the `app/content-list.component.ts' file:

import {Component, OnInit}  from 'angular2/core';
import {ContentNode}        from './content-node';
import {ContentService}     from './content.service';

@Component({
    selector: 'content-list',
    template: `
        <ol class="tree">
            <li *ngFor="#contentNode of contentNodes" class="tree__branch" [ngClass]="{'tree__branch--has-children': contentNode.HasChildren}">
                <a *ngIf="contentNode.HasChildren" (click)="toggleBranch(contentNode.Id)" class="toggle">+</a> {{ contentNode.Name }}
            </li>
        </ol>
        <div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
    `
})
export class ContentListComponent implements OnInit {

    constructor (private _contentService: ContentService) {}

    errorMessage: string;
    private _startNodeId: number = 1053;

    contentNodes: ContentNode[];

    ngOnInit() { 
        this.getContentNodes();
    }

    getContentNodes() {
        this._contentService.getContentNodes(this._startNodeId)
            .subscribe(
                contentNodes => this.contentNodes = contentNodes,
                error =>  this.errorMessage = <any>error
            );
    }

    toggleBranch(branchId:number){
        console.log('branchId: ' + branchId);
    }
}

In this code snippet, I am making a service call returning the JSON mentioned above with parentId set to 1053.

However, I am facing challenges in gradually loading the child nodes of the treeview when the user clicks the "+" button, into the nested HTML list (<ol>).

What would be the best strategy to elegantly achieve this?

My next step involves optimizing API calls made by the app, but for now, my main concern is getting the treeview functioning correctly.

I came across this recursive treeview example, but it seems a bit buggy with empty <ol></ol> elements rendered in the HTML when child nodes are absent. Additionally, the setup appears very hardcoded, and I am not confident enough to refactor it yet.

Thank you in advance.

Due to security reasons, I cannot allow public requests to access the API, making testing challenging on Plunkr using just a static, single-level JSON dataset for demonstration purposes.

Answer №1

Angular2 offers the capability to render directives recursively, simplifying the process of rendering tree structures. I've made some modifications to your Plunker to demonstrate this feature. While it may not be the perfect implementation, it does achieve the desired outcome :).

For instance:

@Component({
    selector: 'tree-view',
    template: `
        <div *ngFor="#dir of dirs">
            <tree-view [dirs]="dir.dirs"></tree-view>
        <div>
    `,
    directives: [TreeView]
})
export class TreeView {
    @Input() 
    private dirs: Array<Directory>;
}

I hope you find this helpful!

Cheers!

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

Prevent selection based on function in Angular

I'm attempting to prevent certain options from being selected based on a specific method. For instance, let's say I have four options: A B C D In my method (let's use "x" as an example): if(name == A) { disable the selection for option A. ...

What causes the issue of Angular 9 routing breaking upon page refresh?

I have deployed my Angular 9 application on Heroku and everything is working perfectly. However, when I refresh the page or copy/paste a link, I get an error message saying "Cannot GET /XXX/XXX". Only the root link seems to work! Initially, I can navigate ...

What could be causing the "no exported member" errors to appear when trying to update Angular?

The dilemma I'm facing a challenge while attempting to upgrade from Angular V9 to V11. Here are the errors that I am encountering: Namespace node_module/@angular/core/core has no exported member ɵɵFactoryDeclaration Namespace node_module/@angular/ ...

Upon updating my application from Angular 14 to 16, I encountered an overwhelming number of errors within the npm packages I had incorporated

After upgrading my angular application from v14 to v16, I encountered numerous peer dependencies issues, which led me to use the --force flag for the upgrade process. However, upon compiling, I am now faced with a multitude of errors as depicted in the scr ...

Unexpected Issue: Angular 12 Encounters JIT Compiler Unavailability

Lately, I've been encountering a persistent issue with an error message: Uncaught Error: JIT compiler unavailable. Ever since I upgraded from Angular version 8 to 12, whenever I run the ng build --prod --output-path = dist command and build Angular, e ...

Using Angular NgRx - triggering an action from an effect once certain actions yield a result

I'm encountering difficulties in dispatching actions that require results from five other actions (all listed in my effect). Could someone lend a hand? Essentially, I need to trigger an action within the effect only after these five actions have retu ...

Limiting the display of every item in Angular ngFor

I'm currently working with Angular and I have the following code in my component.html: <div class="card" *ngFor="let item of galleries;" (mouseenter)=" setBackground(item?.image?.fullpath)" (mouseover)="showCount ...

Exploring the similarities between using jQuery AJAX post and Angular

In my current project, I have a legacy application that relies on jQuery and unfortunately cannot incorporate Angular at this time. For one specific task, I need to make an AJAX POST request to consume a web service using jQuery. Interestingly, the same ...

Azure Static Web App does not retrieve the connection string value from environment.prod.ts

After deploying my Angular App to Azure as a Static Web App, everything seemed to be running smoothly. However, I encountered an issue with the file "environment.prod.ts" in the environments folder within my app that contains the following code: export co ...

Dockerized Angular CLI app experiencing issues with hot reload functionality

My existing angular cli application has been dockerized with the following setup: Dockerfile at root level: # Create a new image from the base nodejs 7 image. FROM node:7 # Create the target directory in the imahge RUN mkdir -p /usr/src/app # Set the cr ...

Navigating the way: Directing all TypeScript transpiled files to the build folder

I am currently working on a project using Angular2/Typescript, and I have the tsconfig.js file below: { "compilerOptions": { "module": "commonjs", "moduleResolution": "node", "target": "es5", "sourceMap": true, ...

ES6 import of CSS file results in string output instead of object

Too long; Didn't read I'm facing an issue where the css file I import into a typescript module resolves to a string instead of an object. Can someone help me understand why this is happening? For Instance // preview.ts import test from './ ...

Displaying properties of a class in Typescript using a default getter: Simplified guide

Here is an interface and a class that I am working with: export interface ISample { propA: string; propB: string; } export class Sample { private props = {} as ISample; public get propA(): string { return this.props.propA; } public se ...

Is there a marble experiment that will alter its results when a function is executed?

Within Angular 8, there exists a service that contains a readonly Observable property. This property is created from a BehaviorSubject<string> which holds a string describing the current state of the service. Additionally, the service includes method ...

How to conceal an element in Angular using its unique identifier

I am looking for a way to toggle the visibility of an element based on its ID. I have a dynamic list with the following structure in my TS component: vehicles = [ { "id": 1, "type": "car", ...

Neither of the elements within the ngIf statement is visible despite the fact that one of them should evaluate to true

I'm currently grappling with using ngIf to conceal a component's details until the necessary variable is set. During this waiting period, it should display a loading message. Despite my efforts to find a solution through online searches, I'v ...

The Google Books API has reached its limit for requests

Encountering a rate limit exceeded error from the Google Books API while using this demo: To reproduce, open the developer console in Chrome and perform some searches. The rate limit errors will be displayed in the console. [],"lazyUpdate":null},"status" ...

Angular 5 - Keeping track of variable updates

I've searched various topics on this issue, but none seem to address my specific problem. I need a way to detect changes in the properties of a component without having to convert the variable into an array or iterable. I tried using Subject, but coul ...

Angular 6: A guide to dynamically highlighting navbar elements based on scroll position

I am currently building a single page using Angular 6. The page is quite basic, and my goal is to emphasize the navbar based on scrolling. Below is the code snippet I am working with: .sticky { position: sticky; top: 0; } #i ul { list-style-type: ...

Even as I create fresh references, my JavaScript array object continues to overwrite previous objects

Coming from a background in c# and c++, I am facing some confusion with a particular situation. Within the scope of my function, I am creating a new object before pushing it into an 'array'. Strangely, when I create the new object, it appears to ...