八、Angular 路由及其组件的处理
我们来到了单页应用 ( SPAs )最重要的部分之一:路线的使用。正如您在第 3 章、中看到的理解 Angular 6 的核心概念,Angular 框架为处理应用路由提供了一个强大的工具:@ angular/router dependency。
在接下来的几节中,您将学习如何使用其中的一些功能,例如子视图,以及如何创建母版页。此外,我们将开始构建应用的视觉领域,用 HTML 标记填充模板。
在本章中,我们将涵盖以下主题:
- 准备基线代码
- 向应用添加组件
- 处理 Angular 路线
- 为详细信息页面配置子路由
- 建筑前端视图
准备基线代码
现在,我们需要准备我们的基线代码,这个过程非常类似于前面章节中的过程。让我们遵循以下步骤:
-
从
chapter-08
文件夹复制所有内容。 -
将文件夹重命名为
chapter-08
。 -
删除
storage-db
文件夹。
现在,让我们对docker-compose.yml
文件进行一些更改,以适应新的数据库和服务器容器。
- 打开
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
端点进行同样的操作。
所以,让我们开始创建组件:
- 打开
./Client/src/app
内的终端窗口,输入以下命令:
ng g c pages/bikes/bike-detail
在上一个命令的末尾,您将在bikes
模块中看到以下结构:
Bikes module structure
前面的命令将创建一个根bikes
文件夹来存储与bikes
端点相关的每个模块;这种模式允许我们有一个模块化的应用,其中每个新特性(例如,bikes-detail
或bike-list
)将以相同的方式组织。
例如,我们可以添加一个新的库存模块,该模块将在其自己的模块(inventory.module.ts
)中创建,并存储在bikes
模块目录中。
认为这是一个很好的实践,并保持您的模块和组件以这种方式组织;避免在同一个文件夹的根目录下对多个组件进行分组。这可以防止您的代码变成意大利面条代码。
- 打开
./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 路由:
- 检查浏览器网址。
- 查找对应于该网址的路由状态。
- 如果路由保护是在路由状态下定义的,则应用路由保护。
- 激活相应的 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
文件夹中的任何其他文件夹/模块中包含任何路由文件,例如login
、register
和logout
。这是因为我们将使用auth-routing.module.ts
文件来创建与身份验证相关的所有路由。
现在,我们将创建身份验证路由:
- 打开
./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';
- 现在,在
routes
常量内添加以下代码:
const routes: Routes = [
{ path: 'register', component: RegisterComponent },
{ path: 'login', component: LoginComponent },
{ path: 'logout', component: LogoutComponent }
];
现在,让我们从home
模块开始,研究应用的其他路线。
创建主路由
现在,我们将创建home
路线,如下所示:
- 打开
./Client/src/app/pages/home/home-routing.module.ts
和import
组件:
// Home Routes Imports
import { HomeComponent } from './home.component';
- 打开
./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.ts
和import
组件:
imports
import { BuilderDetailComponent } from './builder-detail/builder-detail.component';
import { BuilderListComponent } from './builder-list/builder-list.component';
- 仍然在
./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
。那么,让我们创建这个新组件。
- 在
./Client/src/app
内,键入以下命令:
ng g c pages/builders/builder-list
您将在builders
模块中看到以下结构:
Builders module with builder-list module
添加摩托车手子路线
让我们在前端应用中为以下视图创建子路由:
bike-list
bike-detail
现在我们将导入文件顶部的组件:
- 打开
./Client/src/app/pages/bikes/bikes-routing.module.ts
和import
组件:
// Bikes Routes Imports
import { BikeDetailComponent } from './bike-detail/bike-detail.component';
import { BikeListComponent } from './bike-list/bike-list.component';
- 仍然在
./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
做的那样。
- 在
./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 © 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>
标签,如下所示:
- 打开
./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() {
}
}
- 打开
./Client/src/app/app.module.ts
并将Title
导入添加到文件顶部:
import { BrowserModule, Title } from '@angular/platform-browser';
- 现在,将
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 »</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 »</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 »</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>
我们现在在模板中有了必要的代码。然而,我们还没有应用任何样式表。暂时不用担心;在接下来的部分中,在我们应用样式表之前,您将看到应用中一些更重要的点。让我们看看目前为止我们有什么。
测试路线和视图
让我们在开发模式下启动应用,并检查一些网址,以查看我们的路线和模板的结果:
- 打开
./Client
文件夹内的终端窗口,输入以下命令:
npm start
- 打开默认浏览器,进入
http://localhost:4200/bikes/1
。
您将看到一个与以下截图非常相似的结果:
Bike detail page
摘要
你已经到了另一章的结尾。在本章中,您学习了如何在模块中创建附加组件,例如bikes
模块。您使用 Angular 路由添加了一些路由,并学习了如何使用子路由。此外,您还学习了如何使用 Angular 默认服务创建导航组件并更新页面的<title>
标签。
版权属于:月萌API www.moonapi.com,转载请注明出处