【问题标题】:AngularJS multilevel dropdown menu for a menu structure generated from a recursive directiveAngularJS 多级下拉菜单,用于从递归指令生成的菜单结构
【发布时间】:2013-11-24 19:35:22
【问题描述】:

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

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

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

这是作为递归导航菜单的最终输出生成的 html 示例。以这种方式设置后,所有子菜单的 ng-click 都处于活动状态,并且它们与主控制器具有相同的作用域(除了看起来不像下拉菜单之外,所有这些都是花花公子)

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

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

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

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

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

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

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

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

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

*, *: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;
}

它是从官方的引导 css 文件中获得的。 不确定为什么它不可见。 不确定它是否有帮助,但这是 ul 之后的下一个 li 元素的 css

*, *: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;
}

请记住,从我添加 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)">Kids Clothes</a>
...

我怀疑 angularui-bootstrap 库不工作的原因是“recursive-list-item..”元素是“li”元素的子元素,而父元素是“a”元素。我的这个预感正确吗?

【问题讨论】:

  • 不明白为什么类很重要。一个元素可以有许多类。您也可以修改甚至不使用 ui dropwdown 中的 CSS
  • 我遇到了类似的问题并用这个解决了它:stackoverflow.com/questions/11854514/… 它可以帮助你制作 angular-bootstrap 结构。从服务中刷新时,我遇到了项目重复的问题,但这是一个新问题。
  • Charlietfl,你好,我没有考虑每个元素有多个 css 类。我根据文档angular-ui.github.io/bootstrap/#/getting_started 添加了正确的类,现在整个事情都是不可见的。元素结构存在于 DOM (firebug) 中,但您在导航结构中看不到任何类别。我添加了“ui.bootstrap”作为我控制器模块的依赖项,并将 ui-bootstrap-tpls-0.7.0.min.js 添加到我的 index.html 页面,我还添加了 bootstrap.css,甚至添加了额外的 css根据文档行。有什么想法吗?
  • Charlietfl,我创建了一个单独的 Plunker 项目,它只是将 css 类添加到必要的 html 中。 plnkr.co/edit/f0H3Yg?p=preview

标签: javascript angularjs angularjs-directive angularjs-bootstrap


【解决方案1】:

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

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

嵌套下拉菜单更新:

Live demo here (click).

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

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

【讨论】:

  • 好的,我查看了你的演示。当您单击以打开子菜单时,父菜单会关闭。似乎图书馆应该为子菜单处理这个......
  • @thebravedave 是的,不幸的是,我几乎完全肯定这不是故意的。要考虑的一件事是,像这样的嵌套子菜单是一种不好的做法。在一个级别之后,这对用户来说是一种压力(我还需要点击多少!?)。不过,您可以在这里轻松创建自己的功能。
  • 最好你也会有某种悬停功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
相关资源
最近更新 更多