八、Angular 路由及其组件的处理

我们来到了单页应用 ( SPAs )最重要的部分之一:路线的使用。正如您在第 3 章中看到的理解 Angular 6 的核心概念,Angular 框架为处理应用路由提供了一个强大的工具:@ angular/router dependency。

在接下来的几节中,您将学习如何使用其中的一些功能,例如子视图,以及如何创建母版页。此外,我们将开始构建应用的视觉领域,用 HTML 标记填充模板。

在本章中,我们将涵盖以下主题:

  • 准备基线代码
  • 向应用添加组件
  • 处理 Angular 路线
  • 为详细信息页面配置子路由
  • 建筑前端视图

准备基线代码

现在,我们需要准备我们的基线代码,这个过程非常类似于前面章节中的过程。让我们遵循以下步骤:

  1. chapter-08文件夹复制所有内容。

  2. 将文件夹重命名为chapter-08

  3. 删除storage-db文件夹。

现在,让我们对docker-compose.yml文件进行一些更改,以适应新的数据库和服务器容器。

  1. 打开docker-compose.yml并用以下代码替换内容:
version: "3.1"
services:
    mysql:
      image: mysql:5.7
      container_name: chapter-08-mysql
      working_dir: /application
      volumes:
        - .:/application
        - ./storage-db:/var/lib/mysql
      environment:
        - MYSQL_ROOT_PASSWORD=123456
        - MYSQL_DATABASE=chapter-08
        - MYSQL_USER=chapter-08
        - MYSQL_PASSWORD=123456
      ports:
        - "8083:3306"
    webserver:
      image: nginx:alpine
      container_name: chapter-08-webserver
      working_dir: /application
      volumes:
        - .:/application
        - ./phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default
          .conf
      ports:
        - "8081:80"
    php-fpm:
      build: phpdocker/php-fpm
      container_name: chapter-08-php-fpm
      working_dir: /application
      volumes:
        - ./Server:/application
        - ./phpdocker/php-fpm/php-ini-
          overrides.ini:/etc/php/7.2/fpm/conf.d/99-overrides.ini

请注意,我们更改了容器名称、数据库和 MySQL 用户:

  • container_name: chapter-08-mysql
  • container_name: chapter-08-webserver
  • container_name: chapter-08-php-fpm
  • MYSQL_DATABASE=chapter-08
  • MYSQL_USER=chapter-08

  • 添加我们对 Git 源代码控制所做的更改。打开您的终端窗口,键入以下命令:

 git add .
 git commit -m "Initial commit chapter 08"

向我们的应用添加组件

现在,我们将继续向我们的应用添加更多的组件。我们必须记住,在应用摘要中,我们为自行车列表定义了一个页面,该页面引用了我们的应用编程接口的api/bikes端点;此外,我们将有一个自行车详细信息页面,该页面引用了api/bikes/id 端点,包含所选自行车的详细信息。并且,我们将对api/builders端点进行同样的操作。

所以,让我们开始创建组件:

  1. 打开./Client/src/app内的终端窗口,输入以下命令:
 ng g c pages/bikes/bike-detail

在上一个命令的末尾,您将在bikes模块中看到以下结构:

Bikes module structure

前面的命令将创建一个根bikes文件夹来存储与bikes端点相关的每个模块;这种模式允许我们有一个模块化的应用,其中每个新特性(例如,bikes-detailbike-list)将以相同的方式组织。

例如,我们可以添加一个新的库存模块,该模块将在其自己的模块(inventory.module.ts)中创建,并存储在bikes模块目录中。

认为这是一个很好的实践,并保持您的模块和组件以这种方式组织;避免在同一个文件夹的根目录下对多个组件进行分组。这可以防止您的代码变成意大利面条代码。

  1. 打开./Client/src/app内的终端窗口,输入以下命令:
 ng g c pages/builders/builder-detail

现在,您将看到builders模块的以下结果:

Builders folder structure

请注意,builders模块(在./Client/src/app/pages/builders/builders.module.ts)已更新,新的构建器细节组件已添加到声明属性中,如以下突出显示的代码所示:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BuildersRoutingModule } from './builders-routing.module';
import { BuildersComponent } from './builders.component';
import { BuilderDetailComponent } from './builder-detail/builder-detail.component';
@NgModule({
    imports: [
        CommonModule,
        BuildersRoutingModule
    ],
    declarations: [BuildersComponent, BuilderDetailComponent]
    })
export class BuildersModule { }

最棒的是 Angular CLI 足够聪明,可以将新创建的组件添加到它所属的模块中。当我们创建bike-detail组件时,也做了同样的事情。

处理 Angular 路线

此时,我们将继续开发我们的示例应用。在前一章中,我们为前端应用创建了一些 Angular 组件,但是在我们编写每一个组件的内容之前,我们将创建一些路由。

在我们深入研究代码之前,了解 Angular 路由的工作原理非常重要。

当您点击一个链接或转到一个网址(例如,http://localhost:4200/bikes)时,Angular 路由:

  1. 检查浏览器网址。
  2. 查找对应于该网址的路由状态。
  3. 如果路由保护是在路由状态下定义的,则应用路由保护。
  4. 激活相应的 Angular 组件以显示页面。

此外,每条路由可以包含以下属性:

  • 路径:字符串;与网址匹配的路径
  • patchMatch :字符串;如何匹配网址
  • 组件:类引用;激活路线时要激活的组件
  • 重定向至:字符串;激活此路由时要重定向到的 URL
  • 数据:分配给路线的静态数据
  • 解析:动态数据,解析后与数据合并
  • 子路线:子路线

在接下来的部分中,我们将看到为我们的应用创建路由的两种方法,其中一种使用子路由。

You can read more about routes in the official documentation at https://angular.io/guide/router.

创建身份验证路由

让我们看一下身份验证模块的当前文件夹结构:

Auth module folder structure

在前面的截图中,注意我们只在auth文件夹的根里面创建了一个路由文件;我们没有在auth文件夹中的任何其他文件夹/模块中包含任何路由文件,例如loginregisterlogout。这是因为我们将使用auth-routing.module.ts文件来创建与身份验证相关的所有路由。

现在,我们将创建身份验证路由:

  1. 打开./Client/src/app/pages/auth目录下的 auth-routing.module.ts 文件,在Router import后添加以下代码块:
 // Auth Routes Imports
 import { RegisterComponent } from  './register/register.component';
 import { LoginComponent } from  './login/login.component';
 import { LogoutComponent } from  './logout/logout.component';
  1. 现在,在routes 常量内添加以下代码:
 const  routes:  Routes  = [
        { path:  'register', component:  RegisterComponent },
        { path:  'login', component:  LoginComponent },
        { path:  'logout', component:  LogoutComponent }
 ];

现在,让我们从home模块开始,研究应用的其他路线。

创建主路由

现在,我们将创建home路线,如下所示:

  1. 打开./Client/src/app/pages/home/home-routing.module.tsimport组件:
 // Home Routes Imports
 import { HomeComponent } from  './home.component';
  1. 打开./Client/src/app/pages/home/home-routing.module.ts并在routes常量内添加以下路线对象:
 const  routes:  Routes  = [
        { path: " '', component:  HomeComponent }
 ];

由于我们的主页非常简单,它只包含一条路线;稍后,在其他模块中,您将看到更复杂的路由。

为详细信息页面配置子路由

我们将使用另一种方法在 Angular 中创建建筑者和自行车路线。我们将使用子路由,也称为嵌套视图。

当您使用多个子对象时,小心路由对象顺序非常重要。

当路由收到一个 URL 时,它会按顺序跟随内容,从数组的第一个元素开始;如果它找到了与完整 URL 的匹配,它将停止并实例化相应的组件。

在接下来的部分中,您将看到如何实现一个众所周知的名为主细节页面的 UI 模式。我们将制作另一个组件来帮助我们组织文件夹结构。

添加建筑商子路线

让我们在前端应用中为以下视图创建子路由:

  • builders-list
  • builders-detail

  • 打开./Client/src/app/pages/builders/builders-routing.module.tsimport组件:

imports
import { BuilderDetailComponent } from './builder-detail/builder-detail.component';
import { BuilderListComponent } from './builder-list/builder-list.component';
  1. 仍然在./Client/src/app/pages/builders/builders-routing.module.ts中,在routes常量内添加以下routes对象:
const routes: Routes = [
{
    path: 'builders',
    children: [
    {
    path: '',
component: BuilderListComponent
},
    {
    path: ':id',
    component: BuilderDetailComponent
    }
    ]
    }
];

在前面的代码片段中,您会注意到两件不同的事情:一件是我们使用的是children路由数组属性,另一件是一个新组件,名为BuilderListComponent。那么,让我们创建这个新组件。

  1. ./Client/src/app 内,键入以下命令:
 ng g c pages/builders/builder-list

您将在builders模块中看到以下结构:

Builders module with builder-list module

添加摩托车手子路线

让我们在前端应用中为以下视图创建子路由:

  • bike-list
  • bike-detail

现在我们将导入文件顶部的组件:

  1. 打开./Client/src/app/pages/bikes/bikes-routing.module.tsimport组件:
// Bikes Routes Imports
 import { BikeDetailComponent } from  './bike-detail/bike-detail.component';
 import { BikeListComponent } from  './bike-list/bike-list.component';
  1. 仍然在./Client/src/app/pages/bikes/bikes-routing.module.ts中,在routes常量中添加以下路线对象:
 const  routes:  Routes  = [
   { path:  'bikes',
     children: [
    {
      path:  "'',
      component:  BikeListComponent
    },{
      path:  ':id',
      component:  BikeDetailComponent
    }]
  }
 ];

现在,是时候创建新的BikeListComponent了,就像我们之前用Builders做的那样。

  1. ./Client/src/app中,键入以下命令:
 ng g c pages/bikes/bike-list

您将在bikes模块中看到以下结构:

Bikes module with bike-list module

重构 app.component.html

正如我们之前讨论过的,现在让我们的观点更有吸引力一点。

让我们添加导航组件。目前,我们不会将内容放入该文件;我们稍后会这样做。

打开./Client/src/app/app.component.html并用以下内容替换代码:

 <app-nav></app-nav>
 <router-outlet></router-outlet>
     <footer  class="footer">
     <div  class="pl-3">
         <span  class="text-muted">2018 &copy; All Rights
         Reserved</span>
     </div>
     </footer>

请注意,前面的代码现在没有任何内容,只是页脚注释的简单标记。在下一节中,您将看到如何添加更有趣的内容。

建筑前端视图

我们用 Angular 创建的大多数组件都会收到一个 HTML 模板,正如您在前面几章中看到的:

@Component({
        selector:  'app-nav',
        templateUrl:  './nav.component.html',
        styleUrls: ['./nav.component.scss']
})

框架所具有的这种能力——创建与其各自视图相连接的组件——非常棒。它具有开箱即用的特性。它还包括一个完全独立于应用其余部分的样式表,正如您在前面的代码中看到的那样。

在下一步中,我们将添加必要的 HTML 来给我们的应用一个愉快的外观,就像我们在前面几章中建议的那样。

创建导航组件

打开./Client/src/app/layout/nav/nav.component.html并用以下代码的nav works字符串替换该段落:

<header>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" [routerLink]="['/']" (click)="setTitle('Custom Bikes Garage')">Custom Bikes Garage</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse"     data-target="#navbarCollapse" aria-controls="navbarCollapse"
    aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarCollapse">
    <ul class="navbar-nav ml-auto">
    <li class="nav-item">
    <a class="nav-link" [routerLink]="['/bikes']" 
    routerLinkActive="active" (click)="setTitle('Bikes')">Bikes</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" [routerLink]="['/builders']"
             routerLinkActive="active"
         (click)="setTitle('Builders')">Builders</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" [routerLink]="['/login']"
     routerLinkActive="active" (click)="setTitle('Login')">Login</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" [routerLink]="['/register']"
     routerLinkActive="active"
         (click)="setTitle('Register')">Register</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" [routerLink]="['/logout']"
     routerLinkActive="active">Logout</a>
    </li>
    </ul>
    </div>
</nav></header>

关于前面的代码,有两件重要的事情:

  • 我们正在使用routerLink属性;在本章的后面,您将看到如何使用它。
  • 我们正在使用Title服务设置带有<title>标签的标题页面,这是 Angular 提供的内置服务。当我们构建一个 SPA 时,我们需要使用这个资源来给我们的视图命名;没有它,我们应用中的所有页面都将具有相同的客户端名称。请记住,Title标签是在我们第一次使用 Angular CLI 创建应用时设置的,它将接收我们定义的应用名称。

让我们更新<title>标签,如下所示:

  1. 打开./Client/src/app/layout/nav/nav.component.ts 并添加以下代码:
import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
@Component({
    selector: 'app-nav',
        templateUrl: './nav.component.html',
        styleUrls: ['./nav.component.scss']
    })
    export class NavComponent implements OnInit {
    public constructor(private titleTagService: Title ) { }
    public setTitle( pageTitle: string) {
    this.titleTagService.setTitle( pageTitle );
    }
    ngOnInit() {
    }
}
  1. 打开./Client/src/app/app.module.ts并将Title导入添加到文件顶部:
import { BrowserModule, Title } from '@angular/platform-browser';
  1. 现在,将Title提供程序添加到@ngModules提供程序:
providers: [
Title
],

所以,如果我们再次查看同一个网址(http://localhost:4200/)的浏览器,我们可以看到一个链接列表,我们可以浏览它们。结果将类似于下面的截图:

Navigation links

不要担心我们标记中的类名;在本书的后面,我们将添加一些样式表,包括一些 Bootstrap 组件。

创建主视图和模板

打开./Client/src/app/pages/home/home.component.html并用以下代码的home works字符串替换该段落:

<main role="main">
<div class="jumbotron">
<div class="container text-center">
<h1 class="display-3 ">Custom Bikes Garage</h1>
<p>Motorcycle builders and road lovers</p>
<p>
<a class="btn btn-primary btn-lg" [routerLink]="['/register']"role="button">Register</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum
nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
</p>
<p>
<a class="btn btn-secondary" href="#" role="button">View details &raquo;</a>
</p>
</div>
<div class="col-md-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum
nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
</p>
<p>
<a class="btn btn-secondary" href="#" role="button">View details &raquo;</a>
</p>
</div>
<div class="col-md-4">
<h2>Heading</h2>
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod
semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet
risus.</p>
<p>
<a class="btn btn-secondary" href="#" role="button">View details &raquo;</a>
</p>
</div>
</div>
</div>
</main>

创建自行车路由插座

打开./Client/src/app/pages/bikes/bikes.component.html ,用以下代码替换bikes works字符串:

<router-outlet></router-outlet>

创建自行车列表视图和模板

打开./Client/src/app/pages/bikes/bike-list/bike-list.component.html并用以下代码的bike-list工作字符串替换该段落:

<main role="main">
<div class="py-5 bg-light">
<div class="container">
<form>
<div class="form-group row">
<label for="search" class="col-sm-2 col-form-label">Bike List</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="search"placeholder="Search">
</div>
<div class="col-sm-2">
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle btn-block" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Filter
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#">Action</a>
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="https://dummyimage.com/640x480/717171/fff.jpg&text=placeholder-image" alt="Card image cap">
<div class="card-body">
<p>Model | year</p>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
    <div class="btn-group">
    <button routerLink="/bikes/1" type="button" class="btn btn-sm
      btn-    outline-primary">View</button>
    <button type="button" class="btn btn-sm btn-outline-
        primary">Vote</button>
</div>
<small class="text-muted">4 ratings</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top"src="https://dummyimage.com/640x480/717171/fff.jpg&text=placeholder-image" alt="Card image cap">
<div class="card-body">
<p>Model | year</p>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is
a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button routerLink="/bikes/2" type="button" class="btn btn-sm btn-outline-primary">View</button>
<button type="button" class="btn btn-sm btn-outline-primary">Vote</button>
</div>
<small class="text-muted">9 ratings</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="https://dummyimage.com/640x480/717171/fff.jpg&text=placeholder-image" alt="Card image cap">
<div class="card-body">
<p>Model | year</p>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is
a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button routerLink="/bikes/3" type="button" class="btn btn-sm btnoutline-primary">View</button>
<button type="button" class="btn btn-sm btn-outline-primary">Vote</button>
</div>
<small class="text-muted">5 ratings</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>

创建自行车详图和模板

打开./Client/src/app/pages/bikes/bike-detail/bike-detail.component.html并用以下代码的bike-detail工作字符串替换该段落:

<main role="main">
<div class="py-5">
<div class="container">
<div class="row">
<div class="col-md-4">
   <img class="card-img-top"  
   src="https://dummyimage.com/340x280/717171/fff.jpg&text=placeholder-
   image" alt="Card image cap">
</div>
<div class="col-md-8">
<div class="card">
<div class="card-body">
    <h5 class="card-title">Card title | Year | Ratings</h5>
    <p class="card-text">Some quick example text to build on the card
     title and make up the bulk of the card's content.</p>
</div>
    <div class="card-header">
        Builder Name
    </div>
<div class="card-header">
    Featured items
</div>
<ul class="list-group list-group-flush">
    <li class="list-group-item">Cras justo odio</li>
    <li class="list-group-item">Dapibus ac facilisis in</li><li
         class="list-group-item">Vestibulum at eros</li>
</ul>
    <div class="card-body">
        <a href="#" class="card-link">Vote</a>
    </div>
</div>
</div>
</div>
</div>
</div>
</main>

创建建筑路由插座

打开./Client/src/app/pages/builders/builders.component.html ,用以下代码替换builders作品字符串:

<router-outlet></router-outlet>

创建生成器列表视图和模板

打开./Client/src/app/pages/builders/builder-list/builder-list.component.html并用以下代码替换该段落:

<main role="main">
<div class="py-5 bg-light">
<div class="container">
<div class="card-deck mb-3 text-center">
<div class="card mb-4 box-shadow">
<div class="card-header">
<h4 class="my-0 font-weight-normal">Builder Name</h4>
</div>
<div class="card-body">
    <p class="mt-3 mb-4">
    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quam
     aspernatur sit cum necessitatibus.
    </p>
    <button routerLink="/builders/1" type="button" class="btn btn-lg     btn-block btn-outline-primary">View Bikes</button>
</div>
<div class="card-footer text-muted">
City/State
</div>
</div>
<div class="card mb-4 box-shadow">
<div class="card-header">
    <h4 class="my-0 font-weight-normal">Builder Name</h4>
</div>
<div class="card-body">
    <p class="mt-3 mb-4">
    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quam
     aspernatur sit cum necessitatibus.
</p>
    <button routerLink="/builders/2" type="button" class="btn btn-lg
     btn-block btn-outline-primary">View Bikes</button>
</div>
<div class="card-footer text-muted">
City/State
</div>
</div>
<div class="card mb-4 box-shadow">
<div class="card-header">
    <h4 class="my-0 font-weight-normal">Builder Name</h4>
</div>
<div class="card-body">
    <p class="mt-3 mb-4">
    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quam
     aspernatur sit cum necessitatibus.
</p>
    <button routerLink="/builders/3" type="button" class="btn btn-lg
     btn-block btn-outline-primary">View Bikes</button>
</div>
<div class="card-footer text-muted">
City/State
</div>
</div>
</div>
</div>
</div>
</main>

创建构建器详细视图和模板

打开./Client/src/app/pages/builders/builder-detail/builder-detail.component.html并用以下代码的builder-detail工作字符串替换该段落:

<main role="main">
<div class="py-5">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body">
    <h5 class="card-title">Builder Name</h5>
    <p class="card-text">Some quick example text to build on the card     title and make up the bulk of the card's content.</p>
</div>
<div class="card-header">
    Featured Bikes
</div>
    <ul class="list-group list-group-flush">
    <li class="list-group-item">Cras justo odio</li>
    <li class="list-group-item">Dapibus ac facilisis in</li>
    <li class="list-group-item">Vestibulum at eros</li>
    </ul>
</div>
</div>
</div>
</div>
</div>
</main>

创建登录视图和模板

打开./Client/src/app/pages/auth/login/login.component.html并用以下代码的login工作字符串替换该段落:

<main role="main">
<div class="container">
<form class="form-signin">
<div class="text-center mb-4">
    <h1 class="h3 mt-3 mb-3 font-weight-normal">Welcome</h1>
    <p>Motorcycle builders and road lovers</p>
    <hr>
</div>
<div class="form-group">
    <label for="email">Email address</label>
    <input type="email" class="form-control" id="email"
     ariadescribedby="emailHelp" placeholder="Enter email">
</div>
    <div class="form-group">
    <label for="password">Password</label>
    <input type="password" class="form-control" id="password" 
        placeholder="Password">
</div>
    <button class="btn btn-lg btn-primary btn-block mt-5"
         type="submit">Login</button>
</form>
</div>
</main>

创建注册视图和模板

打开./Client/src/app/pages/auth/register/register.component.html并用以下代码的register工作字符串替换该段落:

<main role="main">
<div class="container">
<form class="form-signin">
<div class="text-center mb-4">
<h1 class="h3 mt-3 mb-3 font-weight-normal">Welcome</h1>
<p>Motorcycle builders and road lovers</p>
<hr>
</div>
<div class="form-group">
<label for="name">Name</label><input type="name" class="form-control" id="name" aria-describedby="nameHelp" placeholder="Enter your name">
</div>
<div class="form-group">
    <label for="email">Email address</label>
    <input type="email" class="form-control" id="email" aria-
    describedby="emailHelp" placeholder="Enter email">
</div>
<div class="form-group">
    <label for="password">Password</label>
    <input type="password" class="form-control" id="password"
     placeholder="Password">
</div>
    <button class="btn btn-lg btn-primary btn-block mt-5" 
    type="submit">Register</button>
</form>
</div>
</main>

我们现在在模板中有了必要的代码。然而,我们还没有应用任何样式表。暂时不用担心;在接下来的部分中,在我们应用样式表之前,您将看到应用中一些更重要的点。让我们看看目前为止我们有什么。

测试路线和视图

让我们在开发模式下启动应用,并检查一些网址,以查看我们的路线和模板的结果:

  1. 打开./Client文件夹内的终端窗口,输入以下命令:
npm start
  1. 打开默认浏览器,进入http://localhost:4200/bikes/1

您将看到一个与以下截图非常相似的结果:

Bike detail page

摘要

你已经到了另一章的结尾。在本章中,您学习了如何在模块中创建附加组件,例如bikes模块。您使用 Angular 路由添加了一些路由,并学习了如何使用子路由。此外,您还学习了如何使用 Angular 默认服务创建导航组件并更新页面的<title>标签。