1

Let's assume I have the following tree structure:

    Item1            Item2            Item3            Item4             Itemn  
  /   |   \        /   |   \        /   |   \        /   |   \         /   |   \
 /    |    \      /    |    \      /    |    \      /    |    \       /    |    \
 a    b    c      d    e    f      g    h     i     j    k     l     n-2   n-1   n

Each node from a,b,...,n has values. What's a more efficient way to structure this navigation?

My approach is he following:

Create an array toggleStatus that contains boolean values. Assign a number to each tab which is respectively equal to the index of toggleStatus. A method toggle(index) will toggle the value of toggleStatus at index index.

Please have a look at my Stackblitz for a working example.

2 Answers 2

2
+50

You don't need to add all the data directly into your HTML. You can just create an array of objects which will make it perfect for a nested ngFor case.

Your data will look something like this

menus: any[] = [{
    item: 'Item 1', submenus: [
        { item: 'a', info: 'Info of a' },
        { item: 'b', info: 'Info of b' },
        { item: 'c', info: 'Info of c' }
    ]
}, {
    item: 'Item 2', submenus: [
        { item: 'd', info: 'Info of d' },
        { item: 'e', info: 'Info of e' },
        { item: 'f', info: 'Info of f' }
    ]
}, {
    item: 'Item 3', submenus: [
        { item: 'g', info: 'Info of g' },
        { item: 'h', info: 'Info of h' },
        { item: 'i', info: 'Info of i' }
    ]
}, {
    item: 'Item 4', submenus: [
        { item: 'j', info: 'Info of j' },
        { item: 'k', info: 'Info of k' },
        { item: 'l', info: 'Info of l' }
    ]
}];

Then in your HTML, we will loop over the menus using ngFor

<div *ngFor="let menu of menus; let i = index">
    <!-- Menu -->
    <div (click)="toggleMenu(i)" class="text-white cursor-pointer text-center">
        <div class="py-4 bg-blue-600 hover:bg-blue-400">
            {{menu.item}}
        </div>
        <div class="container" *ngIf="showSubmenu[i]">
            <!-- Submenus -->
            <ng-container *ngFor="let submenu of menu.submenus; let j = index">
                <div (click)="toggleSubmenu($event, submenu.item)" class="py-3 w-1/3 inline-block"
                    [ngClass]="{'bg-blue-200': j === 0, 'bg-blue-300': j === 1, 'bg-blue-400': j === 2}">
                    {{submenu.item}}
                </div>
            </ng-container>
            <!-- Information -->
            <div *ngFor="let submenu of menu.submenus; let j = index">
                <div *ngIf="showInfo[submenu.item]" (click)="$event.stopPropagation()" class="py-3 bg-green-400">
                    {{submenu.info}}
                </div>
            </div>
        </div>
    </div>
</div>

In your component, we shall define the flags and the toggle functions. I have used an array for flags. We will dynamically insert the toggle flag into this array based on the index. $event.stopPropagation is used to prevent the click event from bubbling up into the parent element's click event. Here's how the component will look

showSubmenu: any[] = [];
showInfo: any[] = [];

toggleMenu(index: number) {
    this.showSubmenu[index] = !this.showSubmenu[index];
}

toggleSubmenu(event: MouseEvent, item: string) {
    event.stopPropagation();
    this.showInfo[item] = !this.showInfo[item];
}

Note: The item passed to thetoggleSubmenu should be a unique value. If you have an id, it would be preferred to use that here instead of item.

Here is a working StackBlitz of the same.

Sign up to request clarification or add additional context in comments.

2 Comments

Beautiful! How would I proceed if I also wanted to show not only plain text, but html code?
Just use [innerHTML] instead to display the values. Check out my StackBlitz, I updated the Info part to show the data using innerHTML and I modified the first item in menus to have HTML content in the submenu's info.
1

here I have some sample code through an array in your ts

 toggleArray = [{ name:'Item 1',subItems: [{name:'a',msg:'this is a',isExpand:false},{name:'b',msg:'this is b',isExpand:false},{name:'c',msg:'this is c',isExpand:false}] ,isExpand : false},{ name:'Item 2',subItems: [{name:'d',msg:'this is d',isExpand:false},{name:'e',msg:'this is e',isExpand:false},{name:'f',msg:'this is f',isExpand:false}] ,isExpand : false}]
  toggleItem(item) {
    item.isExpand = item.isExpand ? false:true;
    item.subItems.forEach((item) => {
      item.isExpand = false;
    })
  }
  toggleSubItem(subitem) {
    subitem.isExpand = subitem.isExpand ? false:true;
  }

in your HTML. I just put logic please fix CSS whatever you needed.

<div *ngFor="let item of toggleArray" (click)="toggleItem(item)" class="p-4 bg-blue-600 text-white hover:bg-blue-400 cursor-pointer text-center">
    {{item.name}}
     <div class="container mx-auto">
    <div *ngIf="item.isExpand" class="flex flex-row justify-between">
      <div *ngFor="let subitem of item.subItems" (click)="toggleSubItem(subitem)" class="py-3 w-1/3 text-center bg-blue-200">
        {{subitem.name}}
        <div class="container mx-auto">
          <div *ngIf="subitem.isExpand" class="flex flex-row justify-between">
            <div  (click)="toggleSubItem(subitem)" class="py-3 w-1/3 text-center bg-blue-200">
              {{subitem.msg}}
            </div>
           </div>
          </div>
      </div>
    </div>
  </div>
  </div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.