返回博客主页
← 所有文章

使用嵌套路由出口与 NativeScript 和 Angular

2017 年 3 月 7 日 — 作者:Sebastian Witalec

简介

有时我们只需要应用程序页面的一部分从一个组件更改为另一个组件,而我们希望页面的其余部分保持原样。

想象一下,你有一个带有多个标签的标签视图,并且希望每个标签独立于整个页面进行导航。你可以尝试通过在每个标签中添加所有必需的组件来解决这个问题,然后使用一个巧妙的 *ngIf 来显示和隐藏它们。但是,一旦你添加了几个这样的标签,你就会意识到这并不是一个巧妙的方法。

这时,Angular 带着命名路由出口的魔力来拯救你。其理念是,你可以将多个路由出口添加到你的页面,为每个出口指定一个唯一的名称,然后只需提供路由出口的名称和目标路径即可导航到每个出口。

在我们深入研究代码之前,我假设你已经掌握了如何创建 Angular 组件、服务以及如何将它们添加到 @NgModule 的核心知识。


计划

我们将构建一个应用程序,该应用程序只有一个页面(HomeComponent),它将包含一个带有两个标签的标签视图。一个标签将显示一个狗列表(DogsComponent),当你点击其中一只狗时,你将被重定向到该狗的详细信息视图(DogDetailsComponent)。第二个标签非常相似,只是这次我们将显示一个猫列表(CatsComponent)和猫的详细信息(CatDetailsComponent)。

让我们动手实践

我们将按照以下步骤实施我们的解决方案
- 在根目录中选择正确的路由出口。
- 配置猫和狗的路由。
- 使用 nsRouterLink 构建猫的导航。
- 使用代码构建狗的导航。

选择根路由出口

命名路由出口只适用于根目录下的路由出口,因为 page-router-outlet 每次我们调用 navigate 时都会导航到一个完全不同的页面。
因此,我们需要转到 app.component.html 并将其更改为
<router-outlet></router-outlet>

配置路由

由于我们知道我们需要哪些组件,我们将从配置路由开始(参见 app.routing.ts)。

我们需要为猫和狗相关的组件配置路由,它们被配置为首页路由的子路由

此外,每个子路由都需要一个outlet属性,该属性将指示该路由属于哪个路由出口。因此,每个猫组件都应该分配到 'catoutlet',而每个狗组件都应该分配到 'dogoutlet'。

[注意] 出口名称不需要包含 outlet 一词,但名称只能包含字母数字字符。例如,'cat-outlet' 将不起作用。

此外,我们可以选择应用程序首次加载时要显示的组件。这可以通过更改默认路由上的 redirectTo 属性来实现。在我们的例子中,我们希望首先显示 CatsComponent 和 DogsComponent,因此我们的redirectTo应该是'/home/(catoutlet:cats//dogoutlet:dogs)'

[注意] 如果你需要默认导航到 CatDetails,可以将 redirectTo 设置为'/home/(catoutlet:cats/Betsy//dogoutlet:dogs)''/home/(catoutlet:cats/Betsy)'
以下是配置路由的方式
const routes: Routes = [
  { path: '', redirectTo: '/home/(catoutlet:cats//dogoutlet:dogs)', pathMatch: 'full' },
  { path: 'home', component: HomeComponent, children: [
    { path: 'cats', component: CatsComponent, outlet: 'catoutlet'},
    { path: 'cats/:name', component: CatDetailsComponent, outlet: 'catoutlet'},
    { path: 'dogs', component: DogsComponent, outlet: 'dogoutlet'},
    { path: 'dogs/:id', component: DogDetailsComponent, outlet: 'dogoutlet'}
  ]}
];

配置路由出口

现在让我们实现 HomeComponent。
这个组件将作为两个路由出口的简单容器。因此,HomeComponent 类相当空。
所有的魔力都将在HomeComponent 模板中发生,该模板应该包含一个带有两个命名路由出口实例的标签视图。
命名路由出口就像设置 name 属性一样简单。根据路由配置,我们需要将路由出口命名为 "catoutlet" 和 "dogoutlet"。


TabView 示例

<ActionBar title="Nested Navigation" class="action-bar"></ActionBar>
<TabView class="tab-view">
  <StackLayout *tabItem="{title: 'Cats'}">
    <router-outlet name="catoutlet"></router-outlet>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Dogs'}">
    <router-outlet name="dogoutlet"></router-outlet>
  </StackLayout>
</TabView>

以下是你的预期结果

TabView example

GridLayout 示例

TabView 每次只会显示一个视图。要显示猫和狗,我们可以像这样将它们都放在网格中
<GridLayout rows="*, 2*" class="page">
  <StackLayout row="0">
    <Label text="Cats" class="h2 text-center action-bar"></Label>
    <router-outlet name="catoutlet"></router-outlet>
  </StackLayout>
  <StackLayout row="1">
    <Label text="Dogs" class="h2 text-center action-bar"></Label>
    <router-outlet name="dogoutlet"></router-outlet>
  </StackLayout>
</GridLayout>
以下是你的预期结果:
GridLayout 示例


[nsRouterLink] 猫的导航

现在让我们实现 CatsComponent 和 CatDetailsComponent。

CatsComponent 模板非常简单。它应该有一组按钮,每个按钮都导航到 CatDetailsComponent,并使用猫的名称作为参数。
为此,我们需要使用nsRouterLink,并使用以下参数
- 父路由出口的路径(在本例中是'/home')。
- 出口配置,包括我们要更新的出口名称(在本例中是catoutlet),以及出口的路由(在本例中是['cats', 'cats-name'])。
像这样
<Button 
  text="Show Scratchy"
  [nsRouterLink]="['/home', { outlets: { catoutlet: ['cats', 'Scratchy'] } } ]">
</Button>
<Button 
  text="Show Hissy"
  [nsRouterLink]="['/home', { outlets: { catoutlet: ['cats', 'Hissy'] } } ]">
</Button>
<Button
  text="Show Mystique"
  [nsRouterLink]="['/home', {outlets: { catoutlet: ['cats', 'Mystique']}}]">
</Button>
现在我们只需要创建 CatsDetailComponent,它将显示猫的名称并允许用户导航回去。

要提取名称,我们应该将ngOnInit()与注入的 ActivateRoute 添加到CatDetailsComponent 类
ngOnInit() {
  this.name = this.route.snapshot.params['name'];
}
然后,CatDetailsComponent 模板应该相当简单。
我们需要
- 使用一些消息显示猫的名称。
<Label [text]="'Hello ' + name"></Label>
<Label text="Did you knock the plants off the window sill?" textWrap="true"></Label>
- 显示一个返回按钮,它将使用 nsRouterLink 导航回去。
<Button 
  text="Go Back"
  [nsRouterLink]="['/home', { outlets: { catoutlet: ['cats'] } } ]">
</Button>
瞧!这就是我们使用nsRouterLink在 catoutlet 中导航所需的一切。


狗的代码导航

对于应用程序的狗部分,我们将使用代码进行导航,方法是使用@angular/router中的Router服务的navigate函数(你应该知道流程 - 只需导入它,然后在构造函数中注入它)。

DogsComponent 类中,我们需要一个函数来获取狗的 ID,然后导航到 DogDetailsComponent。
navigate()采用与nsRouterLink完全相同的参数,只是这次我们将使用dogoutlet
navigateToDetails(id: number) {
  this.router.navigate([
    '/home', { outlets: { dogoutlet: ['dogs', id] } }
  ])
}
然后,在DogsComponent 模板中,我们只需要创建一个列表视图,并添加一个点击事件,该事件将调用navigateToDetails
<ListView [items]="dogs" class="list-group" height="100%">
  <template let-item="item" >
    <Label 
      (tap)="navigateToDetails(item.id)"
      [text]="item.name"
      class="list-group-item">
    </Label>
  </template>
</ListView>

现在,要完成示例,我们只需要在DogDetailsComponent 类中添加一个goBack()函数,该函数将再次使用@angular/router中的Router
goBack() {
  this.router.navigate([
    '/home', { outlets: { dogoutlet: ['dogs'] } }
  ])
}
然后,在DogsDetailsComponent 模板中,我们只需要
- 显示有关狗的信息。
<Label [text]="dog.name"></Label>
<Label text="Who is the good doggie?"></Label>
<Label [text]="'Max Weight:' + dog.maxWeight + ' inches'"></Label>
<Label [text]="'Max Height:' + dog.maxHeight + ' pounds'"></Label>
- 添加返回按钮。
<Button text="Go Back" (tap)="goBack()"></Button>

导航多个出口

你也可以在一个调用中导航到多个出口。
只需在 outlet 参数中提供要更新的所有出口即可。
例如,我们可以添加一个重置按钮,该按钮将导航到猫和狗视图的默认值,只需这样做即可

<Button
  text="Reset"
  [nsRouterLink]="['/home', { outlets: { catoutlet: ['cats'], dogoutlet: ['dogs'] } } ]">
</Button>

代码

你可以在我的 github 中找到整个解决方案
nativescript-nested-router