Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a lazy load treeview in Angular 7

I am creating a TreeView using Angular and the scenario of creating treeview is I have different API for each level means if I click on one of the parent level nodes then only child node should generate for that particular node only and so on for further level and every child node list is coming from API

Now the issue is when I'm creating a nested list to the created tree view, on the click of any node. A child node is generating for that node and other nodes also.

Here is my code.

<ul>
    <li *ngFor="let item of companyList" [id]="item.id">
        <span (click)="getLocation(item)">{{item.description}}</span>
      <ul>
          <li *ngFor="let loc of loactionData" [id]="loc.id">
          <span (click)="getDepartment(loc)">{{loc.description}}</span>
          <ul>
            <li *ngFor="let depart of deaprtmentData">
              <span (click)="getEmpStatus(depart)">{{depart.description}}</span>
            </li>
           </ul>
          </li>
    </ul>
  </li>
</ul>

Note: Every list is coming from separate API and these click event helps to call API.

Please help me with the above issue thanks in advance.

like image 972
VIVEK Avatar asked Mar 15 '19 11:03

VIVEK


2 Answers

You are repeating the same nested lists for each nested ul. You have to associate your nested lists to their parents.

Since your companyList items and your locationData items have an id, use this id to associate the nested lists to each company and to each location.

To do the association, create a map in your code using a simple object and type it with an index signature. In your case it will look something like this:

companyList: Company[] = [];
locationData: { [companyId: string]: Location[] } = {};
departmentData: { [locationId: string]: Department[] } = {};

Then in your template, you need to index locationData and departmentData using item.id and loc.id:

<ul>
  <li *ngFor="let item of companyList" [id]="item.id">
    <span (click)="getLocation(item)">{{ item.description }}</span>
    <ul>
      <li *ngFor="let loc of locationData[item.id]" [id]="loc.id">
        <span (click)="getDepartment(loc)">{{ loc.description }}</span>
        <ul>
          <li *ngFor="let depart of departmentData[loc.id]">
            <span (click)="getEmpStatus(depart)">{{ depart.description }}</span>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

When you generate your data, place it under the right id in the objects:

getLocation(item: Company): void {
  this.http.get<Location[]>(`${this.api}/locations?companyId=${item.id}`).subscribe(locations => {
    this.locationData[item.id] = locations;
  })
}

getDepartment(location: Location): void {
  this.http.get<Department[]>(`${this.api}/departments?locationId=${location.id}`).subscribe(departments => {
    this.departmentData[location.id] = departments;
  })
}

Here is an example with a User/Post/Comment data model and the jsonplaceholder API.

See this Stackblitz demo for a live example

<ul>
  <li *ngFor="let user of users" [id]="user.id">
    <span (click)="getPosts(user)">{{ user.name }}</span>
    <ul>
      <li *ngFor="let post of posts[user.id]" [id]="post.id">
        <span (click)="getComments(post)">{{ post.title }}</span>
        <ul>
          <li *ngFor="let comment of comments[post.id]">
            <span>{{ comment.name }}</span>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';

interface User {
  id: number;
  name: string;
  username: string;
  email: string;
}

interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

interface Comment {
  postId: number;
  id: number;
  name: string;
  email: string;
  body: string;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  users: User[] = [];
  posts: { [id: number]: Post[] } = {};
  comments: { [id: number]: Comment[] } = {};

  private api = 'https://jsonplaceholder.typicode.com';

  constructor(private http: HttpClient) { }

  ngOnInit(): void {
    this.http.get<User[]>(`${this.api}/users`).subscribe(users => this.users = users);
  }

  getPosts(user: User): void {
    this.http.get<Post[]>(`${this.api}/posts?userId=${user.id}`).subscribe(posts => {
      this.posts[user.id] = posts;
    })
  }

  getComments(post: Post): void {
    this.http.get<Comment[]>(`${this.api}/comments?postId=${post.id}`).subscribe(comments => {
      this.comments[post.id] = comments;
    })
  }
}
like image 148
jo_va Avatar answered Nov 09 '22 09:11

jo_va


You could keep track of the different datasets via their parent's index in the list (id would be prefered in case an item gets deleted from the list and the indexes change for example)

public locationData = {};

load locationData(item, index):

this.locationData[index] = getDataForItem(item);

....

<ul>
            <li *ngFor="let item of companyList; let companyIndex = index;" [id]="item.id">
              <span (click)="getLocation(item, companyIndex)">{{item.description}}</span>
              <ul>
                <li *ngFor="let loc of loactionData[companyIndex]; let locationIndex = index;" [id]="loc.id">
                  <span (click)="getDepartment(loc, locationIndex)">{{loc.description}}</span>
                  <ul>
                    <li *ngFor="let depart of deaprtmentData[locationIndex]; let departmentIndex = index;">
                      <span (click)="getEmpStatus(depart, departmentIndex)">{{depart.description}}</span>

                    </li>
                  </ul>
                </li>
              </ul>
            </li>
          </ul>
like image 24
Bert Maurau Avatar answered Nov 09 '22 10:11

Bert Maurau