要使用 Angular 2 构建单页应用 ( SPAs ),我们需要学习如何在 Angular 2 中实现路由。Angular 2 自带内置路由 API,功能非常强大,功能丰富,易于使用。在本章中,我们将构建一个基本的搜索引擎模板来演示 Angular 2 中的路由。我们不会构建一个完整的搜索引擎,因为这超出了本模块的范围。我们将使用 Bootstrap 4 来设计搜索引擎模板。在这一章的最后,你会对使用 Angular 2 构建 SPa 感到满意。
在本章中,我们将涵盖以下主题:
- 角度 2 中的布线
- Angular 2 提供的内置 HTTP 客户端
- 使用
Chance.js
库生成随机文本数据
按照以下步骤设置您的项目:
-
在本章的练习文件中,你会发现两个目录,
initial
和final
。final
目录包含最终的搜索引擎模板,而initial
目录包含快速开始构建搜索引擎模板的文件。 -
In the
initial
directory, you will findapp.js
andpackage.json
. In thepackage.json
file, place this code:{ "name": "SearchEngine-Template", "dependencies": { "express": "4.13.3", "chance": "1.0.3" } }
这里,我们将
Express.js
和Chance.js
列为依赖项。Express 将用于构建网络服务器,而Chance.js
将用于生成随机文本数据,以填充模板的搜索结果。 -
Now, run
npm install
inside theinitial
directory to download the packages.在
initial
目录里面,你会发现一个名为public
的目录,里面会放置所有的静态资产。在public
目录中,你会找到componentTemplates
、css
、html
和js
目录。在
css
目录里面,你会发现bootstrap.min.css
;index.html
里面的html
目录;最后是js
目录里面的index.js
、angular2-all.umd.js
、angular2-polyfills.js
和Rx.umd.js
。 -
In
index.html
, place this starting code to load Angular, Bootstrap, and theindex.js
file:<!doctype html> <html> <head> <title>Search Engine Template</title> <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"> </head> <body> <script src="/js/angular2-polyfills.js"></script> <script src="/js/Rx.umd.js"></script> <script src="/js/angular2-all.umd.js"></script> <script src="/js/index.js"></script> </body> </html>
这段代码是不言自明的。
-
Now, in the
app.js
file, place this code:var express = require("express"); var app = express(); app.use(express.static(__dirname + "/public")); app.get("/*", function(httpRequest, httpResponse, next){ httpResponse.sendFile(__dirname + "/public/html/index.html"); }) app.listen(8080);
这里也一样,大部分代码都是不言自明的。我们只是简单地为
index.html
服务,而不管 HTTP 请求路径是什么。
在 SPA 中,我们应用的路线是在前端定义的。在 Angular 2 中,我们需要定义路径和与该路径相关联的组件,该组件将为该路径渲染。
我们提供到根组件的路由,根组件显示绑定到路由的组件。
让我们为搜索引擎模板创建根组件和路由:
-
将此代码放入
index.js
文件中,以创建根组件和路线:var AppComponent = ng.core.Component({ selector: "app", directives: [ng.router.ROUTER_DIRECTIVES], templateUrl: "componentTemplates/app.html" }).Class({ constructor: function(){} }) AppComponent = ng.router.RouteConfig([ { path: "/", component: HomeComponent, name: "Home" }, { path: "/search-result", component: SearchResultComponent, name: "SearchResult" }, { path: "/*path", component: NotFoundComponent, name: "NotFound" } ])(AppComponent); ng.platform.browser.bootstrap(AppComponent, [ ng.router.ROUTER_PROVIDERS, ng.core.provide(ng.router.APP_BASE_HREF, {useValue : "/" }) ]);
-
现在,在
componentTemplates
目录中创建一个名为app.html
的文件,并将该代码放入其中:<nav class="navbar navbar-light bg-faded"> <ul class="nav navbar-nav"> <li class="nav-item"> <a class="nav-link" [routerLink]="['Home']">Home</a> </li> </ul> </nav> <router-outlet></router-outlet>
下面是这个代码是如何工作的:
- 首先,我们创建根组件,称为
AppComponent
。在创建根组件时,我们向其中添加了ng.router.ROUTER_DIRECTIVES
指令,这允许我们使用routerLink
指令。 - 然后,我们使用
ng.router.RouteConfig
为我们的应用配置路由。我们提供了一系列路线作为ng.router.RouteConfig
方法的参数。路由由路径、组件和路由名称组成。路径可以是静态的、参数化的或通配符的,就像快速路由路径一样。这里,第一个路由用于主页,第二个用于显示搜索结果,最后,第三个用于处理无效网址,即没有定义路由的网址。ng.router.RouteConfig
方法返回一个函数,该函数获取根组件并将路由附加到它。 - 然后我们初始化应用。初始化应用时,我们将传递
ng.router.ROUTER_PROVIDERS
提供者,该提供者将用于创建与路由相关的各种服务的实例。此外,我们还提供了一个自定义提供程序,当请求ng.router.APP_BASE_HREF
服务的实例时,它会返回/
字符。ng.router.APP_BASE_HREF
用于查找 app 的基础 URL。 - 在
AppComponent
模板中,我们正在显示一个导航栏。导航栏有一个没有href
属性的anchor
标签;取而代之的是,我们使用routerLink
指令来分配重定向链接,这样当点击时,它只改变 URL 和组件,而不是一个完整的页面重载。最后,<router-outlet>
显示的是基于当前网址的组件。
为了填充我们的模板,我们需要生成一些随机的搜索结果数据。为此,我们可以使用Chance.js
库。我们将在服务器端生成随机数据,而不是在客户端,这样我们以后就可以演示如何使用 Angular 2 发出 HTTP 请求。
Chance.js
可用于客户端和服务器端的 JavaScript。我们之前下载了Node.js
使用的Chance.js
包。下面是生成随机数据的代码。将其放在/*
路线上方的app.js
文件中,以便/*
不会覆盖随机数据路线:
var Chance = require("chance"),
chance = new Chance();
app.get("/getData", function(httpRequest, httpResponse, next){
var result = [];
for(var i = 0; i < 10; i++)
{
result[result.length] = {
title: chance.sentence(),
desc: chance.paragraph()
}
}
httpResponse.send(result);
})
在这里,我们首先为/getData
路径创建一条路线,它发送一系列搜索结果作为响应。路线回调使用chance.sentence()
为搜索结果生成随机标题,使用chance.paragraph()
生成描述。
让我们创建HomeComponent
、SearchResultComponent
和NotFoundComponent
。在此之前,让我们创建一个组件来显示搜索表单。搜索表单将有一个文本框和一个搜索按钮。请遵循以下步骤:
-
将该代码放入
index.js
文件中,AppComponent
代码上方:var FormComponent = ng.core.Component({ selector: "search-form", directives: [ng.router.ROUTER_DIRECTIVES], templateUrl: "componentTemplates/search-form.html", }).Class({ constructor: function(){}, ngOnInit: function(){ this.searchParams = { query: "" }; this.keyup = function(e){ this.searchParams = { query: e.srcElement.value }; }; } })
-
现在,在
componentTemplates
目录中创建一个名为search-form.html
的文件,并将该代码放入其中:<div class="m-a-2 text-xs-center"> <h1>Search for Anything</h1> <form class="form-inline m-t-1"> <input (keyup)="keyup($event)" class="form-control" type="text" placeholder="Search"> <a [routerLink]="['SearchResult', searchParams]"> <button class="btn btn-success-outline" type="submit">Search</button> </a> </form> </div>
代码是这样工作的:
- 首先,我们创建一个名为
FormComponent
的组件。它使用ng.router.ROUTER_DIRECTIVES
指令。 - 在组件的模板中,我们显示一个 HTML 表单。该表单有一个文本框和按钮。
- 我们处理文本输入框的
keyup
事件,并将值存储在searchParams.query
属性中。 - 该按钮重定向至
SearchResult
组件。请注意,这里我们将searchParams
对象传递给routerLink
,它将成为重定向时的查询参数。
现在,让我们创建HomeComponent
组件。此组件显示在主页上。它显示搜索表单。
下面是如何创建HomeComponent
:
-
将该代码放入
index.js
文件中,AppComponent
代码上方:var HomeComponent = ng.core.Component({ selector: "home", directives: [FormComponent], templateUrl: "componentTemplates/home.html", }).Class({ constructor: function(){} })
-
Now, create a file named
search-form.html
, and place it in thecomponentTemplates
directory:<search-form></search-form>
这里
HomeComponent
代码不言自明。 -
现在,让我们创建
SearchResultComponent
组件。该组件应该在其下方显示搜索表单和搜索结果。它应该通过向服务器发出 HTTP 请求来获取结果。这是SearchResultComponent
的代码。将其放入index.js
文件中,AppComponent
代码上方:var SearchResultComponent = ng.core.Component({ selector: "search-result", directives: [FormComponent], viewProviders: [ng.http.HTTP_PROVIDERS], templateUrl: "componentTemplates/searchResult.html" }).Class({ constructor: [ng.router.RouteParams, ng.http.Http, function(params, http) { this.params = params; this.http = http; this.response = []; }], ngOnInit: function(){ var q = this.params.get("query"); this.http.get("getData").subscribe(function(res){ this.response = JSON.parse(res._body); }.bind(this)); } })
-
现在,创建一个名为
searchResult.html
的文件,并将其放置在componentTemplates
中。将此代码放入文件:<style> ul { list-style-type: none; } </style> <div class="container"> <search-form></search-form> <div class="m-a-2 text-xs-center"> <ul> <li *ngFor="#item of response" class="m-t-2"> <h4>{{item.title}}</h4> <p>{{item.desc}}</p> </li> </ul> </div> </div>
这就是代码的工作原理:
- 这里我们提供的是
ng,http.HTTP_PROVIDERS
提供者,在使用 Angular 2 提供的 HTTP 客户端服务时使用。使用 HTTP 客户端服务,我们可以发出 HTTP 请求。 - 在构造函数属性中,我们将 HTTP 服务和
ng.router.RouteParams
服务一起注入,用于获取当前 URL 的查询参数。 - 在
ngOnInit
方法中,您可以看到如何使用 HTTP 服务发出GET
请求,以及如何使用ng.router.RouteParams
服务获取查询参数。 - 在组件的模板中,我们使用
ngFor
指令显示提取的搜索结果。
您可以在https://Angular . io/docs/ts/latest/guide/server-communication . html了解 Angular 2 提供的 HTTP 服务。
现在,让我们创建NotFoundComponent
。这是它的代码:
-
将该代码放入
index.js
文件中,AppComponent
代码上方:var NotFoundComponent = ng.core.Component({ selector: "name-search", templateUrl: "componentTemplates/notFound.html" }).Class({ constructor: function(){} })
-
Now, create a file named
notFound.html
and place it in thecomponentTemplates
directory. Place this code inside the file:<div class="container"> <div class="m-a-2 text-xs-center"> <h1>The page your are looking for is not found</h1> </div> </div>
代码是不言自明的。
要测试模板,我们将遵循以下步骤:
-
在
initial
目录中,运行node app.js
命令。 -
Now, in a browser, open the
http://localhost:8080/
URL. You should see this output: -
Now, type something in the search box and click on the Search button. You should then see this output:
-
Now, enter an invalid path in the address bar. You should be able to see this output:
当路径匹配一个组件时,角度 2 激活该组件,当路径改变时,角度 2 去激活它。当我们说某个组件已经被激活时,意味着 Angular 2 已经创建了该组件的一个实例,也就是调用了该组件的构造器方法,而当我们说某个组件已经被去激活时,则意味着该组件已经从 DOM 中移除,并且该实例被删除。
激活或停用组件时调用的组件方法称为路由生命周期方法。
以下是路由生命周期方法的列表:
CanActivate
:激活组件前调用此钩子。它应该返回一个布尔值或一个承诺,指示是否激活组件。routerOnActivate
:该方法在组件激活后调用。routerCanReuse
:调用此方法是为了在下一次 URL 变更再次为同一个 URL 时,找出是否重用组件的前一个实例。它应该返回一个布尔值或一个承诺,指示是否重用。只有在先前创建了实例的情况下才会调用它。routerOnReuse
:如果组件被重用,则调用此方法。叫做routerCanReuse
之后。routerCanDeactivate
:该方法在组件停用前调用。它应该返回一个布尔值或一个承诺,指示是否停用该组件。routerOnDeactivate
:该方法在组件停用后调用。
让我们看一个路由生命周期方法的代码示例。将HomeComponent
代码替换为:
var HomeComponent = ng.core.Component({
selector: "home",
directives: [FormComponent],
templateUrl: "componentTemplates/home.html",
}).Class({
constructor: function(){},
routerOnActivate: function(){
console.log("Component has been activated");
},
routerCanReuse: function(){
console.log("Component can be reused");
return true;
},
routerOnReuse: function(){
console.log("Component is being reused");
},
routerCanDeactivate: function(){
console.log("Component can be deactivated");
return true;
},
routerOnDeactivate: function(){
console.log("Component has been deactivated");
}
})
HomeComponent = ng.router.CanActivate(function(){
console.log("Component can be activated");
return true;
})(HomeComponent);
现在,访问主页。在那里,再次点击主页按钮。现在,在搜索框中输入一些内容,然后点击搜索按钮。这是您将看到的控制台输出:
Component can be activated
Component has been activated
Component can be reused
Component is being reused
Component can be deactivated
Component has been deactivated
直到现在,我们一直在开发模式下运行 Angular 2。开发和生产模式的区别在于,在开发模式下,Angular 2 在第一次运行后立即开始变化检测,并在检查到第一次和第二次运行之间有任何变化时记录一个值发生了变化错误。这有助于定位 bug。
要启用生产模式,请将此代码置于ng.platform.browser.bootstrap()
方法调用之上:
ng.core.enableProdMode();
在本章中,我们通过构建一个基本的搜索引擎模板来学习 Angular 2 中的路由。除了深入学习路由,我们还学习了 Angular 2 HTTP 客户端服务,以及如何在 Angular 2 中切换到生产模式。
现在,您应该已经习惯于使用 Angular 2 构建任何类型的 web 应用的前端了。