Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursion with ng-repeat in Angular

I have the following data structure for items in my sidemenu, in an Angular app based on a paid-for web site theme. The data structure is my own, and the menu is derived from the original menu view with all items in the ul hard coded.

In SidebarController.js:

$scope.menuItems = [
    {
        "isNavItem": true,
        "href": "#/dashboard.html",
        "text": "Dashboard"
    },
    {
        "isNavItem": true,
        "href": "javascript:;",
        "text": "AngularJS Features",
        "subItems": [
            {
                "href": "#/ui_bootstrap.html",
                "text": " UI Bootstrap"
            },
            ...
        ]
    },
    {
        "isNavItem": true,
        "href": "javascript:;",
        "text": "jQuery Plugins",
        "subItems": [
            {
                "href": "#/form-tools",
                "text": " Form Tools"
            },
            {
                "isNavItem": true,
                "href": "javascript:;",
                "text": " Datatables",
                "subItems": [
                    {
                        "href": "#/datatables/managed.html",
                        "text": " Managed Datatables"
                    },
                    ...
                ]
            }
        ]
    }
];

Then I have the following partial view bound to that model as follows:

<ul class="page-sidebar-menu" data-keep-expanded="false" data-auto-scroll="true" data-slide-speed="200" ng-class="{'page-sidebar-menu-closed': settings.layout.pageSidebarClosed}">
    <li ng-repeat="item in menuItems" ng-class="{'start': item.isStart, 'nav-item': item.isNavItem}">
        <a href="{{item.href}}" ng-class="{'nav-link nav-toggle': item.subItems && item.subItems.length > 0}">
            <span class="title">{{item.text}}</span>
        </a>
        <ul ng-if="item.subItems && item.subItems.length > 0" class="sub-menu">
            <li ng-repeat="item in item.subItems" ng-class="{'start': item.isStart, 'nav-item': item.isNavItem}">
                <a href="{{item.href}}" ng-class="{'nav-link nav-toggle': item.subItems && item.subItems.length > 0}">
                    <span class="title">{{item.text}}</span>
                </a>
            </li>
        </ul>
    </li>
</ul>

NOTE There may be $scope properties in the view bindings you don't see in the model, or vice versa, but that is because I have edited them for brevity. Now because the second level li doesn't also contain a conditional ul for its own subItems, the sub-items under the Datatable menu item don't get rendered.

How can I create a view or template, or both, that will bind recursively to the model, so that all sub-items of all sub-items are rendered? This will normally only be up to four levels.

like image 900
ProfK Avatar asked Jun 13 '16 09:06

ProfK


People also ask

Why we use NG-repeat in angular?

If you have an collection of objects, the ng-repeat directive is perfect for making a HTML table, displaying one table row for each object, and one table data for each object property.

What can I use instead of NG-repeat?

You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.

Where is the last element in NG-repeat?

$first and $last It's common when using ng-repeat to add specific behavior to the first or last element of the loop, e.g. special styling around the edges. Instead, ng-repeat already supplies you with two ready boolean properties. $first is true for the first element, and $last is true for the last element.


2 Answers

You can simply use ng-include to make a partial and call it recursively: Partial should be something like this:

<ul>
    <li ng-repeat="item in item.subItems">
      <a href="{{item.href}}" ng-class="{'nav-link nav-toggle': item.subItems && item.subItems.length > 0}">
          <span class="title">{{item.text}}</span>
      </a>
      <div ng-switch on="item.subItems.length > 0">
        <div ng-switch-when="true">
          <div ng-init="subItems = item.subItems;" ng-include="'partialSubItems.html'"></div>  
        </div>
      </div>
    </li>
</ul>

And your html:

<ul class="page-sidebar-menu" data-keep-expanded="false" data-auto-scroll="true" data-slide-speed="200" ng-class="{'page-sidebar-menu-closed': settings.layout.pageSidebarClosed}">
    <li ng-repeat="item in menuItems" ng-class="{'start': item.isStart, 'nav-item': item.isNavItem}">
        <a href="{{item.href}}" ng-class="{'nav-link nav-toggle': item.subItems && item.subItems.length > 0}">
            <span class="title">{{item.text}}</span>
        </a>
        <ul ng-if="item.subItems && item.subItems.length > 0" class="sub-menu">

            <li ng-repeat="item in item.subItems" ng-class="{'start': item.isStart, 'nav-item': item.isNavItem}">
                 <a href="{{item.href}}" ng-class="{'nav-link nav-toggle': item.subItems && item.subItems.length > 0}">
                    <span class="title">{{item.text}}</span>
                </a>

                 <div ng-switch on="item.subItems.length > 0">
                    <div ng-switch-when="true">
                      <div ng-init="subItems = item.subItems;" ng-include="'newpartial.html'"></div>  
                    </div>
                </div>

            </li>
        </ul>
    </li>
</ul>

Here is the working plunker http://plnkr.co/edit/9HJZzV4cgacK92xxQOr0?p=preview

like image 85
Rahul Arora Avatar answered Sep 24 '22 13:09

Rahul Arora


You can achieve this simply by including a javascript template and template include using ng-include

define javascript template

<script type="text/ng-template" id="menu.html">...</script>

and include it like:

<div ng-if="item.subItems.length" ng-include="'menu.html'"></div>

Example: In this example I used basic html without any class you can use classes as you need. I just shown basic recursion structure.

In html:

<ul>
    <li ng-repeat="item in menuItems">
      <a href="{{item.href}}">
        <span>{{item.text}}</span>
      </a>
      <div ng-if="item.subItems.length" ng-include="'menu.html'"></div>
    </li>
</ul>


<script type="text/ng-template" id="menu.html">
   <ul>
      <li ng-repeat="item in item.subItems">
        <a href="{{item.href}}">
          <span>{{item.text}}</span>
        </a>
        <div ng-if="item.subItems.length" ng-include="'menu.html'"></div>
      </li>
   </ul>
</script>

PLUNKER DEMO

like image 28
Shaishab Roy Avatar answered Sep 23 '22 13:09

Shaishab Roy