如何定义和渲染子菜单项,使用Aurelia's路由器

How to define and render submenu-items, using Aurelia's Router

本文关键字:Aurelia 使用 路由器 菜单项 何定义 定义      更新时间:2023-09-26

在Aurelia应用程序中,我定义了一个简单的路由,如下所示:

configureRouter(config: RouterConfiguration, router: Router) {
    config.title = 'Marino';
    config.map([
        { route: ['', 'home'], name: 'home', moduleId: './pages/home/home', nav: true, title: 'Home' },
        { route: 'colors', name: 'colors', moduleId: './pages/colors/overview', nav: true, title: 'Colors' }
    ]);
    this.router = router;
}

正如所有例子提到的那样,通过实现repeat.for和href.bind,这是完美的:

<ul class="main-navigation">
    <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
        <a class="btn btn-primary" href.bind="row.href">${row.title}</a> 
    </li>
</ul>

我的场景中的挑战是,我想也将带有子菜单项的路由动态渲染到此菜单。类似这样的东西:

<ul class="main-navigation">
    <!-- the regular 'regular' menu and works just fine -->
    <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
        <a class="btn btn-primary" href.bind="row.href">${row.title}</a> 
    </li>
    <!-- 
        below is the pickle; a different kind of element (non-clickable),
        but with child elements
    -->
    <li class="main-navigation-dropdown">
        <a class="btn btn-primary">Menu with Submenu-items</a> 
        <div class="horizontal-dropdown-menu">
            <a class="btn btn-primary sideline">Submenu 1</a> 
            <a class="btn btn-primary sideline">Submenu 2</a> 
            <a class="btn btn-primary sideline">Submenu 3</a> 
        </div>
    </li>                            
</ul>

让我困惑的是两件事:

  1. 如何正确定义路由配置中的子菜单项
  2. 如何有条件地将每条路线渲染为常规(可单击)路线,或者渲染为具有子菜单的不可单击项目

我查看了RouteConfig文档,但似乎找不到任何关于"嵌套"子例程的信息。Aurelia Getting Started确实提供了关于子路线的信息,但在我看来,所有的示例都与在另一个组件上显示"其他"或第二个菜单有关。

我确信这很琐碎,但我似乎无法解决它。

我用ValueConverter解决了这个问题。这只适用于两个级别,但只要稍作更改,它可以支持更多级别。

路线-settings.parentMenu定义它将出现在哪个菜单下。

export class App {
  configureRouter(config, router) {
    config.title = 'Aurelia';
    config.map([
      { route: ['', 'welcome'],   name: 'welcome',          moduleId: 'welcome',    nav: true, title: 'Welcome',   settings: { icon : 'fa-th-large'} },
      { route: '#',               name: 'admin',            moduleId: 'admin',      nav: true, title: 'Admin',     settings: { icon : 'fa-user' } },
      { route: 'admin/templates', name: 'admin-templates',  moduleId: 'users/users',  nav: true, title: 'Templates', settings: { parentMenu: 'Admin'} }
    ]);
    this.router = router;
  }
}

subMenu.js-将子菜单分组到父下

export class SubMenuValueConverter {
    toView(routerMenuItems) {
        var menuItems = [];
        routerMenuItems.forEach(function (menutItem) {
            if (menutItem.settings.parentMenu) {
                // Submenu children
                var parent = menuItems.find(x => x.title == menutItem.settings.parentMenu);
                // If it doesn't exist, then something went wrong, so not checking 
                parent.children.push(menutItem);                   
            } else {
                 // Just insert.  It should not be there multiple times or it's a bad route
                menuItems[menutItem] = menuItems[menutItem] || [];
                // Create empty children
                menutItem.children = [];
                menuItems.push(menutItem);
            }
        });
        return menuItems;
    }
}

nav bar.html-通过管道将router.navigation导入subMenu值转换器,然后在绑定子菜单时检查子菜单。

<template bindable="router">
  <require from="./subMenu"></require>
  <nav role="navigation">
    <li repeat.for="row of router.navigation | subMenu" class="${row.isActive ? 'active' : ''}">
        <a href="${row.children.length == 0 ? row.href : 'javascript:void(0);'}">
        <i class="fa ${row.settings.icon}"></i>
        <span class="nav-label">${row.title}</span>
        </a>
        <ul if.bind="row.children.length > 0" class="nav nav-second-level">
            <li repeat.for="sub of row.children" class="${sub.isActive ? 'active' : ''}">
                <a href.bind="sub.href">
                    <i class="fa ${subrow.settings.icon}"></i>
                    <span class="nav-label">${sub.title}</span>
                </a>
            </li>
        </ul>
    </li>
  </nav>
</template>

我不知道这是否是正确的方法,但这是我发现的最不"时髦"的方法。如果你使用的是子路由,那么除非你将子路由器注入到你的app.js中(或者在你定义路由的任何地方),并调用configureRouter并传入主路由器的配置,否则这是行不通的。我发现这在主路由器上注册了所有的路由,尽管对我来说这似乎真的很糟糕。

问题是子路由往往使用子路由器。

这在Aurelia中实现了一些非常强大的场景,但带来了一个挑战,即在导航到子路线之前,您的路线配置可能不会出现。

我以前已经处理过这种情况,通过证明一个路由服务,它将路由树作为一个单独的对象,并使用一些辅助方法将其中的一部分转换为Aurelia可以使用的路由配置对象。

然后将其注入到子模块中,它们对其进行查询以配置路由器

导航菜单组件可以查看该树,在加载任何子模块之前构建菜单结构。

Mike Graham也做了类似的事情,但他只是预先设置所有的路线配置(使用路线配置上的"级别"变量来确定菜单继承权):

Aurelia:子路由器路由显示在";主";app.html中的导航栏和子视图<路由器视图>要素

这种方法的缺点是,为了配置路由器,您需要提前了解子模块。(子路由器的部分功能是,它们可以在运行时注册,并且可以在托管应用程序中的任何其他地方"插入",而无需任何配置-这抵消了这一优势)

上述方法的缺点是,你不能很容易地使用路由器生成路由href(因为它使用父路由来计算相对href),你最终不得不自己构建导航模型。