发表于: 2019-03-26 21:07:45

1 640


今天完成的事:

注册路由器与路由定义

要使用路由器,必须先注册来自 @angular/router 包中的 RouterModule。 定义一个路由数组 appRoutes 并把它传给 RouterModule.forRoot() 方法。 它会返回一个模块,其中包含配置好的 Router 服务提供商,以及路由库所需的其它提供商。 一旦启动了应用,Router 就会根据当前的浏览器 URL 进行首次导航。

注意: RouterModule.forRoot 方法是用于注册全应用级提供商的编码模式。要详细了解全应用级提供商,参见单例服务 一章。

src/app/app.module.ts (first-config)
import { NgModule }             from '@angular/core';import { BrowserModule }        from '@angular/platform-browser';import { FormsModule }          from '@angular/forms';import { RouterModule, Routes } from'@angular/router';import { AppComponent }          from './app.component';import { CrisisListComponent }  from './crisis-list/crisis-list.component';import { HeroListComponent }     from './hero-list/hero-list.component';const appRoutes: Routes = [  { path: 'crisis-center', component: CrisisListComponent },  { path: 'heroes', component: HeroListComponent },];@NgModule({  imports: [    BrowserModule,    FormsModule,    RouterModule.forRoot(      appRoutes,      { enableTracing: true } // <-- debugging purposes only    )  ], declarations: [    AppComponent,    HeroListComponent,    CrisisListComponent,  ],  bootstrap: [ AppComponent ]})export class AppModule { }

作为简单的路由配置,将添加配置好的 RouterModule 到 AppModule 中就足够了。 随着应用的成长,你将需要将路由配置重构到单独的文件中,并创建路由模块 - 一种特别的、专门为特性模块的路由器服务的服务模块。

把 RouterModule.forRoot() 注册到 AppModule 的 imports 中,能让该 Router 服务在应用的任何地方都能使用。

添加路由出口

根组件 AppComponent 是本应用的壳。它在顶部有一个标题、一个带两个链接的导航条,在底部有一个路由器出口,路由器会在它所指定的位置上把组件切入或调出页面。就像下图中所标出的:

Shell

路由出口扮演一个占位符的角色,路由组件将会渲染在它的下方。

该组件所对应的模板是这样的:

src/app/app.component.html
<h1>Angular Router</h1><nav>  <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>  <arouterLink="/heroes" routerLinkActive="active">Heroes</a></nav><router-outlet></router-outlet>

定义通配符路由

你以前在应用中创建过两个路由,一个是 /crisis-center,另一个是 /heroes。 所有其它 URL 都会导致路由器抛出错误,并让应用崩溃。

可以添加一个通配符路由来拦截所有无效的 URL,并优雅的处理它们。 通配符路由的 path 是两个星号(**),它会匹配任何 URL。 当路由器匹配不上以前定义的那些路由时,它就会选择这个路由。 通配符路由可以导航到自定义的“404 Not Found”组件,也可以重定向到一个现有路由。

路由器使用先匹配者优先的策略来选择路由。 通配符路由是路由配置中最没有特定性的那个,因此务必确保它是配置中的最后一个路由。

要测试本特性,请往 HeroListComponent 的模板中添加一个带 RouterLink 的按钮,并且把它的链接设置为 "/sidekicks"

src/app/hero-list/hero-list.component.html (excerpt)
<h2>HEROES</h2><p>Get your heroes here</p><button routerLink="/sidekicks">Go to sidekicks</button>

当用户点击该按钮时,应用就会失败,因为你尚未定义过 "/sidekicks" 路由。

不要添加 "/sidekicks" 路由,而是定义一个“通配符”路由,让它直接导航到 PageNotFoundComponent 组件。

src/app/app.module.ts (wildcard)
{ path: '**', component: PageNotFoundComponent }

创建 PageNotFoundComponent,以便在用户访问无效网址时显示它。

ng generate component page-not-found

src/app/page-not-found.component.html (404 component)
<h2>Page not found</h2>

现在,当用户访问 /sidekicks 或任何无效的 URL 时,浏览器就会显示“Page not found”。 浏览器的地址栏仍指向无效的 URL。

设置跳转

应用启动时,浏览器地址栏中的初始 URL 是这样的:

localhost:4200

它不能匹配上任何具体的路由,于是就会走到通配符路由中去,并且显示 PageNotFoundComponent

这个应用需要一个有效的默认路由,在这里应该用英雄列表作为默认页。当用户点击"Heroes"链接或把 localhost:4200/heroes 粘贴到地址栏时,它应该导航到列表页。

首选方案是添加一个 redirect 路由来把最初的相对路径('')转换成期望的默认路径(/heroes)。 浏览器地址栏会显示 .../heroes,就像你直接导航到那里一样。

在通配符路由上方添加一个默认路由。 在下方的代码片段中,它出现在通配符路由的紧上方,展示了这个里程碑的完整 appRoutes

src/app/app-routing.module.ts (appRoutes)
const appRoutes: Routes = [  { path: 'crisis-center', component: CrisisListComponent },  { path: 'heroes',        component: HeroListComponent },  { path: '',   redirectTo: '/heroes', pathMatch: 'full' },  { path: '**', component: PageNotFoundComponent }];

重定向路由需要一个 pathMatch 属性,来告诉路由器如何用 URL 去匹配路由的路径,否则路由器就会报错。 在本应用中,路由器应该只有在完整的 URL等于 ''时才选择 HeroListComponent 组件,因此要把 pathMatch 设置为 'full'

从技术角度说,pathMatch = 'full' 导致 URL 中剩下的、未匹配的部分必须等于 ''。 在这个例子中,跳转路由在一个顶级路由中,因此剩下的URL 和完整的URL 是一样的。

pathMatch 的另一个可能的值是 'prefix',它会告诉路由器:当剩下的URL 以这个跳转路由中的 prefix 值开头时,就会匹配上这个跳转路由。

在这里不能这么做!如果 pathMatch 的值是 'prefix',那么每个URL 都会匹配上 ''

尝试把它设置为 'prefix',然后点击 Go to sidekicks 按钮。别忘了,它是一个无效 URL,本应显示“Page not found”页。 但是,你仍然在“英雄列表”页中。在地址栏中输入一个无效的 URL,你又被路由到了 /heroes。 每一个 URL,无论有效与否,都会匹配上这个路由定义。

默认路由应该只有在整个URL 等于 '' 时才重定向到 HeroListComponent,别忘了把重定向路由设置为 pathMatch = 'full'

要了解更多,参见 Victor Savkin 的帖子关于重定向

“起步阶段”总结

你得到了一个非常基本的、带导航的应用,当用户点击链接时,它能在两个视图之间切换。

你学到了如何:

  • 加载路由库

  • 往壳组件的模板中添加一个导航条,导航条中有一些 A 标签、routerLink 指令和 routerLinkActive 指令

  • 往壳组件的模板中添加一个 router-outlet 指令,视图将会被显示在那里

  • 用 RouterModule.forRoot 配置路由器模块

  • 设置路由器,使其合成 HTML5 模式的浏览器 URL

  • 使用通配符路由来处理无效路由

  • 当应用在空路径下启动时,导航到默认路由

这个初学者应用的结构是这样的:

angular-router-sample

src

app

crisis-list

crisis-list.component.css

crisis-list.component.html

crisis-list.component.ts

hero-list

hero-list.component.css

hero-list.component.html

hero-list.component.ts

page-not-found

page-not-found.component.css

page-not-found.component.html

page-not-found.component.ts

app.component.css
app.component.html
app.component.ts

app.module.ts

main.ts

index.html

styles.css

tsconfig.json

node_modules ...

package.json

下面是当前里程碑中讨论过的文件列表:

<h1>Angular Router</h1><nav>  <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>  <arouterLink="/heroes" routerLinkActive="active">Heroes</a></nav><router-outlet></router-outlet>

里程碑 2:路由模块

在原始的路由配置中,你提供了仅有两个路由的简单配置来设置应用的路由。对于简单的路由,这没有问题。 随着应用的成长,你用到了更多路由器特性,比如守卫、解析器和子路由等,你会很自然地想要重构路由。 我们建议将路由信息移到一个单独的特殊用途的模块,叫做路由模块。

路由模块有一系列特性:

  • 把路由这个关注点从其它应用类关注点中分离出去。

  • 测试特性模块时,可以替换或移除路由模块。

  • 为路由服务提供商(包括守卫和解析器等)提供一个共同的地方。

  • 不要声明组件。

把路由集成到应用中

路由应用范例中默认不包含路由。 要想在使用 Angular CLI 创建项目时支持路由,请为项目或应用的每个 NgModule 设置 --routing 选项。 当你用 CLI 命令 ng new 创建新项目或用 ng generate app 命令创建新应用,请指定 --routing 选项。这会告诉 CLI 包含上 @angular/router 包,并创建一个名叫 app-routing.module.ts 的文件。 然后你就可以在添加到项目或应用中的任何 NgModule 中使用路由功能了。

比如,可以用下列命令生成带路由的 NgModule。

ng generate module my-module --routing

这将创建一个名叫 my-module-routing.module.ts 的独立文件,来保存这个 NgModule 的路由信息。 该文件包含一个空的 Routes 对象,你可以使用一些指向各个组件和 NgModule 的路由来填充该对象。

将路由配置重构为路由模块

在 /app 目录下创建一个 AppRouting 模块,以包含路由配置。

ng generate module app-routing --module app --flat

导入 CrisisListComponentHeroListComponent 和 PageNotFoundCompponent 组件,就像 app.module.ts 中那样。然后把 Router 的导入语句和路由配置以及 RouterModule.forRoot 移入这个路由模块中。

把 Angular 的 RouterModule添加到该模块的 exports 数组中,以便再次导出它 。 通过再次导出 RouterModule,当在 AppModule 中导入了 AppRoutingModule 之后,那些声明在 AppModule 中的组件就可以访问路由指令了,比如 RouterLink 和 RouterOutlet

做完这些之后,该文件变成了这样:

src/app/app-routing.module.ts
  1. import { NgModule }              from '@angular/core';
  2. import { RouterModule, Routes }  from '@angular/router';
  3.  
  4. import { CrisisListComponent }   from './crisis-list/crisis-list.component';
  5. import { HeroListComponent }     from './hero-list/hero-list.component';
  6. import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
  7.  
  8. const appRoutes: Routes = [
  9.  { path: 'crisis-center', component: CrisisListComponent },
  10.  { path: 'heroes',        component: HeroListComponent },
  11.  { path: '',   redirectTo: '/heroes', pathMatch: 'full' },
  12.  { path: '**', component: PageNotFoundComponent }
  13. ];
  14.  
  15. @NgModule({
  16.  imports: [
  17.    RouterModule.forRoot(
  18.      appRoutes,
  19.      { enableTracing: true } // <-- debugging purposes only
  20.    )
  21.  ],
  22.  exports: [
  23.    RouterModule
  24.  ]
  25. })
  26. export class AppRoutingModule {}

接下来,修改 app.module.ts 文件,从 imports 数组中移除 RouterModule.forRoot

src/app/app.module.ts
  1. import { NgModule }       from '@angular/core';
  2. import { BrowserModule }  from '@angular/platform-browser';
  3. import { FormsModule }    from '@angular/forms';
  4.  
  5. import { AppComponent }     from './app.component';
  6. import { AppRoutingModule } from './app-routing.module';
  7.  
  8. import { CrisisListComponent }   from './crisis-list/crisis-list.component';
  9. import { HeroListComponent }     from './hero-list/hero-list.component';
  10. import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
  11.  
  12. @NgModule({
  13.  imports: [
  14.    BrowserModule,
  15.    FormsModule,
  16.    AppRoutingModule
  17.  ],
  18.  declarations: [
  19.    AppComponent,
  20.    HeroListComponent,
  21.    CrisisListComponent,
  22.    PageNotFoundComponent
  23.  ],
  24.  bootstrap: [ AppComponent ]
  25. })
  26. export class AppModule { }

本章稍后的部分,你将创建一个多路由模块,并揭示你为何必须以正确的顺序导入那些路由模块

应用继续照常运行,你可以把路由模块作为将来每个模块维护路由配置的中心位置。



目标:

继续学习

问题:

进度慢

收获:

加油




返回列表 返回列表
评论

    分享到