TransWikia.com

Display multiple same values only once while others are looping using ngFor in Angular

Stack Overflow Asked on November 24, 2020

I have the following sample data set which comes from server.

[
    {
        subMenuId: 1,
        submenu: 'Users',
        menu: 'Administration',
        url: '/pms/admin/usrs-dsh',
        icon: 'fas fa-cogs',
    },
    {
        subMenuId: 2,
        submenu: 'Roles',
        menu: 'Administration',
        url: '/pms/admin/roles-dsh',
        icon: 'fas fa-cogs',
    },
    {
        subMenuId: 3,
        submenu: 'Menu Items',
        menu: 'Administration',
        url: '/pms/admin/menus-dsh',
        icon: 'fas fa-cogs',
    },
    {
        subMenuId: 4,
        submenu: 'Banks',
        menu: 'Administration',
        url: '/pms/admin/banks-dsh',
        icon: 'fas fa-cogs',
    },
    {
        subMenuId: 5,
        submenu: 'Branches',
        menu: 'Administration',
        url: '/pms/admin/branches-dsh',
        icon: 'fas fa-cogs',
    },
    {
        subMenuId: 6,
        submenu: 'Dashboard',
        menu: 'PD3',
        url: '/pms/pd3/dsh',
        icon: 'fas fa-cogs',
    },

];

And I want to display this data like this. But currently when displaying data main level values repeat with each sub level value.
Expected Output:
Expected Output

My Output:
my output

Html file:

 <tr *ngFor="let m of userPermissions;">
                        <td>
                            <label class="ml-3 form-check-label">{{m.menu}}</label>
                        </td>
                        <td>
                            <div>
                                <input (change)="changeSubMenus($event, m.menu,m.subMenuId)" type="checkbox" class="ml-0 form-check-input">
                                <label class="ml-3 form-check-label">{{m.submenu}}</label><br>
                            </div>
                        </td>
                    </tr>

TS file:

getMenus(){
  this.userPermissions = this.adminService.allMenus;
  
  this.userPermissions.forEach((val) =>{
     // console.log(val)
     // TODO: remove multiple main level values and display sub level values with one main level value
  })

}

Is there any way to achieve this.
Thanks

One Answer

You need to modify the objects in the array to achieve the requirement you want. You can use reduce for that like below.

You need to have an object structure like the below, to make things simpler

modifiedMenu = [{
  menu: < Main Menu 1 > ,
  subMenu: [ < submenu item > , < submenu item > , ...]
}, {
  menu: < Main Menu 2 > ,
  subMenu: [ < submenu item > , < submenu item > , ...]
}, ...]

So, the original one is

userPermissions = this.menus.reduce((acc, ele) => {
  if (acc.length === 0) {
    acc.push(this.getModifiedMainMenu(ele));
    return acc;
  } else {
    const existedMenu = acc.find(m => m.menu === ele.menu);
    if (existedMenu) {
      existedMenu.subMenu.push(this.getSubMenu(ele));
      return acc;
    } else {
      acc.push(this.getModifiedMainMenu(ele));
      return acc;
    }
  }
}, []);

getModifiedMainMenu(obj) {
  return {
    menu: obj.menu,
    subMenu: [this.getSubMenu(obj)]
  };
}

getSubMenu(obj) {
  return {
    menu: obj.menu,
    subMenuId: obj.subMenuId,
    submenu: obj.submenu,
    url: obj.url,
    icon: obj.icon
  };
}

And in the template file, now you need 2 ngFors to loop main menu items and respective submenu items like below

<tr *ngFor="let m of userPermissions;">
  <td>
    <label class="ml-3 form-check-label">{{m.menu}}</label>
  </td>
  <td>
    <div *ngFor="let subMenu of m.subMenu">
      <input (change)="changeSubMenus($event, subMenu.menu, subMenu.subMenuId)" type="checkbox" class="ml-0 form-check-input">
      <label class="ml-3 form-check-label">{{subMenu.submenu}}</label>
    </div>
  </td>
</tr>

Working Stackblitz

Correct answer by Sivakumar Tadisetti on November 24, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP