用于从递归指令生成的菜单结构的 AngularJS 多级下拉菜单 [英] AngularJS multilevel dropdown menu for a menu structure generated from a recursive directive

查看:22
本文介绍了用于从递归指令生成的菜单结构的 AngularJS 多级下拉菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我这里有泡菜.我必须从网络服务调用中获取我的多级导航菜单.

由于我的导航菜单中可以有无数个子菜单,我不得不使用递归指令来构建我的父/子导航结构.现在我想弄清楚如何把它变成一个功能性的下拉菜单结构.我正在看 angularui-bootstrap,他们有一个Dropdown Toggle,它具有一些基本的下拉菜单功能,但由于我使用了递归指令我的菜单结构已经有 angularjs 生成的 css附加到它们的类.angularjs-bootstrap 下拉菜单有与我的 angularjs 生成的类不同的 css 类......瞧!

    <li ng-repeat="parent in parents" class="ng-scope"><recursive-list-item on-node-click="onNodeClickFn(node)" parent="parent" class="ng-isolate-scope ng-scope"><a data-ng-click="onNodeClick({node: parent})" href="javascript:void(0)" class="ng-scope ng-binding">衣服</a><!-- ngIf: parent.children.length >0 --><ul data-ng-if="parent.children.length &gt; 0" class="ng-scope"><!-- ngRepeat: child in parent.children --><li ng-repeat="child in parent.children" class="ng-scope"><recursive-list-item data-on-node-click="onNodeClickFn(node)" data-parent="child" class="ng-isolate-scope ng-scope"><a data-ng-click="onNodeClick({node: parent})" href="javascript:void(0)" class="ng-scope ng-binding">Gortex Jackets</a><!-- ngIf: parent.children.length >0 --></recursive-list-item><!-- end ngRepeat: child in parent.children -->.........
</recursive-list-item><!-- end ngRepeat: child in parent.children -->......

那是作为我的递归导航菜单的最终输出生成的 html 示例.以这种方式设置所有子菜单的 ng-click 都处于活动状态,并且它们与主控制器具有相同的范围(除了看起来不像下拉菜单外,一切都很好)

这里是 angularjs-bootstrap 的 dropmenu 结构示例

它的 css 类结构与我的非常不同,因此 angularjs-bootstrap 的下拉菜单"不适用于我的.

有人对我有什么建议吗?请记住,由于我是通过 webservice 调用通过 json 获取导航结构,因此我必须使用递归 angularjs 来创建父/子菜单结构.

如果有人对我在此处生成的指令 html 感到困惑,我可以显示我的自定义指令代码,但除非要求简洁,否则不会显示.我的自定义指令代码仅用于构建导航结构,并将所有指令范围连接到主控制器的范围(即:单击活动),但它没有样式/滚动活动.

*****更新********我创建了一个几乎相同的 plunker 复制.在我的项目中,我从 angularjs 服务获取导航菜单数据,这将对我的服务器上的休息 Web 服务进行 Web 服务调用,但我这里没有,所以我只是为我的每个进行 REST Web 服务调用的服务手动创建了 json.重要的部分是递归指令.您将在下方找到 plunker 项目的链接.有人可以帮我吗?

Plunker 项目 -------------------------------------------------------------

****************更新*****************来自 Charlietfl 的评论说我的导航下拉菜单结构中可以有多个 css 类.我正在尝试使用 angularui-bootstrap.我按照说明将其添加到我的项目中,并基于旧的 plunker 项目创建了一个新的 Plunker 项目,但在导航结构中添加了额外的 dropmenu css 类.这是 Plunker 项目:Plunker 项目

导航元素仍然显示在 DOM 中,但它们不可见.我查看了第一个 ul 元素的 css,它是这样的:

*, *:before, *:after {-moz-box-sizing: 边框框;}*,*:之前,*:之后{-moz-box-sizing: 边框框;}.下拉式菜单 {背景剪辑:填充框;背景色:#FFFFFF;边框:1px 实心 rgba(0, 0, 0, 0.15);边框半径:4px;box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);显示:无;向左飘浮;字体大小:14px;左:0;列表样式:无外无;边距:2px 0 0;最小宽度:160px;填充:5px 0;位置:绝对;顶部:100%;z-索引:1000;}

从官方的bootstrap css文件中获取.不确定为什么它不可见.不确定它是否会有所帮助,但这是 ul 之后的下一个 li 元素的 css

*, *:before, *:after {-moz-box-sizing: 边框框;}*,*:之前,*:之后{-moz-box-sizing: 边框框;}.落下 {位置:相对;}.dropup, .dropdown {位置:相对;}李{行高:20px;}*,*:之前,*:之后{-moz-box-sizing: 边框框;}

请记住,当我添加 angularui-bootstrap 所需的 css 标签时,您必须转到 plunker 页面才能查看更新的代码.要查看不可见的导航元素,您需要使用 Firebug 之类的工具来查看 DOM.

这是我更新中的一些 html 最终输出(来自 DOM)的示例,用于尝试使用 angularui-bootstrap css 类.

<代码>...<li ng-repeat="child in parent.children" class="dropdown ng-scope"><recursive-list-item data-on-node-click="onNodeClickFn(node)" data-parent="child" class="ng-isolate-scope ng-scope"><a class="dropdown-toggle ng-scope ng-binding" href="javascript:void(0)">儿童服装</a>...

我怀疑 angularui-bootstrap 库不工作的原因是因为recursive-list-item.."元素是li"元素的子元素,a"的父元素元素.我的这种预感对吗?

解决方案

这就是我使用的,它有很多非常棒的额外功能.查看用法 $scope.menu 以及展开下拉列表时会发生什么 - 您可以放入标题、分隔符,甚至附加点击功能.请注意,您可以根据需要嵌套任意数量的 ul,尽管切换确实有效,但它没有用,因为单击打开子菜单会隐藏其父菜单.据我所知,如果您想在菜单中进行更深入的嵌套,则需要使用悬停来创建自己的 javascript 处理程序或自定义 css.

现场演示(点击).

js:

var app = angular.module('myApp', ['ui.bootstrap']);app.directive('菜单', function() {返回 {限制:'A',范围: {菜单:'=菜单',cls: '=ngClass'},替换:真的,模板:'<ul><li ng-repeat="菜单项" menu-item="item"></li></ul>',链接:函数(范围,元素,属性){element.addClass(attrs.class);element.addClass(scope.cls);}};});app.directive('menuItem', function($compile) {返回 {限制:'A',替换:真的,范围: {项目:'=菜单项'},模板:'<li active-link><a href={{item.href}}>{{item.title}}</a></li>',链接:函数(范围、元素、属性){如果(范围.item.header){element.addClass('导航头');element.text(scope.item.header);}如果(范围.item.divider){element.addClass('divider');element.empty();}如果(范围.项目.子菜单){element.addClass('下拉菜单');var text = element.children('a').text();element.empty();var $a = $('<a class="dropdown-toggle">'+text+'</a>');element.append($a);var $submenu = $('<div menu="item.submenu" class="dropdown-menu"></div>');element.append($submenu);}如果(范围.项目.点击){element.find('a').attr('ng-click', 'item.click()');}$compile(element.contents())(scope);}};});app.controller('myCtrl', function($scope) {$scope.menu = [{"title": "家","href": "#"},{"title": "关于","href": "关于"},{"title": "历史","href": "关于/历史"},{"title": "联系人","href": "联系方式"},{"title": "其他内容 - 在列表中.(点击这里)",子菜单":[{"header": "示例标题"},{"title": "一些链接","href": "某个/地方"},{"title": "另一个链接","href": "某个/其他/地方"},{分频器":真"},{"标题": "标题 2"},{"title": "再次......一个链接.","href": "错误"},{"title": "Nest Parent",子菜单":[{"title": "再次嵌套","href": "嵌套/再次"},{"title": "我也是","href": "样本/地点"}]}]}];});

嵌套下拉列表更新:

现场演示(点击).

.dropdown-menu .dropdown-menu {边距:0;左:100%;顶部:-5px;}.dropdown-menu li:hover .dropdown-menu {显示:块;}

I have a dilly of a pickle here. I have to get my multi-level navigation menu from a webservice call.

Since my navigation menu can have an infinite amount of submenu's in it, I had to use a recursive directive to build my parent/child navigation structure. Now I am trying to figure out how to turn it into a functional dropmenu structure. I was taking a look at angularui-bootstrap, and they have a Dropdown Toggle, which has some basic dropmenu functionality, but since I used a recursive directive my menu structure already has angularjs generated css classes attached to them. The angularjs-bootstrap dropmenu's have css classes which are different than my angularjs generated classes....behold!

<ul>
    <li ng-repeat="parent in parents" class="ng-scope">
        <recursive-list-item on-node-click="onNodeClickFn(node)" parent="parent" class="ng-isolate-scope ng-scope">
            <a data-ng-click="onNodeClick({node: parent})" href="javascript:void(0)" class="ng-scope ng-binding">Clothes</a>
            <!-- ngIf: parent.children.length > 0 -->
            <ul data-ng-if="parent.children.length &gt; 0" class="ng-scope">
                <!-- ngRepeat: child in parent.children -->
                <li ng-repeat="child in parent.children" class="ng-scope">
                    <recursive-list-item data-on-node-click="onNodeClickFn(node)" data-parent="child" class="ng-isolate-scope ng-scope">
                        <a data-ng-click="onNodeClick({node: parent})" href="javascript:void(0)" class="ng-scope ng-binding">Gortex Jackets</a>
                        <!-- ngIf: parent.children.length > 0 -->
                    </recursive-list-item>
                </li>
                <!-- end ngRepeat: child in parent.children -->
                ...
                ...
                ...
            </ul>
        </recursive-list-item>
    </li>
    <!-- end ngRepeat: child in parent.children -->
...
...
</ul>

That was an example of the html that is generated as the final output for my recursive navigation menu. Having it set up this way all submenu's ng-click's are active, and they have the same scope as the main controller (all is dandy except it doesn't look like a dropmenu)

Here is an example of the dropmenu structure for angularjs-bootstrap

<li class="dropdown" ng-controller="DropdownCtrl">
  <a class="dropdown-toggle">
    Click me for a dropdown, yo!
  </a>
  <ul class="dropdown-menu">
    <li ng-repeat="choice in items">
      <a>{{choice}}</a>
    </li>
  </ul>
</li>

It has very much different css class structure than mine, so angularjs-bootstrap's 'dropdown' won't work with mine.

Does anyone have any suggestions for me? Keep in mind that since I am getting my navigation structure via json via a webservice call I have to use recursive angularjs in order to create the parent/child menu structure.

If anyone is confused about my directive html generated here I can show my custom directive code, but will not unless asked for brevity. My custom directive code works for just building the navigation structure, and keeps all the directive scopes connected to the main controller's scope (ie: click active), but it is not styled/scroll active.

*****UPDATE******** I have created a plunker replication that is almost the same. In my project I was getting my navigation menu data from an angularjs service, which would make a webservice call to a rest webservice on my server, but I don't have that here, so I just manually created the json for each one of my services that makes REST webservice calls. The important part is the recursive directive. Right below you will find the link to the plunker project. Can someone help me out?

Plunker Project -------------------------------------------------------------

*************NEWER UPDATE***************** Comment from Charlietfl that I can just have multiple css classes in my navigation dropmenu structure. I am attempting this with angularui-bootstrap. I followed the instructions for adding this to my project and created a new Plunker project based on the old plunker project, but with the extra dropmenu css classes added to the navigation structure. Here is the Plunker Project: Plunker Project

The navigational elements still show up in the DOM but they are not visible. I looked at the css for the first ul element and it is as such:

*, *:before, *:after {
    -moz-box-sizing: border-box;
}
*, *:before, *:after {
    -moz-box-sizing: border-box;
}
.dropdown-menu {
    background-clip: padding-box;
    background-color: #FFFFFF;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 4px;
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.176);
    display: none;
    float: left;
    font-size: 14px;
    left: 0;
    list-style: none outside none;
    margin: 2px 0 0;
    min-width: 160px;
    padding: 5px 0;
    position: absolute;
    top: 100%;
    z-index: 1000;
}

It was obtained from the official bootstrap css file. Not sure exactly why it's not visible. Not sure if it will help, but here is the css for the very next li element after the ul

*, *:before, *:after {
    -moz-box-sizing: border-box;
}
*, *:before, *:after {
    -moz-box-sizing: border-box;
}
.dropdown {
    position: relative;
}
.dropup, .dropdown {
    position: relative;
}
li {
    line-height: 20px;
}
*, *:before, *:after {
    -moz-box-sizing: border-box;
}

Keep in mind that you have to go to the plunker page to see the updated code starting from when I added the css tags needed for angularui-bootstrap. To see the invisible navigational elements you will need something like Firebug to see the DOM.

Here is an example of some html final output (from the DOM) from my update to try and work with angularui-bootstrap css classes.

...
<li ng-repeat="child in parent.children" class="dropdown ng-scope">
            <recursive-list-item data-on-node-click="onNodeClickFn(node)" data-parent="child" class="ng-isolate-scope ng-scope">
            <a class="dropdown-toggle ng-scope ng-binding" href="javascript:void(0)">Kids Clothes</a>
...

I suspect that the reason the angularui-bootstrap libary is not working is because of the "recursive-list-item.." element that is a child element of the "li" element, and parent element to the "a" element. Is this hunch of mine correct?

解决方案

This is what I use and it has a lot of extra functionality that is pretty sweet. See the usage $scope.menu and what happens when you expand the dropdown - you can put in headers, dividers, and even attach click functions. Note that you can nest as many ul as you want, and though toggle does work, it's useless since clicking to open a submenu will hide its parent. As far as I know, you would need to create your own javascript handler or custom css using hovers if you want deeper nesting in the menu.

Live demo here (click).

<nav>
  <div menu="menu"></div> <!-- the element here doesn't matter -->
</nav>

js:

var app = angular.module('myApp', ['ui.bootstrap']);

app.directive('menu', function() {
  return {
    restrict: 'A',
    scope: {
      menu: '=menu',
      cls: '=ngClass'
    },
    replace: true,
    template: '<ul><li ng-repeat="item in menu" menu-item="item"></li></ul>',
    link: function(scope, element, attrs) {
      element.addClass(attrs.class);
      element.addClass(scope.cls);
    }
  };
});

app.directive('menuItem', function($compile) {
  return {
    restrict: 'A',
    replace: true,
    scope: {
      item: '=menuItem'
    },
    template: '<li active-link><a href={{item.href}}>{{item.title}}</a></li>',
    link: function (scope, element, attrs) {
      if (scope.item.header) {
        element.addClass('nav-header');
        element.text(scope.item.header);
      }
      if (scope.item.divider) {
        element.addClass('divider');
        element.empty();
      }
      if (scope.item.submenu) {
        element.addClass('dropdown');

        var text = element.children('a').text();
        element.empty();
        var $a = $('<a class="dropdown-toggle">'+text+'</a>');
        element.append($a);

        var $submenu = $('<div menu="item.submenu" class="dropdown-menu"></div>');
        element.append($submenu);
      }
      if (scope.item.click) {
        element.find('a').attr('ng-click', 'item.click()');
      }
      $compile(element.contents())(scope);
    }
  };
});

app.controller('myCtrl', function($scope) {
  $scope.menu = [
    {
      "title": "Home",
      "href": "#"
    },
    {
      "title": "About",
      "href": "about"
    },
    {
      "title": "History",
      "href": "about/history"
    },
    {
      "title": "Contact",
      "href": "contact"
    },
    {
      "title": "Other things - in a list. (Click here)",
      "submenu": [
        {
          "header": "Sample Header"
        },
        {
          "title": "Some Link",
          "href": "some/place"
        },
        {
          "title": "Another Link",
          "href": "some/other/place"
        },
        {
          "divider": "true"
        },
        {
          "header": "Header 2"
        },
        {
          "title": "Again...a link.",
          "href": "errrr"
        },
        {
          "title": "Nest Parent",
          "submenu": [
            {
              "title": "nested again",
              "href": "nested/again"
            },
            {
              "title": "me too",
              "href": "sample/place"
            }
          ]
        }
      ]
    }
  ];
});

Update for nested dropdown:

Live demo here (click).

.dropdown-menu .dropdown-menu {
  margin: 0;
  left: 100%;
  top: -5px;
}

.dropdown-menu li:hover .dropdown-menu {
  display: block;
}

这篇关于用于从递归指令生成的菜单结构的 AngularJS 多级下拉菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆