Web应用程序开发教程 - 第二章: 图书列表页面
关于本教程
在本系列教程中, 你将构建一个名为 Acme.BookStore
的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的:
- Entity Framework Core 做为ORM提供程序.
- Angular 做为UI框架.
本教程分为以下部分:
- Part 1: 创建服务端
- Part 2: 图书列表页面 (本章)
- Part 3: 创建,更新和删除图书
- Part 4: 集成测试
- Part 5: 授权
- Part 6: 作者: 领域层
- Part 7: 作者: 数据库集成
- Part 8: 作者: 应用服务层
- Part 9: 作者: 用户页面
- Part 10: 图书到作者的关系
下载源码
本教程根据你的UI 和 数据库偏好有多个版本,我们准备了几种可供下载的源码组合:
如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 在Windows 10中启用长路径.
如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
git config --system core.longpaths true
本地化
开始的UI开发之前,我们首先要准备本地化的文本(这是你通常在开发应用程序时需要做的).
本地化文本位于 Acme.BookStore.Domain.Shared
项目的 Localization/BookStore
文件夹下:
打开 en.json
(英文翻译)文件并更改内容,如下所示:
{
"Culture": "en",
"Texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.",
"Menu:BookStore": "Book Store",
"Menu:Books": "Books",
"Actions": "Actions",
"Close": "Close",
"Delete": "Delete",
"Edit": "Edit",
"PublishDate": "Publish date",
"NewBook": "New book",
"Name": "Name",
"Type": "Type",
"Price": "Price",
"CreationTime": "Creation time",
"AreYouSure": "Are you sure?",
"AreYouSureToDelete": "Are you sure you want to delete this item?",
"Enum:BookType.Undefined": "Undefined",
"Enum:BookType.Adventure": "Adventure",
"Enum:BookType.Biography": "Biography",
"Enum:BookType.Dystopia": "Dystopia",
"Enum:BookType.Fantastic": "Fantastic",
"Enum:BookType.Horror": "Horror",
"Enum:BookType.Science": "Science",
"Enum:BookType.ScienceFiction": "Science fiction",
"Enum:BookType.Poetry": "Poetry"
}
}
简体中文翻译请打开
zh-Hans.json
文件 ,并将"Texts"对象中对应的值替换为中文.
- 本地化关键字名称是任意的. 你可以设置任何名称. 对于特定的文本类型,我们更喜欢遵循一些约定:
- 为按钮项添加
Menu:
前缀. - 使用
Enum:<enum-type>:<enum-name>
或<enum-type>.<enum-name>
或<enum-name>
命名约定来本地化枚举成员. 当您这样做时ABP可以在某些适当的情况下自动将枚举本地化.
- 为按钮项添加
如果未在本地化文件中定义文本,则文本将回退到本地化键(ASP.NET Core的标准行为).
ABP本地化系统建立在ASP.NET Core标准本地化系统之上,并以多种方式进行了扩展. 有关详细信息请参见本地化文档.
安装NPM包
注意: 本教程基于ABP Framework v3.1.0+. 如果你的项目版本较旧,请升级您的解决方案. 如果要升级现有的v2.x项目,请参阅迁移指南.
在 angular
目录下打开命令行窗口,选择 yarn
命令安装NPM包:
yarn
创建图书页面
是时候创建可见和可用的东西了!开发ABP Angular前端应用程序时,需要使用一些工具:
- Ng Bootstrap 用做UI组件库.
- ngx-datatable 用做 datatable 类库.
运行以下命令在angular应用程序根目录创建一个名为 BookModule
的新模块:
yarn ng generate module book --module app --routing --route books
该命令应该产生以下的输出:
> yarn ng generate module book --module app --routing --route books
yarn run v1.19.1
$ ng generate module book --module app --routing --route books
CREATE src/app/book/book-routing.module.ts (336 bytes)
CREATE src/app/book/book.module.ts (335 bytes)
CREATE src/app/book/book.component.html (19 bytes)
CREATE src/app/book/book.component.spec.ts (614 bytes)
CREATE src/app/book/book.component.ts (268 bytes)
CREATE src/app/book/book.component.scss (0 bytes)
UPDATE src/app/app-routing.module.ts (1289 bytes)
Done in 3.88s.
BookModule
打开 /src/app/book/book.module.ts
并使用以下内容替换:
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { BookRoutingModule } from './book-routing.module';
import { BookComponent } from './book.component';
@NgModule({
declarations: [BookComponent],
imports: [
BookRoutingModule,
SharedModule
]
})
export class BookModule { }
- 添加了
SharedModule
.SharedModule
导出了一些创建用户界面所需的通用模块. SharedModule
已经导出了CommonModule
,所以我们删除了CommonModule
.
路由
生成的代码将新的路由定义放在 src/app/app-routing.module.ts
文件中,如下所示:
const routes: Routes = [
// other route definitions...
{ path: 'books', loadChildren: () => import('./book/book.module').then(m => m.BookModule) },
];
现在打开 src/app/route.provider.ts
替换 configureRoutes
函数为以下代码:
function configureRoutes(routes: RoutesService) {
return () => {
routes.add([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
{
path: '/book-store',
name: '::Menu:BookStore',
iconClass: 'fas fa-book',
order: 2,
layout: eLayoutType.application,
},
{
path: '/books',
name: '::Menu:Books',
parentName: '::Menu:BookStore',
layout: eLayoutType.application,
},
]);
};
}
RoutesService
是ABP框架提供的用于配置主菜单和路由的服务.
path
路由的URL.name
菜单项的名称(参阅本地化文档了解更多).iconClass
菜单项的图标(你可以使用默认的Font Awesome图标).order
菜单项的排序.layout
BooksModule路由的布局. (有三个预定义的布局类型:eLayoutType.application
,eLayoutType.account
或eLayoutType.empty
).
更多信息请参阅RoutesService 文档.
生成服务代理
ABP CLI 提供 generate-proxy
命令为HTTP APIs生成客户端代理.有了这些代理,在客户端使用HTTP APIs变得更加方便. 运行 generate-proxy
命令前, 你的 host 必须正在运行.
警告: 使用IIS Express时有一个问题; 它不允许从另一个进程连接应用程序. 如果你使用Visual Studio, 在运行按钮的下拉框中选择
Acme.BookStore.HttpApi.Host
,不要选择IIS Express, 如下图:
启动host应用程序后,在 angular
文件夹下运行以下命令:
abp generate-proxy -t ng
这个命令将在/src/app/proxy/books
文件夹下产生以下文件:
BookComponent
打开 /src/app/book/book.component.ts
用以下内容替换它:
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { BookService, BookDto } from '@proxy/books';
@Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.scss'],
providers: [ListService],
})
export class BookComponent implements OnInit {
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
constructor(public readonly list: ListService, private bookService: BookService) {}
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
this.book = response;
});
}
}
- 我们引入并注入了生成的
BookService
. - 我们使用 ListService,它是一个工具服务,提供了易用的分页,排序和搜索.
打开 /src/app/book/book.component.html
用以下内容替换它:
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{{ '::Menu:Books' | abpLocalization }}
</h5>
</div>
<div class="text-end col col-md-6"></div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
<ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
<ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ '::Enum:BookType:' + row.type | abpLocalization }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.publishDate | date }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.price | currency }}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
现在你可以在浏览器看到最终结果:
下一章
查看本教程的下一章.