用ng-repeat角进行递归

时间:2021-12-04 13:29:09

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.

我的sidemenu中有以下数据结构,它是基于付费网站主题的一个有棱角的应用程序。数据结构是我自己的,菜单是从原始菜单视图派生出来的,所有项目都使用ul硬编码。

In SidebarController.js:

在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.

注意,在视图绑定中可能有您在模型中看不到的$scope属性,反之亦然,但这是因为我为简洁起见而对它们进行了编辑。现在,由于第二层次的li不包含有条件的ul用于自己的子项目,所以Datatable菜单项下的子项不会被呈现。

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.

如何创建将递归绑定到模型的视图或模板,从而呈现所有子项的所有子项?这通常只会达到四层。

10 个解决方案

#1


13  

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

您可以简单地使用ng-include创建一个局部,然后递归地调用它:局部应该是这样的:

<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:

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

这是工作柱塞器http://plnkr.co/edit/9hjzzv4cgack92xxqor0?

#2


8  

You can achieve this simply by include java-script template and template include using ng-include

您可以通过包含使用ng-include的java脚本模板和模板来实现这一点。

define java-scrit template

定义java-scrit模板

<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.

示例:在这个示例中,我使用了基本的html,没有任何类可以根据需要使用类。我只是展示了基本的递归结构。

In html:

在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

恰好演示

#3


7  

If your intention is to draw an menu with indefinite level of sub items, probably a good implementation is to make a directive.

如果你的意图是画一个有不定等级的子条目的菜单,很可能一个好的实现就是做一个指令。

With a directive you will be able to assume more control over your menu.

有了指令,你就可以对菜单进行更多的控制。

I created a basic exepmle with full recursion, on with you can see an easy implementation of more than one menu on the same page and more than 3 levels on one of the menus, see this plunker.

我创建了一个带有完整递归的基本exepmle,您可以在同一个页面上看到一个以上菜单的简单实现,在一个菜单上看到这个柱塞。

Code:

代码:

.directive('myMenu', ['$parse', function($parse) {
    return {
      restrict: 'A',
      scope: true,
      template:
        '<li ng-repeat="item in List" ' +
        '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 my-menu="item.subItems" class="sub-menu"> </ul>' +
        '</li>',
      link: function(scope,element,attrs){
        //this List will be scope invariant so if you do multiple directive 
        //menus all of them wil now what list to use
        scope.List = $parse(attrs.myMenu)(scope);
      }
    }
}])

Markup:

标记:

<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}"
    my-menu="menuItems">
</ul>

Edit

编辑

Some notes

一些笔记

When it comes to make the decision about ng-include(that i think is a fair enough solution) or .directive, you have to ask yourself at least two questions first. Does my code fragment going to need some logic? If not you could as well just go for the ng-include. But if you are going to put more logic in the fragment making it customizable, make changes to the element or attrs (DOM) manipulation, you should go for directive. As well one point that make me fell more confortable with the directive is reusability of the code you write, since in my example you can give controll more and make a more generic one i assume you should go for that if your project is big and needs to grow. So the second question does my code going to be reusable?

当你决定考虑ng-include(我认为这是一个足够公平的解决方案)或。directive时,你必须先问自己至少两个问题。我的代码片段需要一些逻辑吗?如果没有,你也可以选择ng-include。但是,如果您打算在片段中添加更多的逻辑,使其可定制,对元素或attrs (DOM)操作进行更改,您应该选择directive。也让我摔了一跤更舒适的指令是您编写的代码的可重用性,因为在我的例子你可以给控制越来越成为一个更通用的我认为你应该去,如果你的项目是大,需要成长。第二个问题,我的代码是否可以重用?

A reminder for a clean directive is that instead of template you could use the templateUrl, and you can give a file to feed the html code that is currently in the template.

一个干净指令的提醒是,您可以使用templateUrl而不是模板,并且您可以提供一个文件来提供模板中当前的html代码。

If you are using 1.5+ you can now choose to use .component. Component is a wrapper arroud .direcitve that has a lot less boilerplate code. Here you can see the diference.

如果您正在使用1.5+,现在可以选择使用.component。组件是一个包装器,它有更少的样板代码。在这里你可以看到差异。

                  Directive                Component

bindings          No                       Yes (binds to controller)
bindToController  Yes (default: false)     No (use bindings instead)
compile function  Yes                      No
controller        Yes                      Yes (default function() {})
controllerAs      Yes (default: false)     Yes (default: $ctrl)
link functions    Yes                      No
multiElement      Yes                      No
priority          Yes                      No
require           Yes                      Yes
restrict          Yes                      No (restricted to elements only)
scope             Yes (default: false)     No (scope is always isolate)
template          Yes                      Yes, injectable
templateNamespace Yes                      No
templateUrl       Yes                      Yes, injectable
terminal          Yes                      No
transclude        Yes (default: false)     Yes (default: false)

Source angular guide for component

组件的源角度指南。

Edit

编辑

As sugested by Mathew Berg if you want not to include the ul element if the list of subItems is empty you can change the ul to this something like this <ul ng-if="item.subItems.length>0" my-menu="item.subItems" class="sub-menu"> </ul>

Mathew Berg建议,如果您不想包含ul元素,如果子项列表是空的,您可以将ul更改为如下所示的

    0 "菜单上= "项目。子项“阶级= "子菜单" > < / ul >

#4


0  

Rahul Arora's answer is good, see this blog post for a similar example. The one change I would make is to use a component instead of ng-include. For an example see this Plunker:

拉胡尔·阿罗拉的回答很好,类似的例子见这篇博客文章。我要做的一个更改是使用组件而不是ng-include。举个例子,看看这个柱塞:

app
  .component('recursiveItem', {
    bindings: {
      item: '<'
    },
    controllerAs: 'vm',
    templateUrl: 'newpartial.html'
  });

#5


0  

In order to make recursion in Angular, I would love to use the basic feature of angularJS and i.e directive.

为了在角度上进行递归,我想使用angularJS和I的基本特性。e指令。

index.html

index . html

<rec-menu menu-items="menuItems"></rec-menu>

recMenu.html

recMenu.html

<ul>
  <li ng-repeat="item in $ctrl.menuItems">
    <a ng-href="{{item.href}}">
      <span ng-bind="item.text"></span>
    </a>
    <div ng-if="item.menuItems && item.menuItems.length">
      <rec-menu menu-items="item.menuItems"></rec-menu>
    </div>
  </li>
</ul>

recMenu.html

recMenu.html

angular.module('myApp').component('recMenu', {
  templateUrl: 'recMenu.html',
  bindings: {
    menuItems: '<'
  }
});

Here is working Plunker

这是工作恰好

#6


0  

Before using templates with ng-include or writing your own directive, I would suggest that you consider using an existing tree component implementation.
The reason is that from your description, that's exactly what you need. You have a hierarchical tree-like data structure that you want to display. It sounds obvious to me that you need a tree component.

在使用带有ng-include的模板或编写自己的指令之前,我建议您考虑使用现有的树组件实现。因为根据你的描述,这正是你所需要的。您有一个想要显示的层次树状数据结构。对我来说,显然需要一个树组件。

Take a look at the following implementations (1st one preferable):
https://github.com/angular-ui-tree/angular-ui-tree
https://github.com/wix/angular-tree-control
http://ngmodules.org/modules/angular.treeview

看看下面的实现(第一种更好):https://github.com/angular-ui-tree/angular-ui-tree https://github.com/wix/angular-tree控件http://ngmodules.org/modules/angular.treeview

All of the above require only that you do a minor adjustment to your model, or alternatively, use a proxy model.

所有这些都只需要对模型做一个小的调整,或者使用代理模型。

If you do insist on implementing it by yourself (and no matter how you end up doing it, essentially you still will be implementing a tree component from scratch), I would suggest the directive approach as proposed in previous answers. Here's how I'd do it:

如果您坚持自己实现它(无论您最终如何实现它,本质上您仍然将从头实现一个tree组件),我将建议使用前面回答中建议的指令方法。我是这样做的:

JS

var app=angular.module('MyApp', []);

app.controller('MyCtrl', function($scope, $window) {
  $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"
                    }
                ]
            }
        ]
    }];
});

app.directive('myMenu', ['$compile', function($compile) {
  return {
    restrict: 'E',
    scope: {
      menu: '='      
    },
    replace: true,
    link: function(scope, elem, attrs) {
      var items = $compile('<my-menu-item ng-repeat="item in menu" menu-item="item"></my-menu-item>')(scope);

      elem.append(items);
    },
    template: '<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}"></ul>'
  };
}]);

app.directive('myMenuItem', [function() {
  return {
    restrict: 'E',
    scope: {
      menuItem: '='
    },
    replace: true,
    template: '<li ng-class="{\'start\': item.isStart, \'nav-item\': item.isNavItem}"><a href="{{menuItem.href}}" ng-class="{\'nav-link nav-toggle\': menuItem.subItems && menuItem.subItems.length > 0}"> <span class="title">{{menuItem.text}}</span></a><my-menu menu="menuItem.subItems"></my-menu></li>'

  };
}]);

HTML

<div ng-app="MyApp" ng-controller="MyCtrl">
  <my-menu menu="menuItems"></my-menu>
</div>

Here's a working CodePen example: http://codepen.io/eitanfar/pen/oxZrpQ

这里有一个可用的代码页示例:http://codepen.io/eitanfar/pen/oxZrpQ

Some notes

  1. You don't have to use 2 directives ("my-menu", "my-menu-item"), you can use just 1 (simply replace the ng-repeat of "my-menu-item" with its template), however, I think it's more coherent this way
  2. 您不必使用2个指令(“my-menu”、“my-menu-item”),只需使用1个指令(只需将“my-menu-item”的ng-repeat替换为其模板),但是,我认为这种方式更加一致
  3. The reason that the directive solution you tried didn't work (an educated guess, as I haven't debugged your attempt), is that it runs into an infinite loop. It does so, since linking happens for internal elements first. What I do in my suggested solution, is to postpone the linking of the sub items till after the linking of the parent menu is finished. Any disadvantages that this might have can be overcome by providing references in the scope (as I provide the 'menuItem' binding).
  4. 你尝试的指令解决方案没有成功的原因(这是一个有根据的猜测,因为我还没有调试你的尝试)是它会运行到一个无限循环中。它这样做,因为链接首先发生在内部元素上。我在建议的解决方案中所做的是,将子项的链接推迟到父菜单的链接完成之后。通过在范围内提供引用(正如我提供“menuItem”绑定一样),可以克服这一点的任何缺点。

Hope this helps.

希望这个有帮助。

#7


0  

I am sure this exactly you are looking for -

我肯定这正是你要找的-

You can achieve unlimited recursion by ng-repeat

你可以通过ng-repeat实现无限递归。

<script type="text/ng-template"  id="tree_item_renderer.html">
{{data.name}}
<button ng-click="add(data)">Add node</button>
<button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
<ul>
    <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'"></li>
</ul>

  angular.module("myApp", []).
controller("TreeController", ['$scope', function($scope) {
    $scope.delete = function(data) {
        data.nodes = [];
    };
    $scope.add = function(data) {
        var post = data.nodes.length + 1;
        var newName = data.name + '-' + post;
        data.nodes.push({name: newName,nodes: []});
    };
    $scope.tree = [{name: "Node", nodes: []}];
}]);

Here is jsfiddle

这是jsfiddle

#8


0  

Do you mean something like this? http://jsfiddle.net/uXbn6/3639/

你的意思是这样的吗?http://jsfiddle.net/uXbn6/3639/

JS

angular.module("myApp", []).controller("TreeController", ['$scope',function($scope) {


    $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": "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"
        }]
      }]
    }];
  }]);

HTML

  <script type="text/ng-template" id="tree_item_renderer.html">
    <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}" ng-include="'tree_item_renderer.html'"></li>
    </ul>
  </script>

  <div ng-app="myApp" ng-controller="TreeController">
    <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}" ng-include="'tree_item_renderer.html'"></li>
    </ul>
  </div>

#9


0  

Recursion can be very tricky. As things will get out of hand depending on how deep your tree is. Here is my suggestion:

递归非常棘手。因为事情会失控,这取决于你的树有多深。这是我的建议:

.directive('menuItem', function($compile){
    return {
        restrict: 'A',
        scope: {
            menuItem: '=menuItem'
        },
        templateUrl: 'menuItem.html',
        replace: true,
        link: function(scope, element){
            var watcher = scope.$watch('menuItem.subItems', function(){
                if(scope.menuItem.subItems && scope.menuItem.subItems.length){
                    var subMenuItems = angular.element('<ul><li ng-repeat="subItem in menuItem.subItems" menu-item="subItem"></li></ul>')
                    $compile(subMenuItems)(scope);
                    element.append(subMenuItems);
                    watcher();
                }
            });
        }           
    }
})

HTML:

HTML:

<li>    
    <a ng-href="{{ menuItem.href }}">{{ menuItem.text }}</a>
</li>

This will make sure it doesn't create sub items repeatedly. You can see it working in a jsFiddle here: http://jsfiddle.net/gnk8vcrv/

这将确保它不会重复创建子项。您可以在这里看到它在jsFiddle中工作:http://jsfiddle.net/gnk8vcrv/。

If you find it's crashing your app because you have a massive amount of lists (I'd be interested to see) you can hide the parts of the if statement besides the watcher behind a $timeout.

如果你发现它正在崩溃你的应用,因为你有大量的列表(我很有兴趣看到),你可以隐藏If语句的部分,除了在$timeout后面的观察者。

#10


0  

After reviewing these options I found this article very clean/helpful for an ng-include approach that handles model changes well: http://benfoster.io/blog/angularjs-recursive-templates

在查看了这些选项之后,我发现这篇文章对于处理模型更改的ng-include方法非常干净/有帮助:http://benfoster.io/blog/angularjs- recursivetemplate

In summary:

总而言之:

<script type="text/ng-template" id="categoryTree">
    {{ category.title }}
    <ul ng-if="category.categories">
        <li ng-repeat="category in category.categories" ng-include="'categoryTree'">           
        </li>
    </ul>
</script>

then

然后

<ul>
    <li ng-repeat="category in categories" ng-include="'categoryTree'"></li>
</ul>  

#1


13  

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

您可以简单地使用ng-include创建一个局部,然后递归地调用它:局部应该是这样的:

<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:

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

这是工作柱塞器http://plnkr.co/edit/9hjzzv4cgack92xxqor0?

#2


8  

You can achieve this simply by include java-script template and template include using ng-include

您可以通过包含使用ng-include的java脚本模板和模板来实现这一点。

define java-scrit template

定义java-scrit模板

<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.

示例:在这个示例中,我使用了基本的html,没有任何类可以根据需要使用类。我只是展示了基本的递归结构。

In html:

在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

恰好演示

#3


7  

If your intention is to draw an menu with indefinite level of sub items, probably a good implementation is to make a directive.

如果你的意图是画一个有不定等级的子条目的菜单,很可能一个好的实现就是做一个指令。

With a directive you will be able to assume more control over your menu.

有了指令,你就可以对菜单进行更多的控制。

I created a basic exepmle with full recursion, on with you can see an easy implementation of more than one menu on the same page and more than 3 levels on one of the menus, see this plunker.

我创建了一个带有完整递归的基本exepmle,您可以在同一个页面上看到一个以上菜单的简单实现,在一个菜单上看到这个柱塞。

Code:

代码:

.directive('myMenu', ['$parse', function($parse) {
    return {
      restrict: 'A',
      scope: true,
      template:
        '<li ng-repeat="item in List" ' +
        '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 my-menu="item.subItems" class="sub-menu"> </ul>' +
        '</li>',
      link: function(scope,element,attrs){
        //this List will be scope invariant so if you do multiple directive 
        //menus all of them wil now what list to use
        scope.List = $parse(attrs.myMenu)(scope);
      }
    }
}])

Markup:

标记:

<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}"
    my-menu="menuItems">
</ul>

Edit

编辑

Some notes

一些笔记

When it comes to make the decision about ng-include(that i think is a fair enough solution) or .directive, you have to ask yourself at least two questions first. Does my code fragment going to need some logic? If not you could as well just go for the ng-include. But if you are going to put more logic in the fragment making it customizable, make changes to the element or attrs (DOM) manipulation, you should go for directive. As well one point that make me fell more confortable with the directive is reusability of the code you write, since in my example you can give controll more and make a more generic one i assume you should go for that if your project is big and needs to grow. So the second question does my code going to be reusable?

当你决定考虑ng-include(我认为这是一个足够公平的解决方案)或。directive时,你必须先问自己至少两个问题。我的代码片段需要一些逻辑吗?如果没有,你也可以选择ng-include。但是,如果您打算在片段中添加更多的逻辑,使其可定制,对元素或attrs (DOM)操作进行更改,您应该选择directive。也让我摔了一跤更舒适的指令是您编写的代码的可重用性,因为在我的例子你可以给控制越来越成为一个更通用的我认为你应该去,如果你的项目是大,需要成长。第二个问题,我的代码是否可以重用?

A reminder for a clean directive is that instead of template you could use the templateUrl, and you can give a file to feed the html code that is currently in the template.

一个干净指令的提醒是,您可以使用templateUrl而不是模板,并且您可以提供一个文件来提供模板中当前的html代码。

If you are using 1.5+ you can now choose to use .component. Component is a wrapper arroud .direcitve that has a lot less boilerplate code. Here you can see the diference.

如果您正在使用1.5+,现在可以选择使用.component。组件是一个包装器,它有更少的样板代码。在这里你可以看到差异。

                  Directive                Component

bindings          No                       Yes (binds to controller)
bindToController  Yes (default: false)     No (use bindings instead)
compile function  Yes                      No
controller        Yes                      Yes (default function() {})
controllerAs      Yes (default: false)     Yes (default: $ctrl)
link functions    Yes                      No
multiElement      Yes                      No
priority          Yes                      No
require           Yes                      Yes
restrict          Yes                      No (restricted to elements only)
scope             Yes (default: false)     No (scope is always isolate)
template          Yes                      Yes, injectable
templateNamespace Yes                      No
templateUrl       Yes                      Yes, injectable
terminal          Yes                      No
transclude        Yes (default: false)     Yes (default: false)

Source angular guide for component

组件的源角度指南。

Edit

编辑

As sugested by Mathew Berg if you want not to include the ul element if the list of subItems is empty you can change the ul to this something like this <ul ng-if="item.subItems.length>0" my-menu="item.subItems" class="sub-menu"> </ul>

Mathew Berg建议,如果您不想包含ul元素,如果子项列表是空的,您可以将ul更改为如下所示的

    0 "菜单上= "项目。子项“阶级= "子菜单" > < / ul >

#4


0  

Rahul Arora's answer is good, see this blog post for a similar example. The one change I would make is to use a component instead of ng-include. For an example see this Plunker:

拉胡尔·阿罗拉的回答很好,类似的例子见这篇博客文章。我要做的一个更改是使用组件而不是ng-include。举个例子,看看这个柱塞:

app
  .component('recursiveItem', {
    bindings: {
      item: '<'
    },
    controllerAs: 'vm',
    templateUrl: 'newpartial.html'
  });

#5


0  

In order to make recursion in Angular, I would love to use the basic feature of angularJS and i.e directive.

为了在角度上进行递归,我想使用angularJS和I的基本特性。e指令。

index.html

index . html

<rec-menu menu-items="menuItems"></rec-menu>

recMenu.html

recMenu.html

<ul>
  <li ng-repeat="item in $ctrl.menuItems">
    <a ng-href="{{item.href}}">
      <span ng-bind="item.text"></span>
    </a>
    <div ng-if="item.menuItems && item.menuItems.length">
      <rec-menu menu-items="item.menuItems"></rec-menu>
    </div>
  </li>
</ul>

recMenu.html

recMenu.html

angular.module('myApp').component('recMenu', {
  templateUrl: 'recMenu.html',
  bindings: {
    menuItems: '<'
  }
});

Here is working Plunker

这是工作恰好

#6


0  

Before using templates with ng-include or writing your own directive, I would suggest that you consider using an existing tree component implementation.
The reason is that from your description, that's exactly what you need. You have a hierarchical tree-like data structure that you want to display. It sounds obvious to me that you need a tree component.

在使用带有ng-include的模板或编写自己的指令之前,我建议您考虑使用现有的树组件实现。因为根据你的描述,这正是你所需要的。您有一个想要显示的层次树状数据结构。对我来说,显然需要一个树组件。

Take a look at the following implementations (1st one preferable):
https://github.com/angular-ui-tree/angular-ui-tree
https://github.com/wix/angular-tree-control
http://ngmodules.org/modules/angular.treeview

看看下面的实现(第一种更好):https://github.com/angular-ui-tree/angular-ui-tree https://github.com/wix/angular-tree控件http://ngmodules.org/modules/angular.treeview

All of the above require only that you do a minor adjustment to your model, or alternatively, use a proxy model.

所有这些都只需要对模型做一个小的调整,或者使用代理模型。

If you do insist on implementing it by yourself (and no matter how you end up doing it, essentially you still will be implementing a tree component from scratch), I would suggest the directive approach as proposed in previous answers. Here's how I'd do it:

如果您坚持自己实现它(无论您最终如何实现它,本质上您仍然将从头实现一个tree组件),我将建议使用前面回答中建议的指令方法。我是这样做的:

JS

var app=angular.module('MyApp', []);

app.controller('MyCtrl', function($scope, $window) {
  $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"
                    }
                ]
            }
        ]
    }];
});

app.directive('myMenu', ['$compile', function($compile) {
  return {
    restrict: 'E',
    scope: {
      menu: '='      
    },
    replace: true,
    link: function(scope, elem, attrs) {
      var items = $compile('<my-menu-item ng-repeat="item in menu" menu-item="item"></my-menu-item>')(scope);

      elem.append(items);
    },
    template: '<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}"></ul>'
  };
}]);

app.directive('myMenuItem', [function() {
  return {
    restrict: 'E',
    scope: {
      menuItem: '='
    },
    replace: true,
    template: '<li ng-class="{\'start\': item.isStart, \'nav-item\': item.isNavItem}"><a href="{{menuItem.href}}" ng-class="{\'nav-link nav-toggle\': menuItem.subItems && menuItem.subItems.length > 0}"> <span class="title">{{menuItem.text}}</span></a><my-menu menu="menuItem.subItems"></my-menu></li>'

  };
}]);

HTML

<div ng-app="MyApp" ng-controller="MyCtrl">
  <my-menu menu="menuItems"></my-menu>
</div>

Here's a working CodePen example: http://codepen.io/eitanfar/pen/oxZrpQ

这里有一个可用的代码页示例:http://codepen.io/eitanfar/pen/oxZrpQ

Some notes

  1. You don't have to use 2 directives ("my-menu", "my-menu-item"), you can use just 1 (simply replace the ng-repeat of "my-menu-item" with its template), however, I think it's more coherent this way
  2. 您不必使用2个指令(“my-menu”、“my-menu-item”),只需使用1个指令(只需将“my-menu-item”的ng-repeat替换为其模板),但是,我认为这种方式更加一致
  3. The reason that the directive solution you tried didn't work (an educated guess, as I haven't debugged your attempt), is that it runs into an infinite loop. It does so, since linking happens for internal elements first. What I do in my suggested solution, is to postpone the linking of the sub items till after the linking of the parent menu is finished. Any disadvantages that this might have can be overcome by providing references in the scope (as I provide the 'menuItem' binding).
  4. 你尝试的指令解决方案没有成功的原因(这是一个有根据的猜测,因为我还没有调试你的尝试)是它会运行到一个无限循环中。它这样做,因为链接首先发生在内部元素上。我在建议的解决方案中所做的是,将子项的链接推迟到父菜单的链接完成之后。通过在范围内提供引用(正如我提供“menuItem”绑定一样),可以克服这一点的任何缺点。

Hope this helps.

希望这个有帮助。

#7


0  

I am sure this exactly you are looking for -

我肯定这正是你要找的-

You can achieve unlimited recursion by ng-repeat

你可以通过ng-repeat实现无限递归。

<script type="text/ng-template"  id="tree_item_renderer.html">
{{data.name}}
<button ng-click="add(data)">Add node</button>
<button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
<ul>
    <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'"></li>
</ul>

  angular.module("myApp", []).
controller("TreeController", ['$scope', function($scope) {
    $scope.delete = function(data) {
        data.nodes = [];
    };
    $scope.add = function(data) {
        var post = data.nodes.length + 1;
        var newName = data.name + '-' + post;
        data.nodes.push({name: newName,nodes: []});
    };
    $scope.tree = [{name: "Node", nodes: []}];
}]);

Here is jsfiddle

这是jsfiddle

#8


0  

Do you mean something like this? http://jsfiddle.net/uXbn6/3639/

你的意思是这样的吗?http://jsfiddle.net/uXbn6/3639/

JS

angular.module("myApp", []).controller("TreeController", ['$scope',function($scope) {


    $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": "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"
        }]
      }]
    }];
  }]);

HTML

  <script type="text/ng-template" id="tree_item_renderer.html">
    <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}" ng-include="'tree_item_renderer.html'"></li>
    </ul>
  </script>

  <div ng-app="myApp" ng-controller="TreeController">
    <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}" ng-include="'tree_item_renderer.html'"></li>
    </ul>
  </div>

#9


0  

Recursion can be very tricky. As things will get out of hand depending on how deep your tree is. Here is my suggestion:

递归非常棘手。因为事情会失控,这取决于你的树有多深。这是我的建议:

.directive('menuItem', function($compile){
    return {
        restrict: 'A',
        scope: {
            menuItem: '=menuItem'
        },
        templateUrl: 'menuItem.html',
        replace: true,
        link: function(scope, element){
            var watcher = scope.$watch('menuItem.subItems', function(){
                if(scope.menuItem.subItems && scope.menuItem.subItems.length){
                    var subMenuItems = angular.element('<ul><li ng-repeat="subItem in menuItem.subItems" menu-item="subItem"></li></ul>')
                    $compile(subMenuItems)(scope);
                    element.append(subMenuItems);
                    watcher();
                }
            });
        }           
    }
})

HTML:

HTML:

<li>    
    <a ng-href="{{ menuItem.href }}">{{ menuItem.text }}</a>
</li>

This will make sure it doesn't create sub items repeatedly. You can see it working in a jsFiddle here: http://jsfiddle.net/gnk8vcrv/

这将确保它不会重复创建子项。您可以在这里看到它在jsFiddle中工作:http://jsfiddle.net/gnk8vcrv/。

If you find it's crashing your app because you have a massive amount of lists (I'd be interested to see) you can hide the parts of the if statement besides the watcher behind a $timeout.

如果你发现它正在崩溃你的应用,因为你有大量的列表(我很有兴趣看到),你可以隐藏If语句的部分,除了在$timeout后面的观察者。

#10


0  

After reviewing these options I found this article very clean/helpful for an ng-include approach that handles model changes well: http://benfoster.io/blog/angularjs-recursive-templates

在查看了这些选项之后,我发现这篇文章对于处理模型更改的ng-include方法非常干净/有帮助:http://benfoster.io/blog/angularjs- recursivetemplate

In summary:

总而言之:

<script type="text/ng-template" id="categoryTree">
    {{ category.title }}
    <ul ng-if="category.categories">
        <li ng-repeat="category in category.categories" ng-include="'categoryTree'">           
        </li>
    </ul>
</script>

then

然后

<ul>
    <li ng-repeat="category in categories" ng-include="'categoryTree'"></li>
</ul>