在本系列的第 2 部分我们已经介绍身份认证,授权,功能规划以及数据建模。
接下来本系列的第 3 部分将介绍如何通过 Node API 从 MongoDB 检索数据以及通过 Angular 在前段显示和过滤数据:
API:获取 Events 数据
让我们继续上次的内容。我们的数据库中已经有数据,所以现在是用 API 检索数据的时候了。我们将从编写四个 API 开始,它们将分别从 MongoDB 获取如下数据:
- 将要发生的公开活动列表
- 包含所有活动的列表(需要 admin 权限)
- 活动详情(需要身份认证)
- 活动的回复列表(需要身份认证)
打开server/api.js
, 让我们开始吧!
GET Future Public Events
获取未来的公开的活动列表数据:
1 | // server/api.js |
GET All Public and Private Events
获取所有的活动列表(包含过去和将来的公开或私密活动):
1 | // server/api.js |
注意,这里添加了jwtCheck
和adminCheck
中间件对请求进行身份校验。只有管理员才能通过列表和所有活动进行交互,虽然普通用户也可以并只限于通过直接链接查看私密活动信息!(简单起见,本系列不会继续深入细化数据权限校验,不过你可以自己研究研究,这并不是什么难事)
GET Event Details
获取特定活动信息:
1 | // server/api.js |
GET RSVPs for an Event
获取特定活动的回复信息:
1 | // server/api.js |
Angular:获取 Events 数据
Node API 已经就绪了,我们只需在 Angular 中请求它们然后显示返回的数据就可以啦。
添加 HttpClientModule 模块
首先我们需要在根模块导入HttpClientModule
,因为我们需要通过 HTTP(s)服务调用后台 API:
1 | ... |
创建 API 服务
现在我们将创建一个 API 服务专注于从 Node API 返回特定的数据。通过 CLI 在src/app/core
文件夹生成api.service.ts
:
1 | import { |
我们会发出未经身份验证和身份验证的请求,因此我们将导入HttpClient
和HttpHeaders
(以添加带有访问令牌的授权头部)以及HttpErrorResponse
。如果在尝试发出经过身份验证的请求时没有发现 JWT,我们还需要AuthService
来提示登录。
我们将使用 API 调用创建流,因此我们将从 RxJS 导入Observable
和ObservableThrowError
以及可链式调用的 catchError
操作符。我们需要环境配置中的 ENV 来获得适当的 API uri。最后,为了声明事件流的类型,我们需要前面创建的模型(EventModel
和RsvpModel
)。
为了发出经过身份验证的请求,我们需要使用存储在本地存储中的访问令牌设置一个授权头,这个访问令牌来自我们在第 2 部分中创建的身份认证服务。我们将创建一个名为_authHeader
的访问器方法,使用当前存储的访问令牌返回必要的授权值。如果在会话期间以静默方式更新身份验证(稍后我们将实现静默的令牌更新),则令牌可能会更改,因此我们将在每个请求时从服务中获取它,以确保其有效性。
最后,我们需要处理 API 错误信息。成功调用则将返回响应作为主体(在我们的示例中,返回 JSON)。如果调用失败,则检查错误消息,并在必要时提示重新登录,取消可观察对象,并在发生其他错误时生成错误提示。
API 服务注入
为了使 API 服务能在整个应用中使用,需要在根模块注入依赖:
1 | // src/app/app.module.ts |
创建加载组件
由于我们将进行异步 API 调用,所以最好也有一个加载状态。或者,我们可以使用route resolve来防止在返回必要的 API 数据之前加载路由,但这可能会让应用程序在导航时显得迟缓。相反,我们可以显示一个带有非常简单的组件的加载图标:
1 | $ ng g component core/loading --is --it --flat |
现在需要一个合适的加载图标,你可以从loading.io选择你喜欢的。然后将它放在src/assets/images
。接着编辑我们的loading.component.ts
:
1 | // src/app/core/loading.component.ts |
我们的加载组件就完成了,简单吧!让我们试着把回调组件的Loading...
字样替换成我们的加载组件:
1 | <!-- src/app/pages/callback/callback.component.html --> |
你会发现,当成功登陆跳转时,将会显示我们的加载图标。
Angular: 创建 Utility 服务
在开始构建组件之前,让我们先创建一个可以在整个开发过程中利用的的实用程序服务。
1 | $ ng g service core/utils |
1 | // src/app/core/utils.service.ts |
这里我们使用了 Angular 内置的DatePipe
,所以我们需要导入它,并且在根模块里注入依赖。
isLoaded()
用于检查传入值是否严格等于false
,我们打算在每个组件里添加一个loading
属性用于同步 API 调用的的状态,因为loading
值可能会为undefined
,所以通过这个公用方法可以防止显示错误的 UI。
然后eventDates()
和eventDatesTimes()
用于转换时间的显示格式。而eventPast()
用于检查是否是过去的时间,从而告诉我们活动已经过期。
为了全局使用这个通用的服务,在根模块注入相应依赖。
1 | // src/app/app.module.ts |
Angular: 创建过滤/排序服务
对于获取的数据数组,我们需要添加一些方法来组织它们。就如同 AngularJS 里面的内置过滤器,比如filter
和orderBy
。虽然 Angular 使用pipes来转换数据,但是并不提供用于过滤或排序的开箱即用管道。(原因)
出于性能和影响最小化的考虑,我们将不创建自定义管道来实现筛选或排序功能。这只会重新引入 Angular 团队试图通过删除这些过滤器来解决的问题。相反,正确的方法是使用服务。
我们将会添加几个全局通用的服务方法用于搜索,过滤和排序: $ ng g service core/filter-sort
打开src/app/core/filter-sort.service.ts
:
1 | import { Injectable } from '@angular/core'; |
_objArrayCheck()
方法,用于确保我们尝试搜索或排序的数组包含对象。如果没有,就会产生未捕获的引用错误,因此我们希望有一种方法来防止这种情况。
search()
方法接受要筛选的对象数组、要搜索的查询、要从搜索中排除的任何可选属性(单个属性字符串或属性数组),以及可选的日期格式字符串。dateFormat
应该是Angular DatePipe
中的一种格式。这允许用户搜索原始数据中可读性差得多的日期。开发可以确定他们想要查询的格式。例如,如果转换 UTC 日期字符串或 JavaScript 日期对象,用户可以查询 Jan 并接收数据中实际值为 2017-01-07T15:00:00.000Z 的结果。
如果查询是falsey
,我们将返回未经过滤的数组。否则,我们将把查询设置为小写,因为我们的搜索应该不区分大小写(我们将对查询的值执行相同的操作)。由于 UTC 日期在 JavaScript 中被识别为字符串而不是日期,因此我们将使用正则表达式将其与其他字符串区分开来。如果没有传递dateFormat
参数,我们将默认为medium
(例如,2010 年 9 月 3 日,12:05:08 PM)。
接下来,我们将使用数组方法filter()
对数组进行筛选。我们将遍历数组中每个对象中的每个属性,首先确保对象包含hasOwnProperty()
方法中的属性。如果键不匹配excludeProps
中传递的任何内容,将检查与query
匹配的值.
这对于不同的值类型是不同的。搜索处理字符串、JavaScript 日期对象和 UTC 字符串。如果我们想确保搜索不查询某些属性,我们将确保在调用组件中的方法时将它们作为excludedProps
传入。
noSearchResults()
方法只接受一个数组和一个查询,如果该数组为空且有查询,则返回 true。
orderByDate()
方法接受一个对象数组、包含要排序的日期值的属性和一个可选的反向参数,以将排序顺序从升序更改为降序。如果没有传递属性,则返回未排序的数组。
在根模块注入依赖:
1 | // src/app/app.module.ts |
现在我们可以在组件中根据日期搜索和排序活动事件列表。
Angular: 首页活动事件列表
组件应该获取和显示事件列表。我们已经创建了 Node API 来返回该数据,并实现了 API 服务来获取它。现在我们需要在组件里订阅这些数据并显示在页面。
为了使用ngModel
指令,我们需要在根模块导入FormsModule
:
1 | // src/app/app.module.ts |
接着,更新我们的主页组件,让它可以显示公开的活动信息。
1 | // src/app/pages/home/home.component.ts |
1 | <!-- src/app/pages/home/home.component.html --> |
小结
我们已经介绍了如何使用 Node API 从数据库中获取数据,以及如何在 Angular 中操作和显示数据。在本系列教程的下一部分中,我们将处理访问管理、显示管理事件列表以及开发带有选项卡子组件的事件详细信息页面。
系列索引