Web 应用程序开发教程 - 第 2 部分:书籍列表页面
本地化
在开始 UI 开发之前,我们首先需要准备本地化文本(通常是在开发应用程序时按需进行)。
本地化文本位于 Acme.BookStore.Domain.Shared 项目的 Localization/BookStore 文件夹下:
打开 en.json 文件(英文翻译),并按以下内容修改:
{
"Culture": "en",
"Texts": {
"Menu:Home": "首页",
"Welcome": "欢迎",
"LongWelcomeMessage": "欢迎使用本应用程序。这是一个基于 ABP 的启动项目。更多信息请访问 abp.io。",
"Menu:BookStore": "书店",
"Menu:Books": "书籍",
"Actions": "操作",
"Close": "关闭",
"Delete": "删除",
"Edit": "编辑",
"PublishDate": "出版日期",
"NewBook": "新书",
"Name": "名称",
"Type": "类型",
"Price": "价格",
"CreationTime": "创建时间",
"AreYouSure": "确定吗?",
"AreYouSureToDelete": "确定要删除此项吗?",
"Enum:BookType.0": "未定义",
"Enum:BookType.1": "冒险",
"Enum:BookType.2": "传记",
"Enum:BookType.3": "反乌托邦",
"Enum:BookType.4": "奇幻",
"Enum:BookType.5": "恐怖",
"Enum:BookType.6": "科学",
"Enum:BookType.7": "科幻",
"Enum:BookType.8": "诗歌"
}
}
- 本地化键名是任意的,你可以设置任何名称。我们针对特定文本类型推荐一些约定:
- 为菜单项添加
Menu:前缀。 - 使用
Enum:<枚举类型>.<枚举值>或<枚举类型>.<枚举值>命名约定来本地化枚举成员。这样处理后,ABP 可以在某些适当场景下自动对枚举进行本地化。
- 为菜单项添加
如果本地化文件中未定义某个文本,它将回退到本地化键(遵循 ASP.NET Core 的标准行为)。
ABP 的本地化系统基于 ASP.NET Core 的标准本地化系统构建,并在许多方面进行了扩展。详情请参阅本地化文档。
安装 NPM 包
注意:本教程基于 ABP v3.1.0+ 版本。如果你的项目版本较旧,请升级你的解决方案。如果你要升级现有的 v2.x 项目,请查看迁移指南。
如果之前未操作,请打开一个新的命令行界面(终端窗口),进入你的 angular 文件夹,然后运行 yarn 命令以安装 NPM 包:
yarn
创建书籍页面
是时候创建一些可见且可用的内容了!在开发 Angular 前端应用程序时,我们将使用以下工具:
- Ng Bootstrap 将用作 UI 组件库。
- Ngx-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 = [
// 其他路由定义...
{ 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 API 生成客户端代理,使客户端更容易使用你的 HTTP API。在运行 generate-proxy 命令之前,你的主机必须处于运行状态。
警告:IIS Express 存在一个问题:它不允许从另一个进程连接到应用程序。如果你使用的是 Visual Studio,请在下拉列表中选择
Acme.BookStore.HttpApi.Host而不是 IIS Express,如下图所示:
一旦主机应用程序运行起来,请在 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, inject } 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>;
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
this.book = response;
});
}
}
- 我们导入并注入了生成的
BookService。 - 我们使用了 ListService,这是 ABP 提供的一个工具服务,可简化分页、排序和搜索。
打开 /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>
现在你可以在浏览器中看到最终结果:
抠丁客






